DevVGA.cpp revision 3956d0151065a11e49d2213b38a5efdad46807e0
016096e367cd20c3d3c3fd9a6650b55935c2e31dvboxsync/* $Id$ */
016096e367cd20c3d3c3fd9a6650b55935c2e31dvboxsync/** @file
016096e367cd20c3d3c3fd9a6650b55935c2e31dvboxsync * DevVGA - VBox VGA/VESA device.
016096e367cd20c3d3c3fd9a6650b55935c2e31dvboxsync */
2f0d866e126dd288169fed591c259c1c6b4016e5vboxsync
016096e367cd20c3d3c3fd9a6650b55935c2e31dvboxsync/*
b2bc8de1367ae24e1b27b53921d0b32ee3df7acdvboxsync * Copyright (C) 2006-2007 Oracle Corporation
016096e367cd20c3d3c3fd9a6650b55935c2e31dvboxsync *
016096e367cd20c3d3c3fd9a6650b55935c2e31dvboxsync * This file is part of VirtualBox Open Source Edition (OSE), as
016096e367cd20c3d3c3fd9a6650b55935c2e31dvboxsync * available from http://www.virtualbox.org. This file is free software;
016096e367cd20c3d3c3fd9a6650b55935c2e31dvboxsync * you can redistribute it and/or modify it under the terms of the GNU
016096e367cd20c3d3c3fd9a6650b55935c2e31dvboxsync * General Public License (GPL) as published by the Free Software
016096e367cd20c3d3c3fd9a6650b55935c2e31dvboxsync * Foundation, in version 2 as it comes in the "COPYING" file of the
016096e367cd20c3d3c3fd9a6650b55935c2e31dvboxsync * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
016096e367cd20c3d3c3fd9a6650b55935c2e31dvboxsync * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
016096e367cd20c3d3c3fd9a6650b55935c2e31dvboxsync * --------------------------------------------------------------------
2f0d866e126dd288169fed591c259c1c6b4016e5vboxsync *
016096e367cd20c3d3c3fd9a6650b55935c2e31dvboxsync * This code is based on:
016096e367cd20c3d3c3fd9a6650b55935c2e31dvboxsync *
016096e367cd20c3d3c3fd9a6650b55935c2e31dvboxsync * QEMU VGA Emulator.
016096e367cd20c3d3c3fd9a6650b55935c2e31dvboxsync *
43747b1f0bc8302a238fb35e55857a5e9aa1933dvboxsync * Copyright (c) 2003 Fabrice Bellard
43747b1f0bc8302a238fb35e55857a5e9aa1933dvboxsync *
b2bc8de1367ae24e1b27b53921d0b32ee3df7acdvboxsync * Permission is hereby granted, free of charge, to any person obtaining a copy
016096e367cd20c3d3c3fd9a6650b55935c2e31dvboxsync * of this software and associated documentation files (the "Software"), to deal
2f0d866e126dd288169fed591c259c1c6b4016e5vboxsync * in the Software without restriction, including without limitation the rights
016096e367cd20c3d3c3fd9a6650b55935c2e31dvboxsync * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
016096e367cd20c3d3c3fd9a6650b55935c2e31dvboxsync * copies of the Software, and to permit persons to whom the Software is
f5e53763b0a581b0299e98028c6c52192eb06785vboxsync * furnished to do so, subject to the following conditions:
016096e367cd20c3d3c3fd9a6650b55935c2e31dvboxsync *
016096e367cd20c3d3c3fd9a6650b55935c2e31dvboxsync * The above copyright notice and this permission notice shall be included in
2f0d866e126dd288169fed591c259c1c6b4016e5vboxsync * all copies or substantial portions of the Software.
2f0d866e126dd288169fed591c259c1c6b4016e5vboxsync *
2f0d866e126dd288169fed591c259c1c6b4016e5vboxsync * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
016096e367cd20c3d3c3fd9a6650b55935c2e31dvboxsync * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
016096e367cd20c3d3c3fd9a6650b55935c2e31dvboxsync * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
016096e367cd20c3d3c3fd9a6650b55935c2e31dvboxsync * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
016096e367cd20c3d3c3fd9a6650b55935c2e31dvboxsync * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
016096e367cd20c3d3c3fd9a6650b55935c2e31dvboxsync * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
9b7ab382b3f9667e8847020e1e58f7143c4d2334vboxsync * THE SOFTWARE.
016096e367cd20c3d3c3fd9a6650b55935c2e31dvboxsync */
8742e4a4ddb7b62d21d96d56dd1baf01c9f22cecvboxsync
8742e4a4ddb7b62d21d96d56dd1baf01c9f22cecvboxsync/*******************************************************************************
016096e367cd20c3d3c3fd9a6650b55935c2e31dvboxsync* Defined Constants And Macros *
016096e367cd20c3d3c3fd9a6650b55935c2e31dvboxsync*******************************************************************************/
8742e4a4ddb7b62d21d96d56dd1baf01c9f22cecvboxsync
8742e4a4ddb7b62d21d96d56dd1baf01c9f22cecvboxsync/* WARNING!!! All defines that affetc VGAState should be placed to DevVGA.h !!!
8742e4a4ddb7b62d21d96d56dd1baf01c9f22cecvboxsync * NEVER place them here as this would lead to VGAState inconsistency
fe554d9c0e3a6de4ba221610ac95a44c7d288e01vboxsync * across different .cpp files !!!
016096e367cd20c3d3c3fd9a6650b55935c2e31dvboxsync */
63787aca0a2a16ec959a5294148726ccf898ddf1vboxsync/** The size of the VGA GC mapping.
63787aca0a2a16ec959a5294148726ccf898ddf1vboxsync * This is supposed to be all the VGA memory accessible to the guest.
016096e367cd20c3d3c3fd9a6650b55935c2e31dvboxsync * The initial value was 256KB but NTAllInOne.iso appears to access more
63787aca0a2a16ec959a5294148726ccf898ddf1vboxsync * thus the limit was upped to 512KB.
016096e367cd20c3d3c3fd9a6650b55935c2e31dvboxsync *
63787aca0a2a16ec959a5294148726ccf898ddf1vboxsync * @todo Someone with some VGA knowhow should make a better guess at this value.
63787aca0a2a16ec959a5294148726ccf898ddf1vboxsync */
fe554d9c0e3a6de4ba221610ac95a44c7d288e01vboxsync#define VGA_MAPPING_SIZE _512K
b2bc8de1367ae24e1b27b53921d0b32ee3df7acdvboxsync
fe554d9c0e3a6de4ba221610ac95a44c7d288e01vboxsync#ifdef VBOX_WITH_HGSMI
016096e367cd20c3d3c3fd9a6650b55935c2e31dvboxsync#define PCIDEV_2_VGASTATE(pPciDev) ((VGAState *)((uintptr_t)pPciDev - RT_OFFSETOF(VGAState, Dev)))
016096e367cd20c3d3c3fd9a6650b55935c2e31dvboxsync#endif /* VBOX_WITH_HGSMI */
016096e367cd20c3d3c3fd9a6650b55935c2e31dvboxsync/** Converts a vga adaptor state pointer to a device instance pointer. */
016096e367cd20c3d3c3fd9a6650b55935c2e31dvboxsync#define VGASTATE2DEVINS(pVgaState) ((pVgaState)->CTX_SUFF(pDevIns))
016096e367cd20c3d3c3fd9a6650b55935c2e31dvboxsync
016096e367cd20c3d3c3fd9a6650b55935c2e31dvboxsync/** Check that the video modes fit into virtual video memory.
016096e367cd20c3d3c3fd9a6650b55935c2e31dvboxsync * Only works when VBE_NEW_DYN_LIST is defined! */
016096e367cd20c3d3c3fd9a6650b55935c2e31dvboxsync#define VRAM_SIZE_FIX
016096e367cd20c3d3c3fd9a6650b55935c2e31dvboxsync
016096e367cd20c3d3c3fd9a6650b55935c2e31dvboxsync/** Check buffer if an VRAM offset is within the right range or not. */
016096e367cd20c3d3c3fd9a6650b55935c2e31dvboxsync#if defined(IN_RC) || defined(VBOX_WITH_2X_4GB_ADDR_SPACE_IN_R0)
016096e367cd20c3d3c3fd9a6650b55935c2e31dvboxsync# define VERIFY_VRAM_WRITE_OFF_RETURN(pThis, off) \
016096e367cd20c3d3c3fd9a6650b55935c2e31dvboxsync do { \
016096e367cd20c3d3c3fd9a6650b55935c2e31dvboxsync if ((off) >= VGA_MAPPING_SIZE) \
016096e367cd20c3d3c3fd9a6650b55935c2e31dvboxsync { \
016096e367cd20c3d3c3fd9a6650b55935c2e31dvboxsync AssertMsgReturn((off) < (pThis)->vram_size, ("%RX32 !< %RX32\n", (uint32_t)(off), (pThis)->vram_size), VINF_SUCCESS); \
016096e367cd20c3d3c3fd9a6650b55935c2e31dvboxsync Log2(("%Rfn[%d]: %RX32 -> R3\n", __PRETTY_FUNCTION__, __LINE__, (off))); \
016096e367cd20c3d3c3fd9a6650b55935c2e31dvboxsync return VINF_IOM_HC_MMIO_WRITE; \
016096e367cd20c3d3c3fd9a6650b55935c2e31dvboxsync } \
016096e367cd20c3d3c3fd9a6650b55935c2e31dvboxsync } while (0)
016096e367cd20c3d3c3fd9a6650b55935c2e31dvboxsync#else
016096e367cd20c3d3c3fd9a6650b55935c2e31dvboxsync# define VERIFY_VRAM_WRITE_OFF_RETURN(pThis, off) \
016096e367cd20c3d3c3fd9a6650b55935c2e31dvboxsync AssertMsgReturn((off) < (pThis)->vram_size, ("%RX32 !< %RX32\n", (uint32_t)(off), (pThis)->vram_size), VINF_SUCCESS)
016096e367cd20c3d3c3fd9a6650b55935c2e31dvboxsync#endif
016096e367cd20c3d3c3fd9a6650b55935c2e31dvboxsync
016096e367cd20c3d3c3fd9a6650b55935c2e31dvboxsync/** Check buffer if an VRAM offset is within the right range or not. */
016096e367cd20c3d3c3fd9a6650b55935c2e31dvboxsync#if defined(IN_RC) || defined(VBOX_WITH_2X_4GB_ADDR_SPACE_IN_R0)
016096e367cd20c3d3c3fd9a6650b55935c2e31dvboxsync# define VERIFY_VRAM_READ_OFF_RETURN(pThis, off, rcVar) \
016096e367cd20c3d3c3fd9a6650b55935c2e31dvboxsync do { \
016096e367cd20c3d3c3fd9a6650b55935c2e31dvboxsync if ((off) >= VGA_MAPPING_SIZE) \
016096e367cd20c3d3c3fd9a6650b55935c2e31dvboxsync { \
016096e367cd20c3d3c3fd9a6650b55935c2e31dvboxsync AssertMsgReturn((off) < (pThis)->vram_size, ("%RX32 !< %RX32\n", (uint32_t)(off), (pThis)->vram_size), 0xff); \
016096e367cd20c3d3c3fd9a6650b55935c2e31dvboxsync Log2(("%Rfn[%d]: %RX32 -> R3\n", __PRETTY_FUNCTION__, __LINE__, (off))); \
7c48fdac0546978ed14617c8096734ce2d18c8e5vboxsync (rcVar) = VINF_IOM_HC_MMIO_READ; \
7c48fdac0546978ed14617c8096734ce2d18c8e5vboxsync return 0; \
7c48fdac0546978ed14617c8096734ce2d18c8e5vboxsync } \
7c48fdac0546978ed14617c8096734ce2d18c8e5vboxsync } while (0)
7c48fdac0546978ed14617c8096734ce2d18c8e5vboxsync#else
7c48fdac0546978ed14617c8096734ce2d18c8e5vboxsync# define VERIFY_VRAM_READ_OFF_RETURN(pThis, off, rcVar) \
7c48fdac0546978ed14617c8096734ce2d18c8e5vboxsync AssertMsgReturn((off) < (pThis)->vram_size, ("%RX32 !< %RX32\n", (uint32_t)(off), (pThis)->vram_size), 0xff)
7c48fdac0546978ed14617c8096734ce2d18c8e5vboxsync#endif
7c48fdac0546978ed14617c8096734ce2d18c8e5vboxsync
9b7ab382b3f9667e8847020e1e58f7143c4d2334vboxsync
9b7ab382b3f9667e8847020e1e58f7143c4d2334vboxsync/*******************************************************************************
016096e367cd20c3d3c3fd9a6650b55935c2e31dvboxsync* Header Files *
016096e367cd20c3d3c3fd9a6650b55935c2e31dvboxsync*******************************************************************************/
63e3a547845f7c31bb4e892a66684b560dc63611vboxsync#define LOG_GROUP LOG_GROUP_DEV_VGA
63e3a547845f7c31bb4e892a66684b560dc63611vboxsync#include <VBox/vmm/pdmdev.h>
63787aca0a2a16ec959a5294148726ccf898ddf1vboxsync#include <VBox/vmm/pgm.h>
63e3a547845f7c31bb4e892a66684b560dc63611vboxsync#ifdef IN_RING3
016096e367cd20c3d3c3fd9a6650b55935c2e31dvboxsync#include <iprt/alloc.h>
016096e367cd20c3d3c3fd9a6650b55935c2e31dvboxsync#include <iprt/ctype.h>
b2bc8de1367ae24e1b27b53921d0b32ee3df7acdvboxsync#endif /* IN_RING3 */
016096e367cd20c3d3c3fd9a6650b55935c2e31dvboxsync#include <iprt/assert.h>
016096e367cd20c3d3c3fd9a6650b55935c2e31dvboxsync#include <iprt/asm.h>
2f0d866e126dd288169fed591c259c1c6b4016e5vboxsync#include <iprt/file.h>
c060166f65b9dd2f1ed53e6e4cffdad948e01722vboxsync#include <iprt/time.h>
c060166f65b9dd2f1ed53e6e4cffdad948e01722vboxsync#include <iprt/string.h>
c060166f65b9dd2f1ed53e6e4cffdad948e01722vboxsync#include <iprt/uuid.h>
c060166f65b9dd2f1ed53e6e4cffdad948e01722vboxsync
c060166f65b9dd2f1ed53e6e4cffdad948e01722vboxsync#include <VBox/VMMDev.h>
c060166f65b9dd2f1ed53e6e4cffdad948e01722vboxsync#include <VBox/VBoxVideo.h>
c060166f65b9dd2f1ed53e6e4cffdad948e01722vboxsync#include <VBox/bioslogo.h>
c060166f65b9dd2f1ed53e6e4cffdad948e01722vboxsync
c060166f65b9dd2f1ed53e6e4cffdad948e01722vboxsync/* should go BEFORE any other DevVGA include to make all DevVGA.h config defines be visible */
c060166f65b9dd2f1ed53e6e4cffdad948e01722vboxsync#include "DevVGA.h"
c060166f65b9dd2f1ed53e6e4cffdad948e01722vboxsync
c060166f65b9dd2f1ed53e6e4cffdad948e01722vboxsync#if defined(VBE_NEW_DYN_LIST) && defined(IN_RING3) && !defined(VBOX_DEVICE_STRUCT_TESTCASE)
c060166f65b9dd2f1ed53e6e4cffdad948e01722vboxsync# include "DevVGAModes.h"
c060166f65b9dd2f1ed53e6e4cffdad948e01722vboxsync# include <stdio.h> /* sscan */
c060166f65b9dd2f1ed53e6e4cffdad948e01722vboxsync#endif
c060166f65b9dd2f1ed53e6e4cffdad948e01722vboxsync
c060166f65b9dd2f1ed53e6e4cffdad948e01722vboxsync#include "vl_vbox.h"
e562a8bb17c0dfa1c316708e085a3a92fcc80521vboxsync#include "VBoxDD.h"
e562a8bb17c0dfa1c316708e085a3a92fcc80521vboxsync#include "VBoxDD2.h"
e562a8bb17c0dfa1c316708e085a3a92fcc80521vboxsync
e562a8bb17c0dfa1c316708e085a3a92fcc80521vboxsync
e562a8bb17c0dfa1c316708e085a3a92fcc80521vboxsync/*******************************************************************************
e562a8bb17c0dfa1c316708e085a3a92fcc80521vboxsync* Structures and Typedefs *
e562a8bb17c0dfa1c316708e085a3a92fcc80521vboxsync*******************************************************************************/
e562a8bb17c0dfa1c316708e085a3a92fcc80521vboxsync#pragma pack(1)
e562a8bb17c0dfa1c316708e085a3a92fcc80521vboxsync
e562a8bb17c0dfa1c316708e085a3a92fcc80521vboxsync/** BMP File Format Bitmap Header. */
e562a8bb17c0dfa1c316708e085a3a92fcc80521vboxsynctypedef struct
e562a8bb17c0dfa1c316708e085a3a92fcc80521vboxsync{
e562a8bb17c0dfa1c316708e085a3a92fcc80521vboxsync uint16_t Type; /* File Type Identifier */
e562a8bb17c0dfa1c316708e085a3a92fcc80521vboxsync uint32_t FileSize; /* Size of File */
e562a8bb17c0dfa1c316708e085a3a92fcc80521vboxsync uint16_t Reserved1; /* Reserved (should be 0) */
e562a8bb17c0dfa1c316708e085a3a92fcc80521vboxsync uint16_t Reserved2; /* Reserved (should be 0) */
e562a8bb17c0dfa1c316708e085a3a92fcc80521vboxsync uint32_t Offset; /* Offset to bitmap data */
e562a8bb17c0dfa1c316708e085a3a92fcc80521vboxsync} BMPINFO;
e562a8bb17c0dfa1c316708e085a3a92fcc80521vboxsync
e562a8bb17c0dfa1c316708e085a3a92fcc80521vboxsync/** Pointer to a bitmap header*/
e562a8bb17c0dfa1c316708e085a3a92fcc80521vboxsynctypedef BMPINFO *PBMPINFO;
e562a8bb17c0dfa1c316708e085a3a92fcc80521vboxsync
e562a8bb17c0dfa1c316708e085a3a92fcc80521vboxsync/** OS/2 1.x Information Header Format. */
e562a8bb17c0dfa1c316708e085a3a92fcc80521vboxsynctypedef struct
e562a8bb17c0dfa1c316708e085a3a92fcc80521vboxsync{
e562a8bb17c0dfa1c316708e085a3a92fcc80521vboxsync uint32_t Size; /* Size of Remaining Header */
e562a8bb17c0dfa1c316708e085a3a92fcc80521vboxsync uint16_t Width; /* Width of Bitmap in Pixels */
2f0d866e126dd288169fed591c259c1c6b4016e5vboxsync uint16_t Height; /* Height of Bitmap in Pixels */
2f0d866e126dd288169fed591c259c1c6b4016e5vboxsync uint16_t Planes; /* Number of Planes */
2f0d866e126dd288169fed591c259c1c6b4016e5vboxsync uint16_t BitCount; /* Color Bits Per Pixel */
016096e367cd20c3d3c3fd9a6650b55935c2e31dvboxsync} OS2HDR;
016096e367cd20c3d3c3fd9a6650b55935c2e31dvboxsync
016096e367cd20c3d3c3fd9a6650b55935c2e31dvboxsync/** Pointer to a OS/2 1.x header format */
016096e367cd20c3d3c3fd9a6650b55935c2e31dvboxsynctypedef OS2HDR *POS2HDR;
016096e367cd20c3d3c3fd9a6650b55935c2e31dvboxsync
016096e367cd20c3d3c3fd9a6650b55935c2e31dvboxsync/** OS/2 2.0 Information Header Format. */
016096e367cd20c3d3c3fd9a6650b55935c2e31dvboxsynctypedef struct
016096e367cd20c3d3c3fd9a6650b55935c2e31dvboxsync{
016096e367cd20c3d3c3fd9a6650b55935c2e31dvboxsync uint32_t Size; /* Size of Remaining Header */
016096e367cd20c3d3c3fd9a6650b55935c2e31dvboxsync uint32_t Width; /* Width of Bitmap in Pixels */
016096e367cd20c3d3c3fd9a6650b55935c2e31dvboxsync uint32_t Height; /* Height of Bitmap in Pixels */
016096e367cd20c3d3c3fd9a6650b55935c2e31dvboxsync uint16_t Planes; /* Number of Planes */
016096e367cd20c3d3c3fd9a6650b55935c2e31dvboxsync uint16_t BitCount; /* Color Bits Per Pixel */
016096e367cd20c3d3c3fd9a6650b55935c2e31dvboxsync uint32_t Compression; /* Compression Scheme (0=none) */
016096e367cd20c3d3c3fd9a6650b55935c2e31dvboxsync uint32_t SizeImage; /* Size of bitmap in bytes */
016096e367cd20c3d3c3fd9a6650b55935c2e31dvboxsync uint32_t XPelsPerMeter; /* Horz. Resolution in Pixels/Meter */
016096e367cd20c3d3c3fd9a6650b55935c2e31dvboxsync uint32_t YPelsPerMeter; /* Vert. Resolution in Pixels/Meter */
016096e367cd20c3d3c3fd9a6650b55935c2e31dvboxsync uint32_t ClrUsed; /* Number of Colors in Color Table */
b2bc8de1367ae24e1b27b53921d0b32ee3df7acdvboxsync uint32_t ClrImportant; /* Number of Important Colors */
b2bc8de1367ae24e1b27b53921d0b32ee3df7acdvboxsync uint16_t Units; /* Resolution Measurement Used */
b2bc8de1367ae24e1b27b53921d0b32ee3df7acdvboxsync uint16_t Reserved; /* Reserved FIelds (always 0) */
016096e367cd20c3d3c3fd9a6650b55935c2e31dvboxsync uint16_t Recording; /* Orientation of Bitmap */
b2bc8de1367ae24e1b27b53921d0b32ee3df7acdvboxsync uint16_t Rendering; /* Halftone Algorithm Used on Image */
016096e367cd20c3d3c3fd9a6650b55935c2e31dvboxsync uint32_t Size1; /* Halftone Algorithm Data */
b2bc8de1367ae24e1b27b53921d0b32ee3df7acdvboxsync uint32_t Size2; /* Halftone Algorithm Data */
b2bc8de1367ae24e1b27b53921d0b32ee3df7acdvboxsync uint32_t ColorEncoding; /* Color Table Format (always 0) */
b2bc8de1367ae24e1b27b53921d0b32ee3df7acdvboxsync uint32_t Identifier; /* Misc. Field for Application Use */
b2bc8de1367ae24e1b27b53921d0b32ee3df7acdvboxsync} OS22HDR;
016096e367cd20c3d3c3fd9a6650b55935c2e31dvboxsync
b2bc8de1367ae24e1b27b53921d0b32ee3df7acdvboxsync/** Pointer to a OS/2 2.0 header format */
016096e367cd20c3d3c3fd9a6650b55935c2e31dvboxsynctypedef OS22HDR *POS22HDR;
b2bc8de1367ae24e1b27b53921d0b32ee3df7acdvboxsync
016096e367cd20c3d3c3fd9a6650b55935c2e31dvboxsync/** Windows 3.x Information Header Format. */
b2bc8de1367ae24e1b27b53921d0b32ee3df7acdvboxsynctypedef struct
016096e367cd20c3d3c3fd9a6650b55935c2e31dvboxsync{
b2bc8de1367ae24e1b27b53921d0b32ee3df7acdvboxsync uint32_t Size; /* Size of Remaining Header */
b2bc8de1367ae24e1b27b53921d0b32ee3df7acdvboxsync uint32_t Width; /* Width of Bitmap in Pixels */
b2bc8de1367ae24e1b27b53921d0b32ee3df7acdvboxsync uint32_t Height; /* Height of Bitmap in Pixels */
016096e367cd20c3d3c3fd9a6650b55935c2e31dvboxsync uint16_t Planes; /* Number of Planes */
b2bc8de1367ae24e1b27b53921d0b32ee3df7acdvboxsync uint16_t BitCount; /* Bits Per Pixel */
016096e367cd20c3d3c3fd9a6650b55935c2e31dvboxsync uint32_t Compression; /* Compression Scheme (0=none) */
b2bc8de1367ae24e1b27b53921d0b32ee3df7acdvboxsync uint32_t SizeImage; /* Size of bitmap in bytes */
016096e367cd20c3d3c3fd9a6650b55935c2e31dvboxsync uint32_t XPelsPerMeter; /* Horz. Resolution in Pixels/Meter */
016096e367cd20c3d3c3fd9a6650b55935c2e31dvboxsync uint32_t YPelsPerMeter; /* Vert. Resolution in Pixels/Meter */
016096e367cd20c3d3c3fd9a6650b55935c2e31dvboxsync uint32_t ClrUsed; /* Number of Colors in Color Table */
016096e367cd20c3d3c3fd9a6650b55935c2e31dvboxsync uint32_t ClrImportant; /* Number of Important Colors */
016096e367cd20c3d3c3fd9a6650b55935c2e31dvboxsync} WINHDR;
016096e367cd20c3d3c3fd9a6650b55935c2e31dvboxsync
016096e367cd20c3d3c3fd9a6650b55935c2e31dvboxsync/** Pointer to a Windows 3.x header format */
016096e367cd20c3d3c3fd9a6650b55935c2e31dvboxsynctypedef WINHDR *PWINHDR;
016096e367cd20c3d3c3fd9a6650b55935c2e31dvboxsync
016096e367cd20c3d3c3fd9a6650b55935c2e31dvboxsync#pragma pack()
016096e367cd20c3d3c3fd9a6650b55935c2e31dvboxsync
016096e367cd20c3d3c3fd9a6650b55935c2e31dvboxsync#define BMP_ID 0x4D42
016096e367cd20c3d3c3fd9a6650b55935c2e31dvboxsync
016096e367cd20c3d3c3fd9a6650b55935c2e31dvboxsync/** @name BMP compressions.
016096e367cd20c3d3c3fd9a6650b55935c2e31dvboxsync * @{ */
016096e367cd20c3d3c3fd9a6650b55935c2e31dvboxsync#define BMP_COMPRESS_NONE 0
016096e367cd20c3d3c3fd9a6650b55935c2e31dvboxsync#define BMP_COMPRESS_RLE8 1
016096e367cd20c3d3c3fd9a6650b55935c2e31dvboxsync#define BMP_COMPRESS_RLE4 2
8287c906b9b1d215824d4cdf6c1eaf40681ebfa8vboxsync/** @} */
8742e4a4ddb7b62d21d96d56dd1baf01c9f22cecvboxsync
016096e367cd20c3d3c3fd9a6650b55935c2e31dvboxsync/** @name BMP header sizes.
8287c906b9b1d215824d4cdf6c1eaf40681ebfa8vboxsync * @{ */
016096e367cd20c3d3c3fd9a6650b55935c2e31dvboxsync#define BMP_HEADER_OS21 12
016096e367cd20c3d3c3fd9a6650b55935c2e31dvboxsync#define BMP_HEADER_OS22 64
8287c906b9b1d215824d4cdf6c1eaf40681ebfa8vboxsync#define BMP_HEADER_WIN3 40
8287c906b9b1d215824d4cdf6c1eaf40681ebfa8vboxsync/** @} */
8287c906b9b1d215824d4cdf6c1eaf40681ebfa8vboxsync
63787aca0a2a16ec959a5294148726ccf898ddf1vboxsync/** The BIOS boot menu text position, X. */
63787aca0a2a16ec959a5294148726ccf898ddf1vboxsync#define LOGO_F12TEXT_X 304
63787aca0a2a16ec959a5294148726ccf898ddf1vboxsync/** The BIOS boot menu text position, Y. */
8287c906b9b1d215824d4cdf6c1eaf40681ebfa8vboxsync#define LOGO_F12TEXT_Y 464
762a68c2bb3ccde807330e3d1cb05f8b244a5f72vboxsync
8287c906b9b1d215824d4cdf6c1eaf40681ebfa8vboxsync/** Width of the "Press F12 to select boot device." bitmap.
016096e367cd20c3d3c3fd9a6650b55935c2e31dvboxsync Anything that exceeds the limit of F12BootText below is filled with
8287c906b9b1d215824d4cdf6c1eaf40681ebfa8vboxsync background. */
016096e367cd20c3d3c3fd9a6650b55935c2e31dvboxsync#define LOGO_F12TEXT_WIDTH 286
8287c906b9b1d215824d4cdf6c1eaf40681ebfa8vboxsync/** Height of the boot device selection bitmap, see LOGO_F12TEXT_WIDTH. */
9540eeb13face31ddc1c5f15338556fe46f18a77vboxsync#define LOGO_F12TEXT_HEIGHT 12
8287c906b9b1d215824d4cdf6c1eaf40681ebfa8vboxsync
9540eeb13face31ddc1c5f15338556fe46f18a77vboxsync/** The BIOS logo delay time (msec). */
fe554d9c0e3a6de4ba221610ac95a44c7d288e01vboxsync#define LOGO_DELAY_TIME 2000
f2a212e40c7307d2b8ef9b2658a539f5ac41e683vboxsync
3ebd5757516d21eccdad25ddd456d2913c2fb215vboxsync#define LOGO_MAX_WIDTH 640
fe554d9c0e3a6de4ba221610ac95a44c7d288e01vboxsync#define LOGO_MAX_HEIGHT 480
016096e367cd20c3d3c3fd9a6650b55935c2e31dvboxsync#define LOGO_MAX_SIZE LOGO_MAX_WIDTH * LOGO_MAX_HEIGHT * 4
016096e367cd20c3d3c3fd9a6650b55935c2e31dvboxsync
016096e367cd20c3d3c3fd9a6650b55935c2e31dvboxsync
016096e367cd20c3d3c3fd9a6650b55935c2e31dvboxsync/*******************************************************************************
016096e367cd20c3d3c3fd9a6650b55935c2e31dvboxsync* Global Variables *
016096e367cd20c3d3c3fd9a6650b55935c2e31dvboxsync*******************************************************************************/
b2bc8de1367ae24e1b27b53921d0b32ee3df7acdvboxsync/* "Press F12 to select boot device." bitmap. */
7c48fdac0546978ed14617c8096734ce2d18c8e5vboxsyncstatic const uint8_t g_abLogoF12BootText[] =
b2bc8de1367ae24e1b27b53921d0b32ee3df7acdvboxsync{
7c48fdac0546978ed14617c8096734ce2d18c8e5vboxsync 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
7c48fdac0546978ed14617c8096734ce2d18c8e5vboxsync 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
7c48fdac0546978ed14617c8096734ce2d18c8e5vboxsync 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x07, 0x0F, 0x7C,
7c48fdac0546978ed14617c8096734ce2d18c8e5vboxsync 0xF8, 0xF0, 0x01, 0xE0, 0x81, 0x9F, 0x3F, 0x00, 0x70, 0xF8, 0x00, 0xE0, 0xC3,
b2bc8de1367ae24e1b27b53921d0b32ee3df7acdvboxsync 0x07, 0x0F, 0x1F, 0x3E, 0x70, 0x00, 0xF0, 0xE1, 0xC3, 0x07, 0x0E, 0x00, 0x6E,
7c48fdac0546978ed14617c8096734ce2d18c8e5vboxsync 0x7C, 0x60, 0xE0, 0xE1, 0xC3, 0x07, 0xC6, 0x80, 0x81, 0x31, 0x63, 0xC6, 0x00,
b2bc8de1367ae24e1b27b53921d0b32ee3df7acdvboxsync 0x30, 0x80, 0x61, 0x0C, 0x00, 0x36, 0x63, 0x00, 0x8C, 0x19, 0x83, 0x61, 0xCC,
7c48fdac0546978ed14617c8096734ce2d18c8e5vboxsync 0x18, 0x36, 0x00, 0xCC, 0x8C, 0x19, 0xC3, 0x06, 0xC0, 0x8C, 0x31, 0x3C, 0x30,
7c48fdac0546978ed14617c8096734ce2d18c8e5vboxsync 0x8C, 0x19, 0x83, 0x31, 0x60, 0x60, 0x00, 0x0C, 0x18, 0x00, 0x0C, 0x60, 0x18,
7c48fdac0546978ed14617c8096734ce2d18c8e5vboxsync 0x00, 0x80, 0xC1, 0x18, 0x00, 0x30, 0x06, 0x60, 0x18, 0x30, 0x80, 0x01, 0x00,
016096e367cd20c3d3c3fd9a6650b55935c2e31dvboxsync 0x33, 0x63, 0xC6, 0x30, 0x00, 0x30, 0x63, 0x80, 0x19, 0x0C, 0x03, 0x06, 0x00,
016096e367cd20c3d3c3fd9a6650b55935c2e31dvboxsync 0x0C, 0x18, 0x18, 0xC0, 0x81, 0x03, 0x00, 0x03, 0x18, 0x0C, 0x00, 0x60, 0x30,
016096e367cd20c3d3c3fd9a6650b55935c2e31dvboxsync 0x06, 0x00, 0x87, 0x01, 0x18, 0x06, 0x0C, 0x60, 0x00, 0xC0, 0xCC, 0x98, 0x31,
016096e367cd20c3d3c3fd9a6650b55935c2e31dvboxsync 0x0C, 0x00, 0xCC, 0x18, 0x30, 0x0C, 0xC3, 0x80, 0x01, 0x00, 0x03, 0x66, 0xFE,
7c48fdac0546978ed14617c8096734ce2d18c8e5vboxsync 0x18, 0x30, 0x00, 0xC0, 0x02, 0x06, 0x06, 0x00, 0x18, 0x8C, 0x01, 0x60, 0xE0,
016096e367cd20c3d3c3fd9a6650b55935c2e31dvboxsync 0x0F, 0x86, 0x3F, 0x03, 0x18, 0x00, 0x30, 0x33, 0x66, 0x0C, 0x03, 0x00, 0x33,
016096e367cd20c3d3c3fd9a6650b55935c2e31dvboxsync 0xFE, 0x0C, 0xC3, 0x30, 0xE0, 0x0F, 0xC0, 0x87, 0x9B, 0x31, 0x63, 0xC6, 0x00,
016096e367cd20c3d3c3fd9a6650b55935c2e31dvboxsync 0xF0, 0x80, 0x01, 0x03, 0x00, 0x06, 0x63, 0x00, 0x8C, 0x19, 0x83, 0x61, 0xCC,
016096e367cd20c3d3c3fd9a6650b55935c2e31dvboxsync 0x18, 0x06, 0x00, 0x6C, 0x8C, 0x19, 0xC3, 0x00, 0x80, 0x8D, 0x31, 0xC3, 0x30,
3ebd5757516d21eccdad25ddd456d2913c2fb215vboxsync 0x8C, 0x19, 0x03, 0x30, 0xB3, 0xC3, 0x87, 0x0F, 0x1F, 0x00, 0x2C, 0x60, 0x80,
016096e367cd20c3d3c3fd9a6650b55935c2e31dvboxsync 0x01, 0xE0, 0x87, 0x0F, 0x00, 0x3E, 0x7C, 0x60, 0xF0, 0xE1, 0xE3, 0x07, 0x00,
63787aca0a2a16ec959a5294148726ccf898ddf1vboxsync 0x0F, 0x3E, 0x7C, 0xFC, 0x00, 0xC0, 0xC3, 0xC7, 0x30, 0x0E, 0x3E, 0x7C, 0x00,
016096e367cd20c3d3c3fd9a6650b55935c2e31dvboxsync 0xCC, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x23, 0x1E, 0xC0, 0x00, 0x60, 0x00,
016096e367cd20c3d3c3fd9a6650b55935c2e31dvboxsync 0x00, 0x00, 0x00, 0x00, 0x18, 0x00, 0x00, 0x60, 0x00, 0xC0, 0x00, 0x00, 0x00,
3ebd5757516d21eccdad25ddd456d2913c2fb215vboxsync 0x0C, 0x00, 0xC0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x33, 0x00, 0x00,
016096e367cd20c3d3c3fd9a6650b55935c2e31dvboxsync 0x00, 0x00, 0x00, 0xC0, 0x0C, 0x87, 0x31, 0x00, 0x18, 0x00, 0x00, 0x00, 0x00,
63787aca0a2a16ec959a5294148726ccf898ddf1vboxsync 0x00, 0x06, 0x00, 0x00, 0x18, 0x00, 0x30, 0x00, 0x00, 0x00, 0x03, 0x00, 0x30,
016096e367cd20c3d3c3fd9a6650b55935c2e31dvboxsync 0x00, 0x00, 0xC0, 0x00, 0x00, 0x00, 0xE0, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00,
016096e367cd20c3d3c3fd9a6650b55935c2e31dvboxsync 0xF8, 0x83, 0xC1, 0x07, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0xC0, 0x01, 0x00,
3ebd5757516d21eccdad25ddd456d2913c2fb215vboxsync 0x00, 0x04, 0x00, 0x0E, 0x00, 0x00, 0x80, 0x00, 0x00, 0x0E, 0x00, 0x00, 0x30,
016096e367cd20c3d3c3fd9a6650b55935c2e31dvboxsync 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
016096e367cd20c3d3c3fd9a6650b55935c2e31dvboxsync 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
016096e367cd20c3d3c3fd9a6650b55935c2e31dvboxsync 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
016096e367cd20c3d3c3fd9a6650b55935c2e31dvboxsync};
016096e367cd20c3d3c3fd9a6650b55935c2e31dvboxsync
b2bc8de1367ae24e1b27b53921d0b32ee3df7acdvboxsync
b2bc8de1367ae24e1b27b53921d0b32ee3df7acdvboxsync#ifndef VBOX_DEVICE_STRUCT_TESTCASE
b2bc8de1367ae24e1b27b53921d0b32ee3df7acdvboxsync/*******************************************************************************
016096e367cd20c3d3c3fd9a6650b55935c2e31dvboxsync* Internal Functions *
016096e367cd20c3d3c3fd9a6650b55935c2e31dvboxsync*******************************************************************************/
7c48fdac0546978ed14617c8096734ce2d18c8e5vboxsyncRT_C_DECLS_BEGIN
7c48fdac0546978ed14617c8096734ce2d18c8e5vboxsync
7c48fdac0546978ed14617c8096734ce2d18c8e5vboxsyncPDMBOTHCBDECL(int) vgaIOPortWrite(PPDMDEVINS pDevIns, void *pvUser, RTIOPORT Port, uint32_t u32, unsigned cb);
016096e367cd20c3d3c3fd9a6650b55935c2e31dvboxsyncPDMBOTHCBDECL(int) vgaIOPortRead(PPDMDEVINS pDevIns, void *pvUser, RTIOPORT Port, uint32_t *pu32, unsigned cb);
016096e367cd20c3d3c3fd9a6650b55935c2e31dvboxsyncPDMBOTHCBDECL(int) vgaIOPortWriteVBEIndex(PPDMDEVINS pDevIns, void *pvUser, RTIOPORT Port, uint32_t u32, unsigned cb);
762a68c2bb3ccde807330e3d1cb05f8b244a5f72vboxsyncPDMBOTHCBDECL(int) vgaIOPortWriteVBEData(PPDMDEVINS pDevIns, void *pvUser, RTIOPORT Port, uint32_t u32, unsigned cb);
016096e367cd20c3d3c3fd9a6650b55935c2e31dvboxsyncPDMBOTHCBDECL(int) vgaIOPortReadVBEIndex(PPDMDEVINS pDevIns, void *pvUser, RTIOPORT Port, uint32_t *pu32, unsigned cb);
016096e367cd20c3d3c3fd9a6650b55935c2e31dvboxsyncPDMBOTHCBDECL(int) vgaIOPortReadVBEData(PPDMDEVINS pDevIns, void *pvUser, RTIOPORT Port, uint32_t *pu32, unsigned cb);
016096e367cd20c3d3c3fd9a6650b55935c2e31dvboxsyncPDMBOTHCBDECL(int) vgaMMIOFill(PPDMDEVINS pDevIns, void *pvUser, RTGCPHYS GCPhysAddr, uint32_t u32Item, unsigned cbItem, unsigned cItems);
7c48fdac0546978ed14617c8096734ce2d18c8e5vboxsyncPDMBOTHCBDECL(int) vgaMMIORead(PPDMDEVINS pDevIns, void *pvUser, RTGCPHYS GCPhysAddr, void *pv, unsigned cb);
7c48fdac0546978ed14617c8096734ce2d18c8e5vboxsyncPDMBOTHCBDECL(int) vgaMMIOWrite(PPDMDEVINS pDevIns, void *pvUser, RTGCPHYS GCPhysAddr, void *pv, unsigned cb);
7c48fdac0546978ed14617c8096734ce2d18c8e5vboxsyncPDMBOTHCBDECL(int) vgaIOPortReadBIOS(PPDMDEVINS pDevIns, void *pvUser, RTIOPORT Port, uint32_t *pu32, unsigned cb);
016096e367cd20c3d3c3fd9a6650b55935c2e31dvboxsyncPDMBOTHCBDECL(int) vgaIOPortWriteBIOS(PPDMDEVINS pDevIns, void *pvUser, RTIOPORT Port, uint32_t u32, unsigned cb);
3ebd5757516d21eccdad25ddd456d2913c2fb215vboxsync#ifdef IN_RC
3ebd5757516d21eccdad25ddd456d2913c2fb215vboxsyncPDMBOTHCBDECL(int) vgaGCLFBAccessHandler(PVM pVM, RTGCUINT uErrorCode, PCPUMCTXCORE pRegFrame, RTGCPTR pvFault, RTGCPHYS GCPhysFault, void *pvUser);
016096e367cd20c3d3c3fd9a6650b55935c2e31dvboxsync#endif
016096e367cd20c3d3c3fd9a6650b55935c2e31dvboxsync#ifdef IN_RING0
7c48fdac0546978ed14617c8096734ce2d18c8e5vboxsyncPDMBOTHCBDECL(int) vgaR0LFBAccessHandler(PVM pVM, RTGCUINT uErrorCode, PCPUMCTXCORE pRegFrame, RTGCPTR pvFault, RTGCPHYS GCPhysFault, void *pvUser);
7c48fdac0546978ed14617c8096734ce2d18c8e5vboxsync#endif
7c48fdac0546978ed14617c8096734ce2d18c8e5vboxsync#ifdef IN_RING3
016096e367cd20c3d3c3fd9a6650b55935c2e31dvboxsync# ifdef VBE_NEW_DYN_LIST
3ebd5757516d21eccdad25ddd456d2913c2fb215vboxsyncPDMBOTHCBDECL(int) vbeIOPortReadVBEExtra(PPDMDEVINS pDevIns, void *pvUser, RTIOPORT Port, uint32_t *pu32, unsigned cb);
3ebd5757516d21eccdad25ddd456d2913c2fb215vboxsyncPDMBOTHCBDECL(int) vbeIOPortWriteVBEExtra(PPDMDEVINS pDevIns, void *pvUser, RTIOPORT Port, uint32_t u32, unsigned cb);
016096e367cd20c3d3c3fd9a6650b55935c2e31dvboxsync# endif
016096e367cd20c3d3c3fd9a6650b55935c2e31dvboxsyncPDMBOTHCBDECL(int) vbeIOPortReadCMDLogo(PPDMDEVINS pDevIns, void *pvUser, RTIOPORT Port, uint32_t *pu32, unsigned cb);
b2bc8de1367ae24e1b27b53921d0b32ee3df7acdvboxsyncPDMBOTHCBDECL(int) vbeIOPortWriteCMDLogo(PPDMDEVINS pDevIns, void *pvUser, RTIOPORT Port, uint32_t u32, unsigned cb);
016096e367cd20c3d3c3fd9a6650b55935c2e31dvboxsync#endif /* IN_RING3 */
016096e367cd20c3d3c3fd9a6650b55935c2e31dvboxsync
016096e367cd20c3d3c3fd9a6650b55935c2e31dvboxsync
b2bc8de1367ae24e1b27b53921d0b32ee3df7acdvboxsyncRT_C_DECLS_END
016096e367cd20c3d3c3fd9a6650b55935c2e31dvboxsync
016096e367cd20c3d3c3fd9a6650b55935c2e31dvboxsync
016096e367cd20c3d3c3fd9a6650b55935c2e31dvboxsync/**
b2bc8de1367ae24e1b27b53921d0b32ee3df7acdvboxsync * Set a VRAM page dirty.
016096e367cd20c3d3c3fd9a6650b55935c2e31dvboxsync *
016096e367cd20c3d3c3fd9a6650b55935c2e31dvboxsync * @param pThis VGA instance data.
7697e43970d863558b6c168a55e8948ccb18d8d1vboxsync * @param offVRAM The VRAM offset of the page to set.
7697e43970d863558b6c168a55e8948ccb18d8d1vboxsync */
7697e43970d863558b6c168a55e8948ccb18d8d1vboxsyncDECLINLINE(void) vga_set_dirty(VGAState *pThis, RTGCPHYS offVRAM)
016096e367cd20c3d3c3fd9a6650b55935c2e31dvboxsync{
016096e367cd20c3d3c3fd9a6650b55935c2e31dvboxsync AssertMsg(offVRAM < pThis->vram_size, ("offVRAM = %p, pThis->vram_size = %p\n", offVRAM, pThis->vram_size));
b2bc8de1367ae24e1b27b53921d0b32ee3df7acdvboxsync ASMBitSet(&pThis->au32DirtyBitmap[0], offVRAM >> PAGE_SHIFT);
016096e367cd20c3d3c3fd9a6650b55935c2e31dvboxsync pThis->fHasDirtyBits = true;
016096e367cd20c3d3c3fd9a6650b55935c2e31dvboxsync}
016096e367cd20c3d3c3fd9a6650b55935c2e31dvboxsync
016096e367cd20c3d3c3fd9a6650b55935c2e31dvboxsync/**
016096e367cd20c3d3c3fd9a6650b55935c2e31dvboxsync * Tests if a VRAM page is dirty.
016096e367cd20c3d3c3fd9a6650b55935c2e31dvboxsync *
b2bc8de1367ae24e1b27b53921d0b32ee3df7acdvboxsync * @returns true if dirty.
016096e367cd20c3d3c3fd9a6650b55935c2e31dvboxsync * @returns false if clean.
b2bc8de1367ae24e1b27b53921d0b32ee3df7acdvboxsync * @param pThis VGA instance data.
b2bc8de1367ae24e1b27b53921d0b32ee3df7acdvboxsync * @param offVRAM The VRAM offset of the page to check.
b2bc8de1367ae24e1b27b53921d0b32ee3df7acdvboxsync */
016096e367cd20c3d3c3fd9a6650b55935c2e31dvboxsyncDECLINLINE(bool) vga_is_dirty(VGAState *pThis, RTGCPHYS offVRAM)
016096e367cd20c3d3c3fd9a6650b55935c2e31dvboxsync{
b2bc8de1367ae24e1b27b53921d0b32ee3df7acdvboxsync AssertMsg(offVRAM < pThis->vram_size, ("offVRAM = %p, pThis->vram_size = %p\n", offVRAM, pThis->vram_size));
016096e367cd20c3d3c3fd9a6650b55935c2e31dvboxsync return ASMBitTest(&pThis->au32DirtyBitmap[0], offVRAM >> PAGE_SHIFT);
b2bc8de1367ae24e1b27b53921d0b32ee3df7acdvboxsync}
b2bc8de1367ae24e1b27b53921d0b32ee3df7acdvboxsync
016096e367cd20c3d3c3fd9a6650b55935c2e31dvboxsync/**
016096e367cd20c3d3c3fd9a6650b55935c2e31dvboxsync * Reset dirty flags in a give range.
016096e367cd20c3d3c3fd9a6650b55935c2e31dvboxsync *
b2bc8de1367ae24e1b27b53921d0b32ee3df7acdvboxsync * @param pThis VGA instance data.
b2bc8de1367ae24e1b27b53921d0b32ee3df7acdvboxsync * @param offVRAMStart Offset into the VRAM buffer of the first page.
016096e367cd20c3d3c3fd9a6650b55935c2e31dvboxsync * @param offVRAMEnd Offset into the VRAM buffer of the last page - exclusive.
016096e367cd20c3d3c3fd9a6650b55935c2e31dvboxsync */
016096e367cd20c3d3c3fd9a6650b55935c2e31dvboxsyncDECLINLINE(void) vga_reset_dirty(VGAState *pThis, RTGCPHYS offVRAMStart, RTGCPHYS offVRAMEnd)
016096e367cd20c3d3c3fd9a6650b55935c2e31dvboxsync{
9674ed871d32468dd791ad601151f46d2e843350vboxsync Assert(offVRAMStart < pThis->vram_size);
9674ed871d32468dd791ad601151f46d2e843350vboxsync Assert(offVRAMEnd <= pThis->vram_size);
9674ed871d32468dd791ad601151f46d2e843350vboxsync Assert(offVRAMStart < offVRAMEnd);
9674ed871d32468dd791ad601151f46d2e843350vboxsync ASMBitClearRange(&pThis->au32DirtyBitmap[0], offVRAMStart >> PAGE_SHIFT, offVRAMEnd >> PAGE_SHIFT);
9674ed871d32468dd791ad601151f46d2e843350vboxsync}
9674ed871d32468dd791ad601151f46d2e843350vboxsync
9674ed871d32468dd791ad601151f46d2e843350vboxsync/* force some bits to zero */
9674ed871d32468dd791ad601151f46d2e843350vboxsyncstatic const uint8_t sr_mask[8] = {
9674ed871d32468dd791ad601151f46d2e843350vboxsync (uint8_t)~0xfc,
9674ed871d32468dd791ad601151f46d2e843350vboxsync (uint8_t)~0xc2,
9674ed871d32468dd791ad601151f46d2e843350vboxsync (uint8_t)~0xf0,
9674ed871d32468dd791ad601151f46d2e843350vboxsync (uint8_t)~0xc0,
9674ed871d32468dd791ad601151f46d2e843350vboxsync (uint8_t)~0xf1,
9674ed871d32468dd791ad601151f46d2e843350vboxsync (uint8_t)~0xff,
9674ed871d32468dd791ad601151f46d2e843350vboxsync (uint8_t)~0xff,
9674ed871d32468dd791ad601151f46d2e843350vboxsync (uint8_t)~0x00,
9674ed871d32468dd791ad601151f46d2e843350vboxsync};
9674ed871d32468dd791ad601151f46d2e843350vboxsync
9674ed871d32468dd791ad601151f46d2e843350vboxsyncstatic const uint8_t gr_mask[16] = {
b2bc8de1367ae24e1b27b53921d0b32ee3df7acdvboxsync (uint8_t)~0xf0, /* 0x00 */
016096e367cd20c3d3c3fd9a6650b55935c2e31dvboxsync (uint8_t)~0xf0, /* 0x01 */
016096e367cd20c3d3c3fd9a6650b55935c2e31dvboxsync (uint8_t)~0xf0, /* 0x02 */
b2bc8de1367ae24e1b27b53921d0b32ee3df7acdvboxsync (uint8_t)~0xe0, /* 0x03 */
016096e367cd20c3d3c3fd9a6650b55935c2e31dvboxsync (uint8_t)~0xfc, /* 0x04 */
e562a8bb17c0dfa1c316708e085a3a92fcc80521vboxsync (uint8_t)~0x84, /* 0x05 */
b2bc8de1367ae24e1b27b53921d0b32ee3df7acdvboxsync (uint8_t)~0xf0, /* 0x06 */
016096e367cd20c3d3c3fd9a6650b55935c2e31dvboxsync (uint8_t)~0xf0, /* 0x07 */
e562a8bb17c0dfa1c316708e085a3a92fcc80521vboxsync (uint8_t)~0x00, /* 0x08 */
016096e367cd20c3d3c3fd9a6650b55935c2e31dvboxsync (uint8_t)~0xff, /* 0x09 */
7c48fdac0546978ed14617c8096734ce2d18c8e5vboxsync (uint8_t)~0xff, /* 0x0a */
7c48fdac0546978ed14617c8096734ce2d18c8e5vboxsync (uint8_t)~0xff, /* 0x0b */
016096e367cd20c3d3c3fd9a6650b55935c2e31dvboxsync (uint8_t)~0xff, /* 0x0c */
016096e367cd20c3d3c3fd9a6650b55935c2e31dvboxsync (uint8_t)~0xff, /* 0x0d */
b2bc8de1367ae24e1b27b53921d0b32ee3df7acdvboxsync (uint8_t)~0xff, /* 0x0e */
b2bc8de1367ae24e1b27b53921d0b32ee3df7acdvboxsync (uint8_t)~0xff, /* 0x0f */
016096e367cd20c3d3c3fd9a6650b55935c2e31dvboxsync};
e562a8bb17c0dfa1c316708e085a3a92fcc80521vboxsync
7c48fdac0546978ed14617c8096734ce2d18c8e5vboxsync#define cbswap_32(__x) \
016096e367cd20c3d3c3fd9a6650b55935c2e31dvboxsync((uint32_t)( \
3956d0151065a11e49d2213b38a5efdad46807e0vboxsync (((uint32_t)(__x) & (uint32_t)0x000000ffUL) << 24) | \
b2bc8de1367ae24e1b27b53921d0b32ee3df7acdvboxsync (((uint32_t)(__x) & (uint32_t)0x0000ff00UL) << 8) | \
7c48fdac0546978ed14617c8096734ce2d18c8e5vboxsync (((uint32_t)(__x) & (uint32_t)0x00ff0000UL) >> 8) | \
b2bc8de1367ae24e1b27b53921d0b32ee3df7acdvboxsync (((uint32_t)(__x) & (uint32_t)0xff000000UL) >> 24) ))
016096e367cd20c3d3c3fd9a6650b55935c2e31dvboxsync
016096e367cd20c3d3c3fd9a6650b55935c2e31dvboxsync#ifdef WORDS_BIGENDIAN
016096e367cd20c3d3c3fd9a6650b55935c2e31dvboxsync#define PAT(x) cbswap_32(x)
e562a8bb17c0dfa1c316708e085a3a92fcc80521vboxsync#else
e562a8bb17c0dfa1c316708e085a3a92fcc80521vboxsync#define PAT(x) (x)
e562a8bb17c0dfa1c316708e085a3a92fcc80521vboxsync#endif
e562a8bb17c0dfa1c316708e085a3a92fcc80521vboxsync
016096e367cd20c3d3c3fd9a6650b55935c2e31dvboxsync#ifdef WORDS_BIGENDIAN
016096e367cd20c3d3c3fd9a6650b55935c2e31dvboxsync#define BIG 1
016096e367cd20c3d3c3fd9a6650b55935c2e31dvboxsync#else
016096e367cd20c3d3c3fd9a6650b55935c2e31dvboxsync#define BIG 0
b2bc8de1367ae24e1b27b53921d0b32ee3df7acdvboxsync#endif
b2bc8de1367ae24e1b27b53921d0b32ee3df7acdvboxsync
9674ed871d32468dd791ad601151f46d2e843350vboxsync#ifdef WORDS_BIGENDIAN
016096e367cd20c3d3c3fd9a6650b55935c2e31dvboxsync#define GET_PLANE(data, p) (((data) >> (24 - (p) * 8)) & 0xff)
016096e367cd20c3d3c3fd9a6650b55935c2e31dvboxsync#else
016096e367cd20c3d3c3fd9a6650b55935c2e31dvboxsync#define GET_PLANE(data, p) (((data) >> ((p) * 8)) & 0xff)
3b1aa24d99d0f9cc157cf72ca76444b2feca3277vboxsync#endif
8a54ed337392872c7cfcfb96f173468bbbb0f7fcvboxsync
8a54ed337392872c7cfcfb96f173468bbbb0f7fcvboxsyncstatic const uint32_t mask16[16] = {
3ebd5757516d21eccdad25ddd456d2913c2fb215vboxsync PAT(0x00000000),
3ebd5757516d21eccdad25ddd456d2913c2fb215vboxsync PAT(0x000000ff),
3ebd5757516d21eccdad25ddd456d2913c2fb215vboxsync PAT(0x0000ff00),
3ebd5757516d21eccdad25ddd456d2913c2fb215vboxsync PAT(0x0000ffff),
3ebd5757516d21eccdad25ddd456d2913c2fb215vboxsync PAT(0x00ff0000),
3ebd5757516d21eccdad25ddd456d2913c2fb215vboxsync PAT(0x00ff00ff),
3ebd5757516d21eccdad25ddd456d2913c2fb215vboxsync PAT(0x00ffff00),
3ebd5757516d21eccdad25ddd456d2913c2fb215vboxsync PAT(0x00ffffff),
3ebd5757516d21eccdad25ddd456d2913c2fb215vboxsync PAT(0xff000000),
3ebd5757516d21eccdad25ddd456d2913c2fb215vboxsync PAT(0xff0000ff),
3ebd5757516d21eccdad25ddd456d2913c2fb215vboxsync PAT(0xff00ff00),
3ebd5757516d21eccdad25ddd456d2913c2fb215vboxsync PAT(0xff00ffff),
016096e367cd20c3d3c3fd9a6650b55935c2e31dvboxsync PAT(0xffff0000),
e562a8bb17c0dfa1c316708e085a3a92fcc80521vboxsync PAT(0xffff00ff),
e562a8bb17c0dfa1c316708e085a3a92fcc80521vboxsync PAT(0xffffff00),
63787aca0a2a16ec959a5294148726ccf898ddf1vboxsync PAT(0xffffffff),
016096e367cd20c3d3c3fd9a6650b55935c2e31dvboxsync};
3ebd5757516d21eccdad25ddd456d2913c2fb215vboxsync
3ebd5757516d21eccdad25ddd456d2913c2fb215vboxsync#undef PAT
3ebd5757516d21eccdad25ddd456d2913c2fb215vboxsync
3ebd5757516d21eccdad25ddd456d2913c2fb215vboxsync#ifdef WORDS_BIGENDIAN
016096e367cd20c3d3c3fd9a6650b55935c2e31dvboxsync#define PAT(x) (x)
016096e367cd20c3d3c3fd9a6650b55935c2e31dvboxsync#else
016096e367cd20c3d3c3fd9a6650b55935c2e31dvboxsync#define PAT(x) cbswap_32(x)
3ebd5757516d21eccdad25ddd456d2913c2fb215vboxsync#endif
3ebd5757516d21eccdad25ddd456d2913c2fb215vboxsync
016096e367cd20c3d3c3fd9a6650b55935c2e31dvboxsyncstatic const uint32_t dmask16[16] = {
016096e367cd20c3d3c3fd9a6650b55935c2e31dvboxsync PAT(0x00000000),
016096e367cd20c3d3c3fd9a6650b55935c2e31dvboxsync PAT(0x000000ff),
3ebd5757516d21eccdad25ddd456d2913c2fb215vboxsync PAT(0x0000ff00),
3ebd5757516d21eccdad25ddd456d2913c2fb215vboxsync PAT(0x0000ffff),
016096e367cd20c3d3c3fd9a6650b55935c2e31dvboxsync PAT(0x00ff0000),
3ebd5757516d21eccdad25ddd456d2913c2fb215vboxsync PAT(0x00ff00ff),
016096e367cd20c3d3c3fd9a6650b55935c2e31dvboxsync PAT(0x00ffff00),
3ebd5757516d21eccdad25ddd456d2913c2fb215vboxsync PAT(0x00ffffff),
3ebd5757516d21eccdad25ddd456d2913c2fb215vboxsync PAT(0xff000000),
016096e367cd20c3d3c3fd9a6650b55935c2e31dvboxsync PAT(0xff0000ff),
3ebd5757516d21eccdad25ddd456d2913c2fb215vboxsync PAT(0xff00ff00),
016096e367cd20c3d3c3fd9a6650b55935c2e31dvboxsync PAT(0xff00ffff),
3ebd5757516d21eccdad25ddd456d2913c2fb215vboxsync PAT(0xffff0000),
3ebd5757516d21eccdad25ddd456d2913c2fb215vboxsync PAT(0xffff00ff),
016096e367cd20c3d3c3fd9a6650b55935c2e31dvboxsync PAT(0xffffff00),
3ebd5757516d21eccdad25ddd456d2913c2fb215vboxsync PAT(0xffffffff),
016096e367cd20c3d3c3fd9a6650b55935c2e31dvboxsync};
3ebd5757516d21eccdad25ddd456d2913c2fb215vboxsync
3ebd5757516d21eccdad25ddd456d2913c2fb215vboxsyncstatic const uint32_t dmask4[4] = {
016096e367cd20c3d3c3fd9a6650b55935c2e31dvboxsync PAT(0x00000000),
3ebd5757516d21eccdad25ddd456d2913c2fb215vboxsync PAT(0x0000ffff),
016096e367cd20c3d3c3fd9a6650b55935c2e31dvboxsync PAT(0xffff0000),
3ebd5757516d21eccdad25ddd456d2913c2fb215vboxsync PAT(0xffffffff),
3ebd5757516d21eccdad25ddd456d2913c2fb215vboxsync};
016096e367cd20c3d3c3fd9a6650b55935c2e31dvboxsync
3ebd5757516d21eccdad25ddd456d2913c2fb215vboxsync#if defined(IN_RING3)
016096e367cd20c3d3c3fd9a6650b55935c2e31dvboxsyncstatic uint32_t expand4[256];
3ebd5757516d21eccdad25ddd456d2913c2fb215vboxsyncstatic uint16_t expand2[256];
3ebd5757516d21eccdad25ddd456d2913c2fb215vboxsyncstatic uint8_t expand4to8[16];
3ebd5757516d21eccdad25ddd456d2913c2fb215vboxsync#endif /* IN_RING3 */
3ebd5757516d21eccdad25ddd456d2913c2fb215vboxsync
3ebd5757516d21eccdad25ddd456d2913c2fb215vboxsync/* Update the values needed for calculating Vertical Retrace and
016096e367cd20c3d3c3fd9a6650b55935c2e31dvboxsync * Display Enable status bits more or less accurately. The Display Enable
016096e367cd20c3d3c3fd9a6650b55935c2e31dvboxsync * bit is set (indicating *disabled* display signal) when either the
3ebd5757516d21eccdad25ddd456d2913c2fb215vboxsync * horizontal (hblank) or vertical (vblank) blanking is active. The
3ebd5757516d21eccdad25ddd456d2913c2fb215vboxsync * Vertical Retrace bit is set when vertical retrace (vsync) is active.
016096e367cd20c3d3c3fd9a6650b55935c2e31dvboxsync * Unless the CRTC is horribly misprogrammed, vsync implies vblank.
016096e367cd20c3d3c3fd9a6650b55935c2e31dvboxsync */
016096e367cd20c3d3c3fd9a6650b55935c2e31dvboxsyncstatic void vga_update_retrace_state(VGAState *s)
3ebd5757516d21eccdad25ddd456d2913c2fb215vboxsync{
3ebd5757516d21eccdad25ddd456d2913c2fb215vboxsync unsigned htotal_cclks, vtotal_lines, chars_per_sec;
3ebd5757516d21eccdad25ddd456d2913c2fb215vboxsync unsigned hblank_start_cclk, hblank_end_cclk, hblank_width, hblank_skew_cclks;
3ebd5757516d21eccdad25ddd456d2913c2fb215vboxsync unsigned vsync_start_line, vsync_end, vsync_width;
3ebd5757516d21eccdad25ddd456d2913c2fb215vboxsync unsigned vblank_start_line, vblank_end, vblank_width;
3ebd5757516d21eccdad25ddd456d2913c2fb215vboxsync unsigned char_dots, clock_doubled, clock_index;
3ebd5757516d21eccdad25ddd456d2913c2fb215vboxsync const int clocks[] = {25175000, 28322000, 25175000, 25175000};
3ebd5757516d21eccdad25ddd456d2913c2fb215vboxsync vga_retrace_s *r = &s->retrace_state;
3ebd5757516d21eccdad25ddd456d2913c2fb215vboxsync
e562a8bb17c0dfa1c316708e085a3a92fcc80521vboxsync /* For horizontal timings, we only care about the blanking start/end. */
e562a8bb17c0dfa1c316708e085a3a92fcc80521vboxsync htotal_cclks = s->cr[0x00] + 5;
e562a8bb17c0dfa1c316708e085a3a92fcc80521vboxsync hblank_start_cclk = s->cr[0x02];
3ebd5757516d21eccdad25ddd456d2913c2fb215vboxsync hblank_end_cclk = (s->cr[0x03] & 0x1f) + ((s->cr[0x05] & 0x80) >> 2);
3ebd5757516d21eccdad25ddd456d2913c2fb215vboxsync hblank_skew_cclks = (s->cr[0x03] >> 5) & 3;
3ebd5757516d21eccdad25ddd456d2913c2fb215vboxsync
e562a8bb17c0dfa1c316708e085a3a92fcc80521vboxsync /* For vertical timings, we need both the blanking start/end... */
e562a8bb17c0dfa1c316708e085a3a92fcc80521vboxsync vtotal_lines = s->cr[0x06] + ((s->cr[0x07] & 1) << 8) + ((s->cr[0x07] & 0x20) << 4) + 2;
63787aca0a2a16ec959a5294148726ccf898ddf1vboxsync vblank_start_line = s->cr[0x15] + ((s->cr[0x07] & 8) << 5) + ((s->cr[0x09] & 0x20) << 4);
016096e367cd20c3d3c3fd9a6650b55935c2e31dvboxsync vblank_end = s->cr[0x16];
f2a212e40c7307d2b8ef9b2658a539f5ac41e683vboxsync /* ... and the vertical retrace (vsync) start/end. */
f2a212e40c7307d2b8ef9b2658a539f5ac41e683vboxsync vsync_start_line = s->cr[0x10] + ((s->cr[0x07] & 4) << 6) + ((s->cr[0x07] & 0x80) << 2);
f2a212e40c7307d2b8ef9b2658a539f5ac41e683vboxsync vsync_end = s->cr[0x11] & 0xf;
016096e367cd20c3d3c3fd9a6650b55935c2e31dvboxsync
016096e367cd20c3d3c3fd9a6650b55935c2e31dvboxsync /* Calculate the blanking and sync widths. The way it's implemented in
b2bc8de1367ae24e1b27b53921d0b32ee3df7acdvboxsync * the VGA with limited-width compare counters is quite a piece of work.
3ebd5757516d21eccdad25ddd456d2913c2fb215vboxsync */
016096e367cd20c3d3c3fd9a6650b55935c2e31dvboxsync hblank_width = (hblank_end_cclk - hblank_start_cclk) & 0x3f;/* 6 bits */
016096e367cd20c3d3c3fd9a6650b55935c2e31dvboxsync vblank_width = (vblank_end - vblank_start_line) & 0xff; /* 8 bits */
016096e367cd20c3d3c3fd9a6650b55935c2e31dvboxsync vsync_width = (vsync_end - vsync_start_line) & 0xf; /* 4 bits */
016096e367cd20c3d3c3fd9a6650b55935c2e31dvboxsync
e562a8bb17c0dfa1c316708e085a3a92fcc80521vboxsync /* Calculate the dot and character clock rates. */
3ebd5757516d21eccdad25ddd456d2913c2fb215vboxsync clock_doubled = (s->sr[0x01] >> 3) & 1; /* Clock doubling bit. */
067712a8118321d460f98b437ecafb6b8dbce301vboxsync clock_index = (s->msr >> 2) & 3;
7c48fdac0546978ed14617c8096734ce2d18c8e5vboxsync char_dots = (s->sr[0x01] & 1) ? 8 : 9; /* 8 or 9 dots per cclk. */
3ebd5757516d21eccdad25ddd456d2913c2fb215vboxsync
f2a212e40c7307d2b8ef9b2658a539f5ac41e683vboxsync chars_per_sec = clocks[clock_index] / char_dots;
7c48fdac0546978ed14617c8096734ce2d18c8e5vboxsync Assert(chars_per_sec); /* Can't possibly be zero. */
7c48fdac0546978ed14617c8096734ce2d18c8e5vboxsync
f2a212e40c7307d2b8ef9b2658a539f5ac41e683vboxsync htotal_cclks <<= clock_doubled;
7c48fdac0546978ed14617c8096734ce2d18c8e5vboxsync
7c48fdac0546978ed14617c8096734ce2d18c8e5vboxsync /* Calculate the number of cclks per entire frame. */
3ebd5757516d21eccdad25ddd456d2913c2fb215vboxsync r->frame_cclks = vtotal_lines * htotal_cclks;
7c48fdac0546978ed14617c8096734ce2d18c8e5vboxsync Assert(r->frame_cclks); /* Can't possibly be zero. */
f2a212e40c7307d2b8ef9b2658a539f5ac41e683vboxsync
016096e367cd20c3d3c3fd9a6650b55935c2e31dvboxsync if (r->v_freq_hz) { /* Could be set to emulate a specific rate. */
7c48fdac0546978ed14617c8096734ce2d18c8e5vboxsync r->cclk_ns = 1000000000 / (r->frame_cclks * r->v_freq_hz);
b2bc8de1367ae24e1b27b53921d0b32ee3df7acdvboxsync } else {
b2bc8de1367ae24e1b27b53921d0b32ee3df7acdvboxsync r->cclk_ns = 1000000000 / chars_per_sec;
016096e367cd20c3d3c3fd9a6650b55935c2e31dvboxsync }
3ebd5757516d21eccdad25ddd456d2913c2fb215vboxsync Assert(r->cclk_ns);
016096e367cd20c3d3c3fd9a6650b55935c2e31dvboxsync r->frame_ns = r->frame_cclks * r->cclk_ns;
f2a212e40c7307d2b8ef9b2658a539f5ac41e683vboxsync
f2a212e40c7307d2b8ef9b2658a539f5ac41e683vboxsync /* Calculate timings in cclks/lines. Stored but not directly used. */
f2a212e40c7307d2b8ef9b2658a539f5ac41e683vboxsync r->hb_start = hblank_start_cclk + hblank_skew_cclks;
fe554d9c0e3a6de4ba221610ac95a44c7d288e01vboxsync r->hb_end = hblank_start_cclk + hblank_width + hblank_skew_cclks;
016096e367cd20c3d3c3fd9a6650b55935c2e31dvboxsync r->h_total = htotal_cclks;
3ebd5757516d21eccdad25ddd456d2913c2fb215vboxsync Assert(r->h_total); /* Can't possibly be zero. */
b2bc8de1367ae24e1b27b53921d0b32ee3df7acdvboxsync
3ebd5757516d21eccdad25ddd456d2913c2fb215vboxsync r->vb_start = vblank_start_line;
e562a8bb17c0dfa1c316708e085a3a92fcc80521vboxsync r->vb_end = vblank_start_line + vblank_width + 1;
016096e367cd20c3d3c3fd9a6650b55935c2e31dvboxsync r->vs_start = vsync_start_line;
016096e367cd20c3d3c3fd9a6650b55935c2e31dvboxsync r->vs_end = vsync_start_line + vsync_width + 1;
b2bc8de1367ae24e1b27b53921d0b32ee3df7acdvboxsync
016096e367cd20c3d3c3fd9a6650b55935c2e31dvboxsync /* Calculate timings in nanoseconds. For easier comparisons, the frame
016096e367cd20c3d3c3fd9a6650b55935c2e31dvboxsync * is considered to start at the beginning of the vertical and horizontal
016096e367cd20c3d3c3fd9a6650b55935c2e31dvboxsync * blanking period.
016096e367cd20c3d3c3fd9a6650b55935c2e31dvboxsync */
016096e367cd20c3d3c3fd9a6650b55935c2e31dvboxsync r->h_total_ns = htotal_cclks * r->cclk_ns;
b2bc8de1367ae24e1b27b53921d0b32ee3df7acdvboxsync r->hb_end_ns = hblank_width * r->cclk_ns;
016096e367cd20c3d3c3fd9a6650b55935c2e31dvboxsync r->vb_end_ns = vblank_width * r->h_total_ns;
016096e367cd20c3d3c3fd9a6650b55935c2e31dvboxsync r->vs_start_ns = (r->vs_start - r->vb_start) * r->h_total_ns;
e562a8bb17c0dfa1c316708e085a3a92fcc80521vboxsync r->vs_end_ns = (r->vs_end - r->vb_start) * r->h_total_ns;
e562a8bb17c0dfa1c316708e085a3a92fcc80521vboxsync Assert(r->h_total_ns); /* See h_total. */
e562a8bb17c0dfa1c316708e085a3a92fcc80521vboxsync}
b2bc8de1367ae24e1b27b53921d0b32ee3df7acdvboxsync
016096e367cd20c3d3c3fd9a6650b55935c2e31dvboxsyncstatic uint8_t vga_retrace(VGAState *s)
f2a212e40c7307d2b8ef9b2658a539f5ac41e683vboxsync{
f2a212e40c7307d2b8ef9b2658a539f5ac41e683vboxsync vga_retrace_s *r = &s->retrace_state;
016096e367cd20c3d3c3fd9a6650b55935c2e31dvboxsync
f2a212e40c7307d2b8ef9b2658a539f5ac41e683vboxsync if (r->frame_ns) {
b2bc8de1367ae24e1b27b53921d0b32ee3df7acdvboxsync uint8_t val = s->st01 & ~(ST01_V_RETRACE | ST01_DISP_ENABLE);
e562a8bb17c0dfa1c316708e085a3a92fcc80521vboxsync unsigned cur_frame_ns, cur_line_ns;
016096e367cd20c3d3c3fd9a6650b55935c2e31dvboxsync uint64_t time_ns;
762a68c2bb3ccde807330e3d1cb05f8b244a5f72vboxsync
b2bc8de1367ae24e1b27b53921d0b32ee3df7acdvboxsync time_ns = PDMDevHlpTMTimeVirtGetNano(VGASTATE2DEVINS(s));
e562a8bb17c0dfa1c316708e085a3a92fcc80521vboxsync
016096e367cd20c3d3c3fd9a6650b55935c2e31dvboxsync /* Determine the time within the frame. */
016096e367cd20c3d3c3fd9a6650b55935c2e31dvboxsync cur_frame_ns = time_ns % r->frame_ns;
b2bc8de1367ae24e1b27b53921d0b32ee3df7acdvboxsync
36fbf6dcd3e6b2e5891456b730577ff0eb355c9fvboxsync /* See if we're in the vertical blanking period... */
016096e367cd20c3d3c3fd9a6650b55935c2e31dvboxsync if (cur_frame_ns < r->vb_end_ns) {
e562a8bb17c0dfa1c316708e085a3a92fcc80521vboxsync val |= ST01_DISP_ENABLE;
e562a8bb17c0dfa1c316708e085a3a92fcc80521vboxsync /* ... and additionally in the vertical sync period. */
e562a8bb17c0dfa1c316708e085a3a92fcc80521vboxsync if (cur_frame_ns >= r->vs_start_ns && cur_frame_ns <= r->vs_end_ns)
e562a8bb17c0dfa1c316708e085a3a92fcc80521vboxsync val |= ST01_V_RETRACE;
e562a8bb17c0dfa1c316708e085a3a92fcc80521vboxsync } else {
e562a8bb17c0dfa1c316708e085a3a92fcc80521vboxsync /* Determine the time within the current scanline. */
e562a8bb17c0dfa1c316708e085a3a92fcc80521vboxsync cur_line_ns = cur_frame_ns % r->h_total_ns;
016096e367cd20c3d3c3fd9a6650b55935c2e31dvboxsync /* See if we're in the horizontal blanking period. */
e562a8bb17c0dfa1c316708e085a3a92fcc80521vboxsync if (cur_line_ns < r->hb_end_ns)
016096e367cd20c3d3c3fd9a6650b55935c2e31dvboxsync val |= ST01_DISP_ENABLE;
e562a8bb17c0dfa1c316708e085a3a92fcc80521vboxsync }
7c48fdac0546978ed14617c8096734ce2d18c8e5vboxsync return val;
e562a8bb17c0dfa1c316708e085a3a92fcc80521vboxsync } else {
e562a8bb17c0dfa1c316708e085a3a92fcc80521vboxsync return s->st01 ^ (ST01_V_RETRACE | ST01_DISP_ENABLE);
e562a8bb17c0dfa1c316708e085a3a92fcc80521vboxsync }
e562a8bb17c0dfa1c316708e085a3a92fcc80521vboxsync}
016096e367cd20c3d3c3fd9a6650b55935c2e31dvboxsync
016096e367cd20c3d3c3fd9a6650b55935c2e31dvboxsyncint vga_ioport_invalid(VGAState *s, uint32_t addr)
b2bc8de1367ae24e1b27b53921d0b32ee3df7acdvboxsync{
016096e367cd20c3d3c3fd9a6650b55935c2e31dvboxsync if (s->msr & MSR_COLOR_EMULATION) {
016096e367cd20c3d3c3fd9a6650b55935c2e31dvboxsync /* Color */
016096e367cd20c3d3c3fd9a6650b55935c2e31dvboxsync return (addr >= 0x3b0 && addr <= 0x3bf);
016096e367cd20c3d3c3fd9a6650b55935c2e31dvboxsync } else {
016096e367cd20c3d3c3fd9a6650b55935c2e31dvboxsync /* Monochrome */
b2bc8de1367ae24e1b27b53921d0b32ee3df7acdvboxsync return (addr >= 0x3d0 && addr <= 0x3df);
016096e367cd20c3d3c3fd9a6650b55935c2e31dvboxsync }
016096e367cd20c3d3c3fd9a6650b55935c2e31dvboxsync}
016096e367cd20c3d3c3fd9a6650b55935c2e31dvboxsync
016096e367cd20c3d3c3fd9a6650b55935c2e31dvboxsyncstatic uint32_t vga_ioport_read(void *opaque, uint32_t addr)
016096e367cd20c3d3c3fd9a6650b55935c2e31dvboxsync{
b2bc8de1367ae24e1b27b53921d0b32ee3df7acdvboxsync VGAState *s = (VGAState*)opaque;
016096e367cd20c3d3c3fd9a6650b55935c2e31dvboxsync int val, index;
016096e367cd20c3d3c3fd9a6650b55935c2e31dvboxsync
e562a8bb17c0dfa1c316708e085a3a92fcc80521vboxsync /* check port range access depending on color/monochrome mode */
e562a8bb17c0dfa1c316708e085a3a92fcc80521vboxsync if (vga_ioport_invalid(s, addr)) {
e562a8bb17c0dfa1c316708e085a3a92fcc80521vboxsync val = 0xff;
016096e367cd20c3d3c3fd9a6650b55935c2e31dvboxsync Log(("VGA: following read ignored\n"));
016096e367cd20c3d3c3fd9a6650b55935c2e31dvboxsync } else {
016096e367cd20c3d3c3fd9a6650b55935c2e31dvboxsync switch(addr) {
b2bc8de1367ae24e1b27b53921d0b32ee3df7acdvboxsync case 0x3c0:
016096e367cd20c3d3c3fd9a6650b55935c2e31dvboxsync if (s->ar_flip_flop == 0) {
016096e367cd20c3d3c3fd9a6650b55935c2e31dvboxsync val = s->ar_index;
016096e367cd20c3d3c3fd9a6650b55935c2e31dvboxsync } else {
016096e367cd20c3d3c3fd9a6650b55935c2e31dvboxsync val = 0;
3b1aa24d99d0f9cc157cf72ca76444b2feca3277vboxsync }
8a54ed337392872c7cfcfb96f173468bbbb0f7fcvboxsync break;
8a54ed337392872c7cfcfb96f173468bbbb0f7fcvboxsync case 0x3c1:
8a54ed337392872c7cfcfb96f173468bbbb0f7fcvboxsync index = s->ar_index & 0x1f;
8a54ed337392872c7cfcfb96f173468bbbb0f7fcvboxsync if (index < 21)
8a54ed337392872c7cfcfb96f173468bbbb0f7fcvboxsync val = s->ar[index];
8a54ed337392872c7cfcfb96f173468bbbb0f7fcvboxsync else
8a54ed337392872c7cfcfb96f173468bbbb0f7fcvboxsync val = 0;
8a54ed337392872c7cfcfb96f173468bbbb0f7fcvboxsync break;
8a54ed337392872c7cfcfb96f173468bbbb0f7fcvboxsync case 0x3c2:
e562a8bb17c0dfa1c316708e085a3a92fcc80521vboxsync val = s->st00;
e562a8bb17c0dfa1c316708e085a3a92fcc80521vboxsync break;
8a54ed337392872c7cfcfb96f173468bbbb0f7fcvboxsync case 0x3c4:
e562a8bb17c0dfa1c316708e085a3a92fcc80521vboxsync val = s->sr_index;
8a54ed337392872c7cfcfb96f173468bbbb0f7fcvboxsync break;
e562a8bb17c0dfa1c316708e085a3a92fcc80521vboxsync case 0x3c5:
e562a8bb17c0dfa1c316708e085a3a92fcc80521vboxsync val = s->sr[s->sr_index];
8a54ed337392872c7cfcfb96f173468bbbb0f7fcvboxsync Log2(("vga: read SR%x = 0x%02x\n", s->sr_index, val));
8a54ed337392872c7cfcfb96f173468bbbb0f7fcvboxsync break;
8a54ed337392872c7cfcfb96f173468bbbb0f7fcvboxsync case 0x3c7:
8a54ed337392872c7cfcfb96f173468bbbb0f7fcvboxsync val = s->dac_state;
e562a8bb17c0dfa1c316708e085a3a92fcc80521vboxsync break;
63787aca0a2a16ec959a5294148726ccf898ddf1vboxsync case 0x3c8:
e562a8bb17c0dfa1c316708e085a3a92fcc80521vboxsync val = s->dac_write_index;
8a54ed337392872c7cfcfb96f173468bbbb0f7fcvboxsync break;
8a54ed337392872c7cfcfb96f173468bbbb0f7fcvboxsync case 0x3c9:
8a54ed337392872c7cfcfb96f173468bbbb0f7fcvboxsync val = s->palette[s->dac_read_index * 3 + s->dac_sub_index];
8a54ed337392872c7cfcfb96f173468bbbb0f7fcvboxsync if (++s->dac_sub_index == 3) {
e562a8bb17c0dfa1c316708e085a3a92fcc80521vboxsync s->dac_sub_index = 0;
63787aca0a2a16ec959a5294148726ccf898ddf1vboxsync s->dac_read_index++;
e562a8bb17c0dfa1c316708e085a3a92fcc80521vboxsync }
8a54ed337392872c7cfcfb96f173468bbbb0f7fcvboxsync break;
8a54ed337392872c7cfcfb96f173468bbbb0f7fcvboxsync case 0x3ca:
8a54ed337392872c7cfcfb96f173468bbbb0f7fcvboxsync val = s->fcr;
8a54ed337392872c7cfcfb96f173468bbbb0f7fcvboxsync break;
e562a8bb17c0dfa1c316708e085a3a92fcc80521vboxsync case 0x3cc:
8a54ed337392872c7cfcfb96f173468bbbb0f7fcvboxsync val = s->msr;
e562a8bb17c0dfa1c316708e085a3a92fcc80521vboxsync break;
8a54ed337392872c7cfcfb96f173468bbbb0f7fcvboxsync case 0x3ce:
8a54ed337392872c7cfcfb96f173468bbbb0f7fcvboxsync val = s->gr_index;
8a54ed337392872c7cfcfb96f173468bbbb0f7fcvboxsync break;
8a54ed337392872c7cfcfb96f173468bbbb0f7fcvboxsync case 0x3cf:
e562a8bb17c0dfa1c316708e085a3a92fcc80521vboxsync val = s->gr[s->gr_index];
8a54ed337392872c7cfcfb96f173468bbbb0f7fcvboxsync Log2(("vga: read GR%x = 0x%02x\n", s->gr_index, val));
e562a8bb17c0dfa1c316708e085a3a92fcc80521vboxsync break;
8a54ed337392872c7cfcfb96f173468bbbb0f7fcvboxsync case 0x3b4:
8a54ed337392872c7cfcfb96f173468bbbb0f7fcvboxsync case 0x3d4:
8a54ed337392872c7cfcfb96f173468bbbb0f7fcvboxsync val = s->cr_index;
8a54ed337392872c7cfcfb96f173468bbbb0f7fcvboxsync break;
8a54ed337392872c7cfcfb96f173468bbbb0f7fcvboxsync case 0x3b5:
8a54ed337392872c7cfcfb96f173468bbbb0f7fcvboxsync case 0x3d5:
e562a8bb17c0dfa1c316708e085a3a92fcc80521vboxsync val = s->cr[s->cr_index];
e562a8bb17c0dfa1c316708e085a3a92fcc80521vboxsync Log2(("vga: read CR%x = 0x%02x\n", s->cr_index, val));
8a54ed337392872c7cfcfb96f173468bbbb0f7fcvboxsync break;
8a54ed337392872c7cfcfb96f173468bbbb0f7fcvboxsync case 0x3ba:
8a54ed337392872c7cfcfb96f173468bbbb0f7fcvboxsync case 0x3da:
8a54ed337392872c7cfcfb96f173468bbbb0f7fcvboxsync val = s->st01 = vga_retrace(s);
8a54ed337392872c7cfcfb96f173468bbbb0f7fcvboxsync s->ar_flip_flop = 0;
e562a8bb17c0dfa1c316708e085a3a92fcc80521vboxsync break;
e562a8bb17c0dfa1c316708e085a3a92fcc80521vboxsync default:
e562a8bb17c0dfa1c316708e085a3a92fcc80521vboxsync val = 0x00;
8a54ed337392872c7cfcfb96f173468bbbb0f7fcvboxsync break;
8a54ed337392872c7cfcfb96f173468bbbb0f7fcvboxsync }
8a54ed337392872c7cfcfb96f173468bbbb0f7fcvboxsync }
8a54ed337392872c7cfcfb96f173468bbbb0f7fcvboxsync Log(("VGA: read addr=0x%04x data=0x%02x\n", addr, val));
8a54ed337392872c7cfcfb96f173468bbbb0f7fcvboxsync return val;
8a54ed337392872c7cfcfb96f173468bbbb0f7fcvboxsync}
8a54ed337392872c7cfcfb96f173468bbbb0f7fcvboxsync
8a54ed337392872c7cfcfb96f173468bbbb0f7fcvboxsyncstatic void vga_ioport_write(void *opaque, uint32_t addr, uint32_t val)
e562a8bb17c0dfa1c316708e085a3a92fcc80521vboxsync{
8a54ed337392872c7cfcfb96f173468bbbb0f7fcvboxsync VGAState *s = (VGAState*)opaque;
e562a8bb17c0dfa1c316708e085a3a92fcc80521vboxsync int index;
e562a8bb17c0dfa1c316708e085a3a92fcc80521vboxsync
8a54ed337392872c7cfcfb96f173468bbbb0f7fcvboxsync Log(("VGA: write addr=0x%04x data=0x%02x\n", addr, val));
8a54ed337392872c7cfcfb96f173468bbbb0f7fcvboxsync
8a54ed337392872c7cfcfb96f173468bbbb0f7fcvboxsync /* check port range access depending on color/monochrome mode */
8a54ed337392872c7cfcfb96f173468bbbb0f7fcvboxsync if (vga_ioport_invalid(s, addr)) {
8a54ed337392872c7cfcfb96f173468bbbb0f7fcvboxsync Log(("VGA: previous write ignored\n"));
8a54ed337392872c7cfcfb96f173468bbbb0f7fcvboxsync return;
8a54ed337392872c7cfcfb96f173468bbbb0f7fcvboxsync }
8a54ed337392872c7cfcfb96f173468bbbb0f7fcvboxsync
8a54ed337392872c7cfcfb96f173468bbbb0f7fcvboxsync switch(addr) {
8a54ed337392872c7cfcfb96f173468bbbb0f7fcvboxsync case 0x3c0:
8a54ed337392872c7cfcfb96f173468bbbb0f7fcvboxsync if (s->ar_flip_flop == 0) {
8a54ed337392872c7cfcfb96f173468bbbb0f7fcvboxsync val &= 0x3f;
8a54ed337392872c7cfcfb96f173468bbbb0f7fcvboxsync s->ar_index = val;
3ebd5757516d21eccdad25ddd456d2913c2fb215vboxsync } else {
3ebd5757516d21eccdad25ddd456d2913c2fb215vboxsync index = s->ar_index & 0x1f;
3ebd5757516d21eccdad25ddd456d2913c2fb215vboxsync switch(index) {
3ebd5757516d21eccdad25ddd456d2913c2fb215vboxsync case 0x00: case 0x01: case 0x02: case 0x03: case 0x04: case 0x05: case 0x06: case 0x07:
3ebd5757516d21eccdad25ddd456d2913c2fb215vboxsync case 0x08: case 0x09: case 0x0a: case 0x0b: case 0x0c: case 0x0d: case 0x0e: case 0x0f:
3ebd5757516d21eccdad25ddd456d2913c2fb215vboxsync s->ar[index] = val & 0x3f;
3ebd5757516d21eccdad25ddd456d2913c2fb215vboxsync break;
3ebd5757516d21eccdad25ddd456d2913c2fb215vboxsync case 0x10:
e562a8bb17c0dfa1c316708e085a3a92fcc80521vboxsync s->ar[index] = val & ~0x10;
e562a8bb17c0dfa1c316708e085a3a92fcc80521vboxsync break;
e562a8bb17c0dfa1c316708e085a3a92fcc80521vboxsync case 0x11:
3ebd5757516d21eccdad25ddd456d2913c2fb215vboxsync s->ar[index] = val;
3ebd5757516d21eccdad25ddd456d2913c2fb215vboxsync break;
016096e367cd20c3d3c3fd9a6650b55935c2e31dvboxsync case 0x12:
e562a8bb17c0dfa1c316708e085a3a92fcc80521vboxsync s->ar[index] = val & ~0xc0;
e562a8bb17c0dfa1c316708e085a3a92fcc80521vboxsync break;
016096e367cd20c3d3c3fd9a6650b55935c2e31dvboxsync case 0x13:
3ebd5757516d21eccdad25ddd456d2913c2fb215vboxsync s->ar[index] = val & ~0xf0;
016096e367cd20c3d3c3fd9a6650b55935c2e31dvboxsync break;
016096e367cd20c3d3c3fd9a6650b55935c2e31dvboxsync case 0x14:
016096e367cd20c3d3c3fd9a6650b55935c2e31dvboxsync s->ar[index] = val & ~0xf0;
016096e367cd20c3d3c3fd9a6650b55935c2e31dvboxsync break;
016096e367cd20c3d3c3fd9a6650b55935c2e31dvboxsync default:
016096e367cd20c3d3c3fd9a6650b55935c2e31dvboxsync break;
016096e367cd20c3d3c3fd9a6650b55935c2e31dvboxsync }
b2bc8de1367ae24e1b27b53921d0b32ee3df7acdvboxsync }
016096e367cd20c3d3c3fd9a6650b55935c2e31dvboxsync s->ar_flip_flop ^= 1;
016096e367cd20c3d3c3fd9a6650b55935c2e31dvboxsync break;
e562a8bb17c0dfa1c316708e085a3a92fcc80521vboxsync case 0x3c2:
b2bc8de1367ae24e1b27b53921d0b32ee3df7acdvboxsync s->msr = val & ~0x10;
3ebd5757516d21eccdad25ddd456d2913c2fb215vboxsync if (s->fRealRetrace)
3614117c1132a61599e6190939e775cafe549411vboxsync vga_update_retrace_state(s);
034f0367d3b0431c6346b1a3af3abb435ee50d4evboxsync break;
3614117c1132a61599e6190939e775cafe549411vboxsync case 0x3c4:
3614117c1132a61599e6190939e775cafe549411vboxsync s->sr_index = val & 7;
3614117c1132a61599e6190939e775cafe549411vboxsync break;
3ebd5757516d21eccdad25ddd456d2913c2fb215vboxsync case 0x3c5:
3ebd5757516d21eccdad25ddd456d2913c2fb215vboxsync Log2(("vga: write SR%x = 0x%02x\n", s->sr_index, val));
3ebd5757516d21eccdad25ddd456d2913c2fb215vboxsync s->sr[s->sr_index] = val & sr_mask[s->sr_index];
3ebd5757516d21eccdad25ddd456d2913c2fb215vboxsync if (s->fRealRetrace && s->sr_index == 0x01)
3ebd5757516d21eccdad25ddd456d2913c2fb215vboxsync vga_update_retrace_state(s);
3ebd5757516d21eccdad25ddd456d2913c2fb215vboxsync#ifndef IN_RC
3ebd5757516d21eccdad25ddd456d2913c2fb215vboxsync /* The VGA region is (could be) affected by this change; reset all aliases we've created. */
3ebd5757516d21eccdad25ddd456d2913c2fb215vboxsync if ( s->sr_index == 4 /* mode */
3ebd5757516d21eccdad25ddd456d2913c2fb215vboxsync || s->sr_index == 2 /* plane mask */)
e562a8bb17c0dfa1c316708e085a3a92fcc80521vboxsync {
e562a8bb17c0dfa1c316708e085a3a92fcc80521vboxsync if (s->fRemappedVGA)
e562a8bb17c0dfa1c316708e085a3a92fcc80521vboxsync {
e562a8bb17c0dfa1c316708e085a3a92fcc80521vboxsync IOMMMIOResetRegion(PDMDevHlpGetVM(s->CTX_SUFF(pDevIns)), 0x000a0000);
3ebd5757516d21eccdad25ddd456d2913c2fb215vboxsync s->fRemappedVGA = false;
3ebd5757516d21eccdad25ddd456d2913c2fb215vboxsync }
3ebd5757516d21eccdad25ddd456d2913c2fb215vboxsync }
3ebd5757516d21eccdad25ddd456d2913c2fb215vboxsync#endif
63787aca0a2a16ec959a5294148726ccf898ddf1vboxsync break;
3ebd5757516d21eccdad25ddd456d2913c2fb215vboxsync case 0x3c7:
016096e367cd20c3d3c3fd9a6650b55935c2e31dvboxsync s->dac_read_index = val;
3ebd5757516d21eccdad25ddd456d2913c2fb215vboxsync s->dac_sub_index = 0;
016096e367cd20c3d3c3fd9a6650b55935c2e31dvboxsync s->dac_state = 3;
fe554d9c0e3a6de4ba221610ac95a44c7d288e01vboxsync break;
b2bc8de1367ae24e1b27b53921d0b32ee3df7acdvboxsync case 0x3c8:
63e3a547845f7c31bb4e892a66684b560dc63611vboxsync s->dac_write_index = val;
7c48fdac0546978ed14617c8096734ce2d18c8e5vboxsync s->dac_sub_index = 0;
016096e367cd20c3d3c3fd9a6650b55935c2e31dvboxsync s->dac_state = 0;
016096e367cd20c3d3c3fd9a6650b55935c2e31dvboxsync break;
3ebd5757516d21eccdad25ddd456d2913c2fb215vboxsync case 0x3c9:
016096e367cd20c3d3c3fd9a6650b55935c2e31dvboxsync s->dac_cache[s->dac_sub_index] = val;
016096e367cd20c3d3c3fd9a6650b55935c2e31dvboxsync if (++s->dac_sub_index == 3) {
016096e367cd20c3d3c3fd9a6650b55935c2e31dvboxsync memcpy(&s->palette[s->dac_write_index * 3], s->dac_cache, 3);
63e3a547845f7c31bb4e892a66684b560dc63611vboxsync s->dac_sub_index = 0;
016096e367cd20c3d3c3fd9a6650b55935c2e31dvboxsync s->dac_write_index++;
016096e367cd20c3d3c3fd9a6650b55935c2e31dvboxsync }
e562a8bb17c0dfa1c316708e085a3a92fcc80521vboxsync break;
e562a8bb17c0dfa1c316708e085a3a92fcc80521vboxsync case 0x3ce:
016096e367cd20c3d3c3fd9a6650b55935c2e31dvboxsync s->gr_index = val & 0x0f;
016096e367cd20c3d3c3fd9a6650b55935c2e31dvboxsync break;
b2bc8de1367ae24e1b27b53921d0b32ee3df7acdvboxsync case 0x3cf:
016096e367cd20c3d3c3fd9a6650b55935c2e31dvboxsync Log2(("vga: write GR%x = 0x%02x\n", s->gr_index, val));
016096e367cd20c3d3c3fd9a6650b55935c2e31dvboxsync s->gr[s->gr_index] = val & gr_mask[s->gr_index];
e562a8bb17c0dfa1c316708e085a3a92fcc80521vboxsync
3ebd5757516d21eccdad25ddd456d2913c2fb215vboxsync#ifndef IN_RC
762a68c2bb3ccde807330e3d1cb05f8b244a5f72vboxsync /* The VGA region is (could be) affected by this change; reset all aliases we've created. */
b2bc8de1367ae24e1b27b53921d0b32ee3df7acdvboxsync if (s->gr_index == 6 /* memory map mode */)
3ebd5757516d21eccdad25ddd456d2913c2fb215vboxsync {
e562a8bb17c0dfa1c316708e085a3a92fcc80521vboxsync if (s->fRemappedVGA)
016096e367cd20c3d3c3fd9a6650b55935c2e31dvboxsync {
016096e367cd20c3d3c3fd9a6650b55935c2e31dvboxsync IOMMMIOResetRegion(PDMDevHlpGetVM(s->CTX_SUFF(pDevIns)), 0x000a0000);
b2bc8de1367ae24e1b27b53921d0b32ee3df7acdvboxsync s->fRemappedVGA = false;
016096e367cd20c3d3c3fd9a6650b55935c2e31dvboxsync }
016096e367cd20c3d3c3fd9a6650b55935c2e31dvboxsync }
e562a8bb17c0dfa1c316708e085a3a92fcc80521vboxsync#endif
3ebd5757516d21eccdad25ddd456d2913c2fb215vboxsync break;
3ebd5757516d21eccdad25ddd456d2913c2fb215vboxsync
3ebd5757516d21eccdad25ddd456d2913c2fb215vboxsync case 0x3b4:
e562a8bb17c0dfa1c316708e085a3a92fcc80521vboxsync case 0x3d4:
ac6445a70a26cb69d08734f1d9dbc171cec86cd8vboxsync s->cr_index = val;
ac6445a70a26cb69d08734f1d9dbc171cec86cd8vboxsync break;
b2bc8de1367ae24e1b27b53921d0b32ee3df7acdvboxsync case 0x3b5:
ac6445a70a26cb69d08734f1d9dbc171cec86cd8vboxsync case 0x3d5:
ac6445a70a26cb69d08734f1d9dbc171cec86cd8vboxsync Log2(("vga: write CR%x = 0x%02x\n", s->cr_index, val));
3ebd5757516d21eccdad25ddd456d2913c2fb215vboxsync /* handle CR0-7 protection */
3ebd5757516d21eccdad25ddd456d2913c2fb215vboxsync if ((s->cr[0x11] & 0x80) && s->cr_index <= 7) {
3ebd5757516d21eccdad25ddd456d2913c2fb215vboxsync /* can always write bit 4 of CR7 */
3ebd5757516d21eccdad25ddd456d2913c2fb215vboxsync if (s->cr_index == 7)
3ebd5757516d21eccdad25ddd456d2913c2fb215vboxsync s->cr[7] = (s->cr[7] & ~0x10) | (val & 0x10);
3ebd5757516d21eccdad25ddd456d2913c2fb215vboxsync return;
3ebd5757516d21eccdad25ddd456d2913c2fb215vboxsync }
016096e367cd20c3d3c3fd9a6650b55935c2e31dvboxsync s->cr[s->cr_index] = val;
016096e367cd20c3d3c3fd9a6650b55935c2e31dvboxsync
b2bc8de1367ae24e1b27b53921d0b32ee3df7acdvboxsync if (s->fRealRetrace) {
016096e367cd20c3d3c3fd9a6650b55935c2e31dvboxsync /* The following registers are only updated during a mode set. */
016096e367cd20c3d3c3fd9a6650b55935c2e31dvboxsync switch(s->cr_index) {
e562a8bb17c0dfa1c316708e085a3a92fcc80521vboxsync case 0x00:
3ebd5757516d21eccdad25ddd456d2913c2fb215vboxsync case 0x02:
3ebd5757516d21eccdad25ddd456d2913c2fb215vboxsync case 0x03:
e562a8bb17c0dfa1c316708e085a3a92fcc80521vboxsync case 0x05:
016096e367cd20c3d3c3fd9a6650b55935c2e31dvboxsync case 0x06:
016096e367cd20c3d3c3fd9a6650b55935c2e31dvboxsync case 0x07:
b2bc8de1367ae24e1b27b53921d0b32ee3df7acdvboxsync case 0x09:
016096e367cd20c3d3c3fd9a6650b55935c2e31dvboxsync case 0x10:
016096e367cd20c3d3c3fd9a6650b55935c2e31dvboxsync case 0x11:
e562a8bb17c0dfa1c316708e085a3a92fcc80521vboxsync case 0x15:
3ebd5757516d21eccdad25ddd456d2913c2fb215vboxsync case 0x16:
3ebd5757516d21eccdad25ddd456d2913c2fb215vboxsync vga_update_retrace_state(s);
e562a8bb17c0dfa1c316708e085a3a92fcc80521vboxsync break;
016096e367cd20c3d3c3fd9a6650b55935c2e31dvboxsync }
016096e367cd20c3d3c3fd9a6650b55935c2e31dvboxsync }
b2bc8de1367ae24e1b27b53921d0b32ee3df7acdvboxsync break;
016096e367cd20c3d3c3fd9a6650b55935c2e31dvboxsync case 0x3ba:
e562a8bb17c0dfa1c316708e085a3a92fcc80521vboxsync case 0x3da:
e562a8bb17c0dfa1c316708e085a3a92fcc80521vboxsync s->fcr = val & 0x10;
e562a8bb17c0dfa1c316708e085a3a92fcc80521vboxsync break;
e562a8bb17c0dfa1c316708e085a3a92fcc80521vboxsync }
016096e367cd20c3d3c3fd9a6650b55935c2e31dvboxsync}
e562a8bb17c0dfa1c316708e085a3a92fcc80521vboxsync
016096e367cd20c3d3c3fd9a6650b55935c2e31dvboxsync#ifdef CONFIG_BOCHS_VBE
016096e367cd20c3d3c3fd9a6650b55935c2e31dvboxsyncstatic uint32_t vbe_ioport_read_index(void *opaque, uint32_t addr)
016096e367cd20c3d3c3fd9a6650b55935c2e31dvboxsync{
016096e367cd20c3d3c3fd9a6650b55935c2e31dvboxsync VGAState *s = (VGAState*)opaque;
016096e367cd20c3d3c3fd9a6650b55935c2e31dvboxsync uint32_t val;
3ebd5757516d21eccdad25ddd456d2913c2fb215vboxsync val = s->vbe_index;
3ebd5757516d21eccdad25ddd456d2913c2fb215vboxsync return val;
3ebd5757516d21eccdad25ddd456d2913c2fb215vboxsync}
3ebd5757516d21eccdad25ddd456d2913c2fb215vboxsync
3ebd5757516d21eccdad25ddd456d2913c2fb215vboxsyncstatic uint32_t vbe_ioport_read_data(void *opaque, uint32_t addr)
3ebd5757516d21eccdad25ddd456d2913c2fb215vboxsync{
3ebd5757516d21eccdad25ddd456d2913c2fb215vboxsync VGAState *s = (VGAState*)opaque;
3ebd5757516d21eccdad25ddd456d2913c2fb215vboxsync uint32_t val;
016096e367cd20c3d3c3fd9a6650b55935c2e31dvboxsync
3ebd5757516d21eccdad25ddd456d2913c2fb215vboxsync if (s->vbe_index < VBE_DISPI_INDEX_NB) {
3ebd5757516d21eccdad25ddd456d2913c2fb215vboxsync if (s->vbe_regs[VBE_DISPI_INDEX_ENABLE] & VBE_DISPI_GETCAPS) {
f7f5cd7b1e530eb5636da51c974b48ae0c1775b3vboxsync switch(s->vbe_index) {
016096e367cd20c3d3c3fd9a6650b55935c2e31dvboxsync /* XXX: do not hardcode ? */
3ebd5757516d21eccdad25ddd456d2913c2fb215vboxsync case VBE_DISPI_INDEX_XRES:
016096e367cd20c3d3c3fd9a6650b55935c2e31dvboxsync val = VBE_DISPI_MAX_XRES;
3ebd5757516d21eccdad25ddd456d2913c2fb215vboxsync break;
016096e367cd20c3d3c3fd9a6650b55935c2e31dvboxsync case VBE_DISPI_INDEX_YRES:
016096e367cd20c3d3c3fd9a6650b55935c2e31dvboxsync val = VBE_DISPI_MAX_YRES;
016096e367cd20c3d3c3fd9a6650b55935c2e31dvboxsync break;
3ebd5757516d21eccdad25ddd456d2913c2fb215vboxsync case VBE_DISPI_INDEX_BPP:
e562a8bb17c0dfa1c316708e085a3a92fcc80521vboxsync val = VBE_DISPI_MAX_BPP;
e562a8bb17c0dfa1c316708e085a3a92fcc80521vboxsync break;
3ebd5757516d21eccdad25ddd456d2913c2fb215vboxsync default:
3ebd5757516d21eccdad25ddd456d2913c2fb215vboxsync Assert(s->vbe_index < VBE_DISPI_INDEX_NB_SAVED);
3ebd5757516d21eccdad25ddd456d2913c2fb215vboxsync val = s->vbe_regs[s->vbe_index];
3ebd5757516d21eccdad25ddd456d2913c2fb215vboxsync break;
e562a8bb17c0dfa1c316708e085a3a92fcc80521vboxsync }
e562a8bb17c0dfa1c316708e085a3a92fcc80521vboxsync } else {
016096e367cd20c3d3c3fd9a6650b55935c2e31dvboxsync switch(s->vbe_index) {
3ebd5757516d21eccdad25ddd456d2913c2fb215vboxsync case VBE_DISPI_INDEX_VBOX_VIDEO:
016096e367cd20c3d3c3fd9a6650b55935c2e31dvboxsync /* Reading from the port means that the old additions are requesting the number of monitors. */
3ebd5757516d21eccdad25ddd456d2913c2fb215vboxsync val = 1;
ac6445a70a26cb69d08734f1d9dbc171cec86cd8vboxsync break;
ac6445a70a26cb69d08734f1d9dbc171cec86cd8vboxsync default:
3ebd5757516d21eccdad25ddd456d2913c2fb215vboxsync Assert(s->vbe_index < VBE_DISPI_INDEX_NB_SAVED);
3ebd5757516d21eccdad25ddd456d2913c2fb215vboxsync val = s->vbe_regs[s->vbe_index];
7697e43970d863558b6c168a55e8948ccb18d8d1vboxsync break;
3ebd5757516d21eccdad25ddd456d2913c2fb215vboxsync }
3ebd5757516d21eccdad25ddd456d2913c2fb215vboxsync }
3ebd5757516d21eccdad25ddd456d2913c2fb215vboxsync } else {
ac6445a70a26cb69d08734f1d9dbc171cec86cd8vboxsync val = 0;
3ebd5757516d21eccdad25ddd456d2913c2fb215vboxsync }
3ebd5757516d21eccdad25ddd456d2913c2fb215vboxsync Log(("VBE: read index=0x%x val=0x%x\n", s->vbe_index, val));
3ebd5757516d21eccdad25ddd456d2913c2fb215vboxsync return val;
3ebd5757516d21eccdad25ddd456d2913c2fb215vboxsync}
ac6445a70a26cb69d08734f1d9dbc171cec86cd8vboxsync
3ebd5757516d21eccdad25ddd456d2913c2fb215vboxsync#define VBE_PITCH_ALIGN 4 /* Align pitch to 32 bits - Qt requires that. */
3ebd5757516d21eccdad25ddd456d2913c2fb215vboxsync
e562a8bb17c0dfa1c316708e085a3a92fcc80521vboxsync/* Calculate scanline pitch based on bit depth and width in pixels. */
e562a8bb17c0dfa1c316708e085a3a92fcc80521vboxsyncstatic uint32_t calc_line_pitch(uint16_t bpp, uint16_t width)
e562a8bb17c0dfa1c316708e085a3a92fcc80521vboxsync{
e562a8bb17c0dfa1c316708e085a3a92fcc80521vboxsync uint32_t pitch, aligned_pitch;
e562a8bb17c0dfa1c316708e085a3a92fcc80521vboxsync
e562a8bb17c0dfa1c316708e085a3a92fcc80521vboxsync if (bpp <= 4)
ac6445a70a26cb69d08734f1d9dbc171cec86cd8vboxsync pitch = width >> 1;
ac6445a70a26cb69d08734f1d9dbc171cec86cd8vboxsync else
ac6445a70a26cb69d08734f1d9dbc171cec86cd8vboxsync pitch = width * ((bpp + 7) >> 3);
e562a8bb17c0dfa1c316708e085a3a92fcc80521vboxsync
e562a8bb17c0dfa1c316708e085a3a92fcc80521vboxsync /* Align the pitch to some sensible value. */
e562a8bb17c0dfa1c316708e085a3a92fcc80521vboxsync aligned_pitch = (pitch + (VBE_PITCH_ALIGN - 1)) & ~(VBE_PITCH_ALIGN - 1);
e562a8bb17c0dfa1c316708e085a3a92fcc80521vboxsync if (aligned_pitch != pitch)
e562a8bb17c0dfa1c316708e085a3a92fcc80521vboxsync Log(("VBE: Line pitch %d aligned to %d bytes\n", pitch, aligned_pitch));
e562a8bb17c0dfa1c316708e085a3a92fcc80521vboxsync
e562a8bb17c0dfa1c316708e085a3a92fcc80521vboxsync return aligned_pitch;
e562a8bb17c0dfa1c316708e085a3a92fcc80521vboxsync}
e562a8bb17c0dfa1c316708e085a3a92fcc80521vboxsync
e562a8bb17c0dfa1c316708e085a3a92fcc80521vboxsync/* Calculate line width in pixels based on bit depth and pitch. */
e562a8bb17c0dfa1c316708e085a3a92fcc80521vboxsyncstatic uint32_t calc_line_width(uint16_t bpp, uint32_t pitch)
e562a8bb17c0dfa1c316708e085a3a92fcc80521vboxsync{
e562a8bb17c0dfa1c316708e085a3a92fcc80521vboxsync uint32_t width;
e562a8bb17c0dfa1c316708e085a3a92fcc80521vboxsync
e562a8bb17c0dfa1c316708e085a3a92fcc80521vboxsync if (bpp <= 4)
e562a8bb17c0dfa1c316708e085a3a92fcc80521vboxsync width = pitch << 1;
e562a8bb17c0dfa1c316708e085a3a92fcc80521vboxsync else
ac6445a70a26cb69d08734f1d9dbc171cec86cd8vboxsync width = pitch / ((bpp + 7) >> 3);
ac6445a70a26cb69d08734f1d9dbc171cec86cd8vboxsync
ac6445a70a26cb69d08734f1d9dbc171cec86cd8vboxsync return width;
016096e367cd20c3d3c3fd9a6650b55935c2e31dvboxsync}
3ebd5757516d21eccdad25ddd456d2913c2fb215vboxsync
3ebd5757516d21eccdad25ddd456d2913c2fb215vboxsyncstatic void vbe_ioport_write_index(void *opaque, uint32_t addr, uint32_t val)
3ebd5757516d21eccdad25ddd456d2913c2fb215vboxsync{
3ebd5757516d21eccdad25ddd456d2913c2fb215vboxsync VGAState *s = (VGAState*)opaque;
3ebd5757516d21eccdad25ddd456d2913c2fb215vboxsync s->vbe_index = val;
3ebd5757516d21eccdad25ddd456d2913c2fb215vboxsync}
016096e367cd20c3d3c3fd9a6650b55935c2e31dvboxsync
016096e367cd20c3d3c3fd9a6650b55935c2e31dvboxsyncstatic int vbe_ioport_write_data(void *opaque, uint32_t addr, uint32_t val)
009d969fa3276b108ddb99a9c1a7a26c003438a7vboxsync{
016096e367cd20c3d3c3fd9a6650b55935c2e31dvboxsync VGAState *s = (VGAState*)opaque;
016096e367cd20c3d3c3fd9a6650b55935c2e31dvboxsync uint32_t max_bank;
016096e367cd20c3d3c3fd9a6650b55935c2e31dvboxsync
016096e367cd20c3d3c3fd9a6650b55935c2e31dvboxsync if (s->vbe_index <= VBE_DISPI_INDEX_NB) {
016096e367cd20c3d3c3fd9a6650b55935c2e31dvboxsync bool fRecalculate = false;
3ebd5757516d21eccdad25ddd456d2913c2fb215vboxsync Log(("VBE: write index=0x%x val=0x%x\n", s->vbe_index, val));
3ebd5757516d21eccdad25ddd456d2913c2fb215vboxsync switch(s->vbe_index) {
3ebd5757516d21eccdad25ddd456d2913c2fb215vboxsync case VBE_DISPI_INDEX_ID:
3ebd5757516d21eccdad25ddd456d2913c2fb215vboxsync if (val == VBE_DISPI_ID0 ||
6826c1a65f586b47c2abbbabab801950c9a0bb75vboxsync val == VBE_DISPI_ID1 ||
016096e367cd20c3d3c3fd9a6650b55935c2e31dvboxsync val == VBE_DISPI_ID2 ||
b2bc8de1367ae24e1b27b53921d0b32ee3df7acdvboxsync val == VBE_DISPI_ID3 ||
3ebd5757516d21eccdad25ddd456d2913c2fb215vboxsync val == VBE_DISPI_ID4) {
3ebd5757516d21eccdad25ddd456d2913c2fb215vboxsync s->vbe_regs[s->vbe_index] = val;
3ebd5757516d21eccdad25ddd456d2913c2fb215vboxsync }
f7f5cd7b1e530eb5636da51c974b48ae0c1775b3vboxsync if (val == VBE_DISPI_ID_VBOX_VIDEO) {
016096e367cd20c3d3c3fd9a6650b55935c2e31dvboxsync s->vbe_regs[s->vbe_index] = val;
3ebd5757516d21eccdad25ddd456d2913c2fb215vboxsync } else if (val == VBE_DISPI_ID_ANYX) {
016096e367cd20c3d3c3fd9a6650b55935c2e31dvboxsync s->vbe_regs[s->vbe_index] = val;
016096e367cd20c3d3c3fd9a6650b55935c2e31dvboxsync }
016096e367cd20c3d3c3fd9a6650b55935c2e31dvboxsync#ifdef VBOX_WITH_HGSMI
3ebd5757516d21eccdad25ddd456d2913c2fb215vboxsync else if (val == VBE_DISPI_ID_HGSMI) {
762a68c2bb3ccde807330e3d1cb05f8b244a5f72vboxsync s->vbe_regs[s->vbe_index] = val;
3ebd5757516d21eccdad25ddd456d2913c2fb215vboxsync }
3ebd5757516d21eccdad25ddd456d2913c2fb215vboxsync#endif /* VBOX_WITH_HGSMI */
3ebd5757516d21eccdad25ddd456d2913c2fb215vboxsync break;
016096e367cd20c3d3c3fd9a6650b55935c2e31dvboxsync case VBE_DISPI_INDEX_XRES:
3ebd5757516d21eccdad25ddd456d2913c2fb215vboxsync if (val <= VBE_DISPI_MAX_XRES)
016096e367cd20c3d3c3fd9a6650b55935c2e31dvboxsync {
b2bc8de1367ae24e1b27b53921d0b32ee3df7acdvboxsync s->vbe_regs[s->vbe_index] = val;
ac6445a70a26cb69d08734f1d9dbc171cec86cd8vboxsync s->vbe_regs[VBE_DISPI_INDEX_VIRT_WIDTH] = val;
ac6445a70a26cb69d08734f1d9dbc171cec86cd8vboxsync fRecalculate = true;
3ebd5757516d21eccdad25ddd456d2913c2fb215vboxsync }
3ebd5757516d21eccdad25ddd456d2913c2fb215vboxsync break;
ac6445a70a26cb69d08734f1d9dbc171cec86cd8vboxsync case VBE_DISPI_INDEX_YRES:
3ebd5757516d21eccdad25ddd456d2913c2fb215vboxsync if (val <= VBE_DISPI_MAX_YRES)
ac6445a70a26cb69d08734f1d9dbc171cec86cd8vboxsync s->vbe_regs[s->vbe_index] = val;
ac6445a70a26cb69d08734f1d9dbc171cec86cd8vboxsync break;
b2bc8de1367ae24e1b27b53921d0b32ee3df7acdvboxsync case VBE_DISPI_INDEX_BPP:
3ebd5757516d21eccdad25ddd456d2913c2fb215vboxsync if (val == 0)
e562a8bb17c0dfa1c316708e085a3a92fcc80521vboxsync val = 8;
3ebd5757516d21eccdad25ddd456d2913c2fb215vboxsync if (val == 4 || val == 8 || val == 15 ||
3ebd5757516d21eccdad25ddd456d2913c2fb215vboxsync val == 16 || val == 24 || val == 32) {
3ebd5757516d21eccdad25ddd456d2913c2fb215vboxsync s->vbe_regs[s->vbe_index] = val;
ac6445a70a26cb69d08734f1d9dbc171cec86cd8vboxsync fRecalculate = true;
3ebd5757516d21eccdad25ddd456d2913c2fb215vboxsync }
3ebd5757516d21eccdad25ddd456d2913c2fb215vboxsync break;
3ebd5757516d21eccdad25ddd456d2913c2fb215vboxsync case VBE_DISPI_INDEX_BANK:
3ebd5757516d21eccdad25ddd456d2913c2fb215vboxsync if (s->vbe_regs[VBE_DISPI_INDEX_BPP] <= 4)
b2bc8de1367ae24e1b27b53921d0b32ee3df7acdvboxsync max_bank = s->vbe_bank_max >> 2; /* Each bank really covers 256K */
3ebd5757516d21eccdad25ddd456d2913c2fb215vboxsync else
ac6445a70a26cb69d08734f1d9dbc171cec86cd8vboxsync max_bank = s->vbe_bank_max;
ac6445a70a26cb69d08734f1d9dbc171cec86cd8vboxsync /* Old software may pass garbage in the high byte of bank. If the maximum
ac6445a70a26cb69d08734f1d9dbc171cec86cd8vboxsync * bank fits into a single byte, toss the high byte the user supplied.
3ebd5757516d21eccdad25ddd456d2913c2fb215vboxsync */
b2bc8de1367ae24e1b27b53921d0b32ee3df7acdvboxsync if (max_bank < 0x100)
3ebd5757516d21eccdad25ddd456d2913c2fb215vboxsync val &= 0xff;
ac6445a70a26cb69d08734f1d9dbc171cec86cd8vboxsync if (val > max_bank)
e562a8bb17c0dfa1c316708e085a3a92fcc80521vboxsync val = max_bank;
ac6445a70a26cb69d08734f1d9dbc171cec86cd8vboxsync s->vbe_regs[s->vbe_index] = val;
ac6445a70a26cb69d08734f1d9dbc171cec86cd8vboxsync s->bank_offset = (val << 16);
016096e367cd20c3d3c3fd9a6650b55935c2e31dvboxsync
3ebd5757516d21eccdad25ddd456d2913c2fb215vboxsync#ifndef IN_RC
3ebd5757516d21eccdad25ddd456d2913c2fb215vboxsync /* The VGA region is (could be) affected by this change; reset all aliases we've created. */
3ebd5757516d21eccdad25ddd456d2913c2fb215vboxsync if (s->fRemappedVGA)
3ebd5757516d21eccdad25ddd456d2913c2fb215vboxsync {
3ebd5757516d21eccdad25ddd456d2913c2fb215vboxsync IOMMMIOResetRegion(PDMDevHlpGetVM(s->CTX_SUFF(pDevIns)), 0x000a0000);
3ebd5757516d21eccdad25ddd456d2913c2fb215vboxsync s->fRemappedVGA = false;
016096e367cd20c3d3c3fd9a6650b55935c2e31dvboxsync }
016096e367cd20c3d3c3fd9a6650b55935c2e31dvboxsync#endif
016096e367cd20c3d3c3fd9a6650b55935c2e31dvboxsync break;
b2bc8de1367ae24e1b27b53921d0b32ee3df7acdvboxsync
016096e367cd20c3d3c3fd9a6650b55935c2e31dvboxsync case VBE_DISPI_INDEX_ENABLE:
016096e367cd20c3d3c3fd9a6650b55935c2e31dvboxsync#ifndef IN_RING3
016096e367cd20c3d3c3fd9a6650b55935c2e31dvboxsync return VINF_IOM_HC_IOPORT_WRITE;
016096e367cd20c3d3c3fd9a6650b55935c2e31dvboxsync#else
016096e367cd20c3d3c3fd9a6650b55935c2e31dvboxsync if ((val & VBE_DISPI_ENABLED) &&
016096e367cd20c3d3c3fd9a6650b55935c2e31dvboxsync !(s->vbe_regs[VBE_DISPI_INDEX_ENABLE] & VBE_DISPI_ENABLED)) {
016096e367cd20c3d3c3fd9a6650b55935c2e31dvboxsync int h, shift_control;
3ebd5757516d21eccdad25ddd456d2913c2fb215vboxsync /* Check the values before we screw up with a resolution which is too big or small. */
016096e367cd20c3d3c3fd9a6650b55935c2e31dvboxsync size_t cb = s->vbe_regs[VBE_DISPI_INDEX_XRES];
016096e367cd20c3d3c3fd9a6650b55935c2e31dvboxsync if (s->vbe_regs[VBE_DISPI_INDEX_BPP] == 4)
3ebd5757516d21eccdad25ddd456d2913c2fb215vboxsync cb = s->vbe_regs[VBE_DISPI_INDEX_XRES] >> 1;
016096e367cd20c3d3c3fd9a6650b55935c2e31dvboxsync else
3ebd5757516d21eccdad25ddd456d2913c2fb215vboxsync cb = s->vbe_regs[VBE_DISPI_INDEX_XRES] * ((s->vbe_regs[VBE_DISPI_INDEX_BPP] + 7) >> 3);
3ebd5757516d21eccdad25ddd456d2913c2fb215vboxsync cb *= s->vbe_regs[VBE_DISPI_INDEX_YRES];
016096e367cd20c3d3c3fd9a6650b55935c2e31dvboxsync uint16_t cVirtWidth = s->vbe_regs[VBE_DISPI_INDEX_VIRT_WIDTH];
3ebd5757516d21eccdad25ddd456d2913c2fb215vboxsync if (!cVirtWidth)
016096e367cd20c3d3c3fd9a6650b55935c2e31dvboxsync cVirtWidth = s->vbe_regs[VBE_DISPI_INDEX_XRES];
3ebd5757516d21eccdad25ddd456d2913c2fb215vboxsync if ( !cVirtWidth
3ebd5757516d21eccdad25ddd456d2913c2fb215vboxsync || !s->vbe_regs[VBE_DISPI_INDEX_YRES]
3ebd5757516d21eccdad25ddd456d2913c2fb215vboxsync || cb > s->vram_size)
3ebd5757516d21eccdad25ddd456d2913c2fb215vboxsync {
3ebd5757516d21eccdad25ddd456d2913c2fb215vboxsync AssertMsgFailed(("VIRT WIDTH=%d YRES=%d cb=%d vram_size=%d\n",
3ebd5757516d21eccdad25ddd456d2913c2fb215vboxsync s->vbe_regs[VBE_DISPI_INDEX_VIRT_WIDTH], s->vbe_regs[VBE_DISPI_INDEX_YRES], cb, s->vram_size));
3ebd5757516d21eccdad25ddd456d2913c2fb215vboxsync return VINF_SUCCESS; /* Note: silent failure like before */
3ebd5757516d21eccdad25ddd456d2913c2fb215vboxsync }
3ebd5757516d21eccdad25ddd456d2913c2fb215vboxsync
3ebd5757516d21eccdad25ddd456d2913c2fb215vboxsync /* When VBE interface is enabled, it is reset. */
3ebd5757516d21eccdad25ddd456d2913c2fb215vboxsync s->vbe_regs[VBE_DISPI_INDEX_X_OFFSET] = 0;
016096e367cd20c3d3c3fd9a6650b55935c2e31dvboxsync s->vbe_regs[VBE_DISPI_INDEX_Y_OFFSET] = 0;
3ebd5757516d21eccdad25ddd456d2913c2fb215vboxsync fRecalculate = true;
016096e367cd20c3d3c3fd9a6650b55935c2e31dvboxsync
016096e367cd20c3d3c3fd9a6650b55935c2e31dvboxsync /* clear the screen (should be done in BIOS) */
3ebd5757516d21eccdad25ddd456d2913c2fb215vboxsync if (!(val & VBE_DISPI_NOCLEARMEM)) {
016096e367cd20c3d3c3fd9a6650b55935c2e31dvboxsync uint16_t cY = RT_MIN(s->vbe_regs[VBE_DISPI_INDEX_YRES],
3ebd5757516d21eccdad25ddd456d2913c2fb215vboxsync s->vbe_regs[VBE_DISPI_INDEX_VIRT_HEIGHT]);
016096e367cd20c3d3c3fd9a6650b55935c2e31dvboxsync uint16_t cbLinePitch = s->vbe_line_offset;
3ebd5757516d21eccdad25ddd456d2913c2fb215vboxsync memset(s->CTX_SUFF(vram_ptr), 0,
3ebd5757516d21eccdad25ddd456d2913c2fb215vboxsync cY * cbLinePitch);
016096e367cd20c3d3c3fd9a6650b55935c2e31dvboxsync }
3ebd5757516d21eccdad25ddd456d2913c2fb215vboxsync
23603ed361f22874964e3a841add2c58ec2bb1eavboxsync /* we initialize the VGA graphic mode (should be done
23603ed361f22874964e3a841add2c58ec2bb1eavboxsync in BIOS) */
b2bc8de1367ae24e1b27b53921d0b32ee3df7acdvboxsync s->gr[0x06] = (s->gr[0x06] & ~0x0c) | 0x05; /* graphic mode + memory map 1 */
7697e43970d863558b6c168a55e8948ccb18d8d1vboxsync s->cr[0x17] |= 3; /* no CGA modes */
23603ed361f22874964e3a841add2c58ec2bb1eavboxsync s->cr[0x13] = s->vbe_line_offset >> 3;
3ebd5757516d21eccdad25ddd456d2913c2fb215vboxsync /* width */
23603ed361f22874964e3a841add2c58ec2bb1eavboxsync s->cr[0x01] = (cVirtWidth >> 3) - 1;
9b7ab382b3f9667e8847020e1e58f7143c4d2334vboxsync /* height (only meaningful if < 1024) */
3ebd5757516d21eccdad25ddd456d2913c2fb215vboxsync h = s->vbe_regs[VBE_DISPI_INDEX_YRES] - 1;
b2bc8de1367ae24e1b27b53921d0b32ee3df7acdvboxsync s->cr[0x12] = h;
b2bc8de1367ae24e1b27b53921d0b32ee3df7acdvboxsync s->cr[0x07] = (s->cr[0x07] & ~0x42) |
9b7ab382b3f9667e8847020e1e58f7143c4d2334vboxsync ((h >> 7) & 0x02) | ((h >> 3) & 0x40);
3ebd5757516d21eccdad25ddd456d2913c2fb215vboxsync /* line compare to 1023 */
3ebd5757516d21eccdad25ddd456d2913c2fb215vboxsync s->cr[0x18] = 0xff;
b2bc8de1367ae24e1b27b53921d0b32ee3df7acdvboxsync s->cr[0x07] |= 0x10;
23603ed361f22874964e3a841add2c58ec2bb1eavboxsync s->cr[0x09] |= 0x40;
af5224eb6b6676bc892a3f5abeb21f602547d31cvboxsync
fe554d9c0e3a6de4ba221610ac95a44c7d288e01vboxsync if (s->vbe_regs[VBE_DISPI_INDEX_BPP] == 4) {
3ebd5757516d21eccdad25ddd456d2913c2fb215vboxsync shift_control = 0;
23603ed361f22874964e3a841add2c58ec2bb1eavboxsync s->sr[0x01] &= ~8; /* no double line */
23603ed361f22874964e3a841add2c58ec2bb1eavboxsync } else {
23603ed361f22874964e3a841add2c58ec2bb1eavboxsync shift_control = 2;
016096e367cd20c3d3c3fd9a6650b55935c2e31dvboxsync s->sr[4] |= 0x08; /* set chain 4 mode */
016096e367cd20c3d3c3fd9a6650b55935c2e31dvboxsync s->sr[2] |= 0x0f; /* activate all planes */
016096e367cd20c3d3c3fd9a6650b55935c2e31dvboxsync }
016096e367cd20c3d3c3fd9a6650b55935c2e31dvboxsync s->gr[0x05] = (s->gr[0x05] & ~0x60) | (shift_control << 5);
016096e367cd20c3d3c3fd9a6650b55935c2e31dvboxsync s->cr[0x09] &= ~0x9f; /* no double scan */
016096e367cd20c3d3c3fd9a6650b55935c2e31dvboxsync /* sunlover 30.05.2007
016096e367cd20c3d3c3fd9a6650b55935c2e31dvboxsync * The ar_index remains with bit 0x20 cleared after a switch from fullscreen
3ebd5757516d21eccdad25ddd456d2913c2fb215vboxsync * DOS mode on Windows XP guest. That leads to GMODE_BLANK in vga_update_display.
016096e367cd20c3d3c3fd9a6650b55935c2e31dvboxsync * But the VBE mode is graphics, so not a blank anymore.
016096e367cd20c3d3c3fd9a6650b55935c2e31dvboxsync */
b2bc8de1367ae24e1b27b53921d0b32ee3df7acdvboxsync s->ar_index |= 0x20;
b2bc8de1367ae24e1b27b53921d0b32ee3df7acdvboxsync } else {
b2bc8de1367ae24e1b27b53921d0b32ee3df7acdvboxsync /* XXX: the bios should do that */
016096e367cd20c3d3c3fd9a6650b55935c2e31dvboxsync /* sunlover 21.12.2006
016096e367cd20c3d3c3fd9a6650b55935c2e31dvboxsync * Here is probably more to reset. When this was executed in GC
b2bc8de1367ae24e1b27b53921d0b32ee3df7acdvboxsync * then the *update* functions could not detect a mode change.
016096e367cd20c3d3c3fd9a6650b55935c2e31dvboxsync * Or may be these update function should take the s->vbe_regs[s->vbe_index]
b2bc8de1367ae24e1b27b53921d0b32ee3df7acdvboxsync * into account when detecting a mode change.
016096e367cd20c3d3c3fd9a6650b55935c2e31dvboxsync *
b2bc8de1367ae24e1b27b53921d0b32ee3df7acdvboxsync * The 'mode reset not detected' problem is now fixed by executing the
016096e367cd20c3d3c3fd9a6650b55935c2e31dvboxsync * VBE_DISPI_INDEX_ENABLE case always in RING3 in order to call the
b2bc8de1367ae24e1b27b53921d0b32ee3df7acdvboxsync * LFBChange callback.
b2bc8de1367ae24e1b27b53921d0b32ee3df7acdvboxsync */
016096e367cd20c3d3c3fd9a6650b55935c2e31dvboxsync s->bank_offset = 0;
b2bc8de1367ae24e1b27b53921d0b32ee3df7acdvboxsync }
b2bc8de1367ae24e1b27b53921d0b32ee3df7acdvboxsync s->vbe_regs[s->vbe_index] = val;
016096e367cd20c3d3c3fd9a6650b55935c2e31dvboxsync /*
b2bc8de1367ae24e1b27b53921d0b32ee3df7acdvboxsync * LFB video mode is either disabled or changed. This notification
016096e367cd20c3d3c3fd9a6650b55935c2e31dvboxsync * is used by the display to disable VBVA.
b2bc8de1367ae24e1b27b53921d0b32ee3df7acdvboxsync */
b2bc8de1367ae24e1b27b53921d0b32ee3df7acdvboxsync s->pDrv->pfnLFBModeChange(s->pDrv, (val & VBE_DISPI_ENABLED) != 0);
b2bc8de1367ae24e1b27b53921d0b32ee3df7acdvboxsync
016096e367cd20c3d3c3fd9a6650b55935c2e31dvboxsync /* The VGA region is (could be) affected by this change; reset all aliases we've created. */
016096e367cd20c3d3c3fd9a6650b55935c2e31dvboxsync if (s->fRemappedVGA)
016096e367cd20c3d3c3fd9a6650b55935c2e31dvboxsync {
016096e367cd20c3d3c3fd9a6650b55935c2e31dvboxsync IOMMMIOResetRegion(PDMDevHlpGetVM(s->CTX_SUFF(pDevIns)), 0x000a0000);
3ebd5757516d21eccdad25ddd456d2913c2fb215vboxsync s->fRemappedVGA = false;
016096e367cd20c3d3c3fd9a6650b55935c2e31dvboxsync }
016096e367cd20c3d3c3fd9a6650b55935c2e31dvboxsync break;
3ebd5757516d21eccdad25ddd456d2913c2fb215vboxsync#endif /* IN_RING3 */
3ebd5757516d21eccdad25ddd456d2913c2fb215vboxsync case VBE_DISPI_INDEX_VIRT_WIDTH:
3ebd5757516d21eccdad25ddd456d2913c2fb215vboxsync case VBE_DISPI_INDEX_X_OFFSET:
3ebd5757516d21eccdad25ddd456d2913c2fb215vboxsync case VBE_DISPI_INDEX_Y_OFFSET:
016096e367cd20c3d3c3fd9a6650b55935c2e31dvboxsync {
3ebd5757516d21eccdad25ddd456d2913c2fb215vboxsync s->vbe_regs[s->vbe_index] = val;
3ebd5757516d21eccdad25ddd456d2913c2fb215vboxsync fRecalculate = true;
3ebd5757516d21eccdad25ddd456d2913c2fb215vboxsync }
3ebd5757516d21eccdad25ddd456d2913c2fb215vboxsync break;
3ebd5757516d21eccdad25ddd456d2913c2fb215vboxsync case VBE_DISPI_INDEX_VBOX_VIDEO:
f7f5cd7b1e530eb5636da51c974b48ae0c1775b3vboxsync#ifndef IN_RING3
3ebd5757516d21eccdad25ddd456d2913c2fb215vboxsync return VINF_IOM_HC_IOPORT_WRITE;
3ebd5757516d21eccdad25ddd456d2913c2fb215vboxsync#else
3ebd5757516d21eccdad25ddd456d2913c2fb215vboxsync /* Changes in the VGA device are minimal. The device is bypassed. The driver does all work. */
d5a801e98910a0c3c9c65cfc7cf8826f0b0b4450vboxsync if (val == VBOX_VIDEO_DISABLE_ADAPTER_MEMORY)
d5a801e98910a0c3c9c65cfc7cf8826f0b0b4450vboxsync {
d5a801e98910a0c3c9c65cfc7cf8826f0b0b4450vboxsync s->pDrv->pfnProcessAdapterData(s->pDrv, NULL, 0);
d5a801e98910a0c3c9c65cfc7cf8826f0b0b4450vboxsync }
63787aca0a2a16ec959a5294148726ccf898ddf1vboxsync else if (val == VBOX_VIDEO_INTERPRET_ADAPTER_MEMORY)
d5a801e98910a0c3c9c65cfc7cf8826f0b0b4450vboxsync {
63787aca0a2a16ec959a5294148726ccf898ddf1vboxsync s->pDrv->pfnProcessAdapterData(s->pDrv, s->CTX_SUFF(vram_ptr), s->vram_size);
3ebd5757516d21eccdad25ddd456d2913c2fb215vboxsync }
3ebd5757516d21eccdad25ddd456d2913c2fb215vboxsync else if ((val & 0xFFFF0000) == VBOX_VIDEO_INTERPRET_DISPLAY_MEMORY_BASE)
63e3a547845f7c31bb4e892a66684b560dc63611vboxsync {
3ebd5757516d21eccdad25ddd456d2913c2fb215vboxsync s->pDrv->pfnProcessDisplayData(s->pDrv, s->CTX_SUFF(vram_ptr), val & 0xFFFF);
3ebd5757516d21eccdad25ddd456d2913c2fb215vboxsync }
3ebd5757516d21eccdad25ddd456d2913c2fb215vboxsync#endif /* IN_RING3 */
3ebd5757516d21eccdad25ddd456d2913c2fb215vboxsync break;
3ebd5757516d21eccdad25ddd456d2913c2fb215vboxsync default:
3ebd5757516d21eccdad25ddd456d2913c2fb215vboxsync break;
3ebd5757516d21eccdad25ddd456d2913c2fb215vboxsync }
3ebd5757516d21eccdad25ddd456d2913c2fb215vboxsync if (fRecalculate)
3ebd5757516d21eccdad25ddd456d2913c2fb215vboxsync {
3ebd5757516d21eccdad25ddd456d2913c2fb215vboxsync uint16_t cBPP = s->vbe_regs[VBE_DISPI_INDEX_BPP];
3ebd5757516d21eccdad25ddd456d2913c2fb215vboxsync uint16_t cVirtWidth = s->vbe_regs[VBE_DISPI_INDEX_VIRT_WIDTH];
3ebd5757516d21eccdad25ddd456d2913c2fb215vboxsync uint16_t cX = s->vbe_regs[VBE_DISPI_INDEX_XRES];
3ebd5757516d21eccdad25ddd456d2913c2fb215vboxsync uint16_t offX = s->vbe_regs[VBE_DISPI_INDEX_X_OFFSET];
3ebd5757516d21eccdad25ddd456d2913c2fb215vboxsync uint16_t offY = s->vbe_regs[VBE_DISPI_INDEX_Y_OFFSET];
3ebd5757516d21eccdad25ddd456d2913c2fb215vboxsync if (!cBPP || !cX)
3ebd5757516d21eccdad25ddd456d2913c2fb215vboxsync return VINF_SUCCESS; /* Not enough data has been set yet. */
3ebd5757516d21eccdad25ddd456d2913c2fb215vboxsync uint32_t cbLinePitch = calc_line_pitch(cBPP, cVirtWidth);
3ebd5757516d21eccdad25ddd456d2913c2fb215vboxsync if (!cbLinePitch)
3ebd5757516d21eccdad25ddd456d2913c2fb215vboxsync cbLinePitch = calc_line_pitch(cBPP, cX);
f7f5cd7b1e530eb5636da51c974b48ae0c1775b3vboxsync Assert(cbLinePitch != 0);
3ebd5757516d21eccdad25ddd456d2913c2fb215vboxsync uint16_t cVirtHeight = s->vram_size / cbLinePitch;
63787aca0a2a16ec959a5294148726ccf898ddf1vboxsync uint32_t offStart = cbLinePitch * offY;
3ebd5757516d21eccdad25ddd456d2913c2fb215vboxsync if (cBPP == 4)
3ebd5757516d21eccdad25ddd456d2913c2fb215vboxsync offStart += offX >> 1;
3ebd5757516d21eccdad25ddd456d2913c2fb215vboxsync else
3ebd5757516d21eccdad25ddd456d2913c2fb215vboxsync offStart += offX * ((cBPP + 7) >> 3);
3ebd5757516d21eccdad25ddd456d2913c2fb215vboxsync offStart >>= 2;
3ebd5757516d21eccdad25ddd456d2913c2fb215vboxsync s->vbe_line_offset = RT_MIN(cbLinePitch, s->vram_size);
3ebd5757516d21eccdad25ddd456d2913c2fb215vboxsync s->vbe_start_addr = RT_MIN(offStart, s->vram_size);
3ebd5757516d21eccdad25ddd456d2913c2fb215vboxsync s->vbe_regs[VBE_DISPI_INDEX_VIRT_HEIGHT] = cVirtHeight;
3ebd5757516d21eccdad25ddd456d2913c2fb215vboxsync }
3ebd5757516d21eccdad25ddd456d2913c2fb215vboxsync }
3ebd5757516d21eccdad25ddd456d2913c2fb215vboxsync return VINF_SUCCESS;
3ebd5757516d21eccdad25ddd456d2913c2fb215vboxsync}
3ebd5757516d21eccdad25ddd456d2913c2fb215vboxsync#endif
3ebd5757516d21eccdad25ddd456d2913c2fb215vboxsync
3ebd5757516d21eccdad25ddd456d2913c2fb215vboxsync/* called for accesses between 0xa0000 and 0xc0000 */
3ebd5757516d21eccdad25ddd456d2913c2fb215vboxsyncstatic uint32_t vga_mem_readb(void *opaque, target_phys_addr_t addr, int *prc)
3ebd5757516d21eccdad25ddd456d2913c2fb215vboxsync{
3ebd5757516d21eccdad25ddd456d2913c2fb215vboxsync VGAState *s = (VGAState*)opaque;
3ebd5757516d21eccdad25ddd456d2913c2fb215vboxsync int memory_map_mode, plane;
3ebd5757516d21eccdad25ddd456d2913c2fb215vboxsync uint32_t ret;
63787aca0a2a16ec959a5294148726ccf898ddf1vboxsync
63e3a547845f7c31bb4e892a66684b560dc63611vboxsync Log3(("vga: read [0x%x] -> ", addr));
3ebd5757516d21eccdad25ddd456d2913c2fb215vboxsync /* convert to VGA memory offset */
3ebd5757516d21eccdad25ddd456d2913c2fb215vboxsync memory_map_mode = (s->gr[6] >> 2) & 3;
3ebd5757516d21eccdad25ddd456d2913c2fb215vboxsync RTGCPHYS GCPhys = addr; /* save original address */
3ebd5757516d21eccdad25ddd456d2913c2fb215vboxsync
3ebd5757516d21eccdad25ddd456d2913c2fb215vboxsync addr &= 0x1ffff;
3ebd5757516d21eccdad25ddd456d2913c2fb215vboxsync switch(memory_map_mode) {
3ebd5757516d21eccdad25ddd456d2913c2fb215vboxsync case 0:
3ebd5757516d21eccdad25ddd456d2913c2fb215vboxsync break;
3ebd5757516d21eccdad25ddd456d2913c2fb215vboxsync case 1:
3ebd5757516d21eccdad25ddd456d2913c2fb215vboxsync if (addr >= 0x10000)
3ebd5757516d21eccdad25ddd456d2913c2fb215vboxsync return 0xff;
63787aca0a2a16ec959a5294148726ccf898ddf1vboxsync addr += s->bank_offset;
63787aca0a2a16ec959a5294148726ccf898ddf1vboxsync break;
3ebd5757516d21eccdad25ddd456d2913c2fb215vboxsync case 2:
3ebd5757516d21eccdad25ddd456d2913c2fb215vboxsync addr -= 0x10000;
3ebd5757516d21eccdad25ddd456d2913c2fb215vboxsync if (addr >= 0x8000)
3ebd5757516d21eccdad25ddd456d2913c2fb215vboxsync return 0xff;
3ebd5757516d21eccdad25ddd456d2913c2fb215vboxsync break;
3ebd5757516d21eccdad25ddd456d2913c2fb215vboxsync default:
3ebd5757516d21eccdad25ddd456d2913c2fb215vboxsync case 3:
3ebd5757516d21eccdad25ddd456d2913c2fb215vboxsync addr -= 0x18000;
3ebd5757516d21eccdad25ddd456d2913c2fb215vboxsync if (addr >= 0x8000)
3ebd5757516d21eccdad25ddd456d2913c2fb215vboxsync return 0xff;
3ebd5757516d21eccdad25ddd456d2913c2fb215vboxsync break;
3ebd5757516d21eccdad25ddd456d2913c2fb215vboxsync }
3ebd5757516d21eccdad25ddd456d2913c2fb215vboxsync
3ebd5757516d21eccdad25ddd456d2913c2fb215vboxsync if (s->sr[4] & 0x08) {
3ebd5757516d21eccdad25ddd456d2913c2fb215vboxsync /* chain 4 mode : simplest access */
3ebd5757516d21eccdad25ddd456d2913c2fb215vboxsync# ifndef IN_RC
3ebd5757516d21eccdad25ddd456d2913c2fb215vboxsync /* If all planes are accessible, then map the page to the frame buffer and make it writable. */
3ebd5757516d21eccdad25ddd456d2913c2fb215vboxsync if ( (s->sr[2] & 3) == 3
3ebd5757516d21eccdad25ddd456d2913c2fb215vboxsync && !vga_is_dirty(s, addr))
3ebd5757516d21eccdad25ddd456d2913c2fb215vboxsync {
3ebd5757516d21eccdad25ddd456d2913c2fb215vboxsync /** @todo only allow read access (doesn't work now) */
3ebd5757516d21eccdad25ddd456d2913c2fb215vboxsync STAM_COUNTER_INC(&s->StatMapPage);
3ebd5757516d21eccdad25ddd456d2913c2fb215vboxsync IOMMMIOMapMMIO2Page(PDMDevHlpGetVM(s->CTX_SUFF(pDevIns)), GCPhys, s->GCPhysVRAM + addr, X86_PTE_RW|X86_PTE_P);
3ebd5757516d21eccdad25ddd456d2913c2fb215vboxsync /* Set as dirty as write accesses won't be noticed now. */
3ebd5757516d21eccdad25ddd456d2913c2fb215vboxsync vga_set_dirty(s, addr);
3ebd5757516d21eccdad25ddd456d2913c2fb215vboxsync s->fRemappedVGA = true;
3ebd5757516d21eccdad25ddd456d2913c2fb215vboxsync }
63e3a547845f7c31bb4e892a66684b560dc63611vboxsync# endif /* IN_RC */
63e3a547845f7c31bb4e892a66684b560dc63611vboxsync VERIFY_VRAM_READ_OFF_RETURN(s, addr, *prc);
63e3a547845f7c31bb4e892a66684b560dc63611vboxsync ret = s->CTX_SUFF(vram_ptr)[addr];
3ebd5757516d21eccdad25ddd456d2913c2fb215vboxsync } else if (!(s->sr[4] & 0x04)) { /* Host access is controlled by SR4, not GR5! */
3ebd5757516d21eccdad25ddd456d2913c2fb215vboxsync /* odd/even mode (aka text mode mapping) */
3ebd5757516d21eccdad25ddd456d2913c2fb215vboxsync plane = (s->gr[4] & 2) | (addr & 1);
3ebd5757516d21eccdad25ddd456d2913c2fb215vboxsync /* See the comment for a similar line in vga_mem_writeb. */
3ebd5757516d21eccdad25ddd456d2913c2fb215vboxsync RTGCPHYS off = ((addr & ~1) << 2) | plane;
3ebd5757516d21eccdad25ddd456d2913c2fb215vboxsync VERIFY_VRAM_READ_OFF_RETURN(s, off, *prc);
3ebd5757516d21eccdad25ddd456d2913c2fb215vboxsync ret = s->CTX_SUFF(vram_ptr)[off];
63e3a547845f7c31bb4e892a66684b560dc63611vboxsync } else {
3ebd5757516d21eccdad25ddd456d2913c2fb215vboxsync /* standard VGA latched access */
3ebd5757516d21eccdad25ddd456d2913c2fb215vboxsync VERIFY_VRAM_READ_OFF_RETURN(s, addr, *prc);
3ebd5757516d21eccdad25ddd456d2913c2fb215vboxsync s->latch = ((uint32_t *)s->CTX_SUFF(vram_ptr))[addr];
3ebd5757516d21eccdad25ddd456d2913c2fb215vboxsync
3ebd5757516d21eccdad25ddd456d2913c2fb215vboxsync if (!(s->gr[5] & 0x08)) {
3ebd5757516d21eccdad25ddd456d2913c2fb215vboxsync /* read mode 0 */
3ebd5757516d21eccdad25ddd456d2913c2fb215vboxsync plane = s->gr[4];
3ebd5757516d21eccdad25ddd456d2913c2fb215vboxsync ret = GET_PLANE(s->latch, plane);
3ebd5757516d21eccdad25ddd456d2913c2fb215vboxsync } else {
3ebd5757516d21eccdad25ddd456d2913c2fb215vboxsync /* read mode 1 */
3ebd5757516d21eccdad25ddd456d2913c2fb215vboxsync ret = (s->latch ^ mask16[s->gr[2]]) & mask16[s->gr[7]];
63787aca0a2a16ec959a5294148726ccf898ddf1vboxsync ret |= ret >> 16;
63787aca0a2a16ec959a5294148726ccf898ddf1vboxsync ret |= ret >> 8;
3ebd5757516d21eccdad25ddd456d2913c2fb215vboxsync ret = (~ret) & 0xff;
3ebd5757516d21eccdad25ddd456d2913c2fb215vboxsync }
d5a801e98910a0c3c9c65cfc7cf8826f0b0b4450vboxsync }
d5a801e98910a0c3c9c65cfc7cf8826f0b0b4450vboxsync Log3((" 0x%02x\n", ret));
d5a801e98910a0c3c9c65cfc7cf8826f0b0b4450vboxsync return ret;
63787aca0a2a16ec959a5294148726ccf898ddf1vboxsync}
63e3a547845f7c31bb4e892a66684b560dc63611vboxsync
63787aca0a2a16ec959a5294148726ccf898ddf1vboxsync/* called for accesses between 0xa0000 and 0xc0000 */
63787aca0a2a16ec959a5294148726ccf898ddf1vboxsyncstatic int vga_mem_writeb(void *opaque, target_phys_addr_t addr, uint32_t val)
63787aca0a2a16ec959a5294148726ccf898ddf1vboxsync{
d5a801e98910a0c3c9c65cfc7cf8826f0b0b4450vboxsync VGAState *s = (VGAState*)opaque;
d5a801e98910a0c3c9c65cfc7cf8826f0b0b4450vboxsync int memory_map_mode, plane, write_mode, b, func_select, mask;
d5a801e98910a0c3c9c65cfc7cf8826f0b0b4450vboxsync uint32_t write_mask, bit_mask, set_mask;
d5a801e98910a0c3c9c65cfc7cf8826f0b0b4450vboxsync
d5a801e98910a0c3c9c65cfc7cf8826f0b0b4450vboxsync Log3(("vga: [0x%x] = 0x%02x\n", addr, val));
63e3a547845f7c31bb4e892a66684b560dc63611vboxsync /* convert to VGA memory offset */
d5a801e98910a0c3c9c65cfc7cf8826f0b0b4450vboxsync memory_map_mode = (s->gr[6] >> 2) & 3;
d5a801e98910a0c3c9c65cfc7cf8826f0b0b4450vboxsync RTGCPHYS GCPhys = addr; /* save original address */
9674ed871d32468dd791ad601151f46d2e843350vboxsync
9674ed871d32468dd791ad601151f46d2e843350vboxsync addr &= 0x1ffff;
d5a801e98910a0c3c9c65cfc7cf8826f0b0b4450vboxsync switch(memory_map_mode) {
d5a801e98910a0c3c9c65cfc7cf8826f0b0b4450vboxsync case 0:
d5a801e98910a0c3c9c65cfc7cf8826f0b0b4450vboxsync break;
3ebd5757516d21eccdad25ddd456d2913c2fb215vboxsync case 1:
3ebd5757516d21eccdad25ddd456d2913c2fb215vboxsync if (addr >= 0x10000)
3ebd5757516d21eccdad25ddd456d2913c2fb215vboxsync return VINF_SUCCESS;
3ebd5757516d21eccdad25ddd456d2913c2fb215vboxsync addr += s->bank_offset;
3ebd5757516d21eccdad25ddd456d2913c2fb215vboxsync break;
3ebd5757516d21eccdad25ddd456d2913c2fb215vboxsync case 2:
3ebd5757516d21eccdad25ddd456d2913c2fb215vboxsync addr -= 0x10000;
3ebd5757516d21eccdad25ddd456d2913c2fb215vboxsync if (addr >= 0x8000)
016096e367cd20c3d3c3fd9a6650b55935c2e31dvboxsync return VINF_SUCCESS;
016096e367cd20c3d3c3fd9a6650b55935c2e31dvboxsync break;
016096e367cd20c3d3c3fd9a6650b55935c2e31dvboxsync default:
016096e367cd20c3d3c3fd9a6650b55935c2e31dvboxsync case 3:
016096e367cd20c3d3c3fd9a6650b55935c2e31dvboxsync addr -= 0x18000;
f7f5cd7b1e530eb5636da51c974b48ae0c1775b3vboxsync if (addr >= 0x8000)
016096e367cd20c3d3c3fd9a6650b55935c2e31dvboxsync return VINF_SUCCESS;
016096e367cd20c3d3c3fd9a6650b55935c2e31dvboxsync break;
016096e367cd20c3d3c3fd9a6650b55935c2e31dvboxsync }
016096e367cd20c3d3c3fd9a6650b55935c2e31dvboxsync
8287c906b9b1d215824d4cdf6c1eaf40681ebfa8vboxsync if (s->sr[4] & 0x08) {
016096e367cd20c3d3c3fd9a6650b55935c2e31dvboxsync /* chain 4 mode : simplest access */
016096e367cd20c3d3c3fd9a6650b55935c2e31dvboxsync plane = addr & 3;
016096e367cd20c3d3c3fd9a6650b55935c2e31dvboxsync mask = (1 << plane);
016096e367cd20c3d3c3fd9a6650b55935c2e31dvboxsync if (s->sr[2] & mask) {
016096e367cd20c3d3c3fd9a6650b55935c2e31dvboxsync# ifndef IN_RC
016096e367cd20c3d3c3fd9a6650b55935c2e31dvboxsync /* If all planes are accessible, then map the page to the frame buffer and make it writable. */
016096e367cd20c3d3c3fd9a6650b55935c2e31dvboxsync if ( (s->sr[2] & 3) == 3
016096e367cd20c3d3c3fd9a6650b55935c2e31dvboxsync && !vga_is_dirty(s, addr))
3ebd5757516d21eccdad25ddd456d2913c2fb215vboxsync {
016096e367cd20c3d3c3fd9a6650b55935c2e31dvboxsync STAM_COUNTER_INC(&s->StatMapPage);
3ebd5757516d21eccdad25ddd456d2913c2fb215vboxsync IOMMMIOMapMMIO2Page(PDMDevHlpGetVM(s->CTX_SUFF(pDevIns)), GCPhys, s->GCPhysVRAM + addr, X86_PTE_RW | X86_PTE_P);
016096e367cd20c3d3c3fd9a6650b55935c2e31dvboxsync s->fRemappedVGA = true;
016096e367cd20c3d3c3fd9a6650b55935c2e31dvboxsync }
016096e367cd20c3d3c3fd9a6650b55935c2e31dvboxsync# endif /* IN_RC */
016096e367cd20c3d3c3fd9a6650b55935c2e31dvboxsync
016096e367cd20c3d3c3fd9a6650b55935c2e31dvboxsync VERIFY_VRAM_WRITE_OFF_RETURN(s, addr);
3614117c1132a61599e6190939e775cafe549411vboxsync s->CTX_SUFF(vram_ptr)[addr] = val;
04cdb3815f28f5dbf0e7dffc7957fec59260e76fvboxsync Log3(("vga: chain4: [0x%x]\n", addr));
04cdb3815f28f5dbf0e7dffc7957fec59260e76fvboxsync s->plane_updated |= mask; /* only used to detect font change */
04cdb3815f28f5dbf0e7dffc7957fec59260e76fvboxsync vga_set_dirty(s, addr);
04cdb3815f28f5dbf0e7dffc7957fec59260e76fvboxsync }
63e3a547845f7c31bb4e892a66684b560dc63611vboxsync } else if (!(s->sr[4] & 0x04)) { /* Host access is controlled by SR4, not GR5! */
016096e367cd20c3d3c3fd9a6650b55935c2e31dvboxsync /* odd/even mode (aka text mode mapping) */
b2bc8de1367ae24e1b27b53921d0b32ee3df7acdvboxsync plane = (s->gr[4] & 2) | (addr & 1);
8287c906b9b1d215824d4cdf6c1eaf40681ebfa8vboxsync mask = (1 << plane);
601d153b9b7960b2a58cc013b6ac3aa00b95bdeavboxsync if (s->sr[2] & mask) {
8287c906b9b1d215824d4cdf6c1eaf40681ebfa8vboxsync /* 'addr' is offset in a plane, bit 0 selects the plane.
016096e367cd20c3d3c3fd9a6650b55935c2e31dvboxsync * Mask the bit 0, convert plane index to vram offset,
7c48fdac0546978ed14617c8096734ce2d18c8e5vboxsync * that is multiply by the number of planes,
b2bc8de1367ae24e1b27b53921d0b32ee3df7acdvboxsync * and select the plane byte in the vram offset.
b2bc8de1367ae24e1b27b53921d0b32ee3df7acdvboxsync */
b2bc8de1367ae24e1b27b53921d0b32ee3df7acdvboxsync addr = ((addr & ~1) << 2) | plane;
7c48fdac0546978ed14617c8096734ce2d18c8e5vboxsync VERIFY_VRAM_WRITE_OFF_RETURN(s, addr);
b2bc8de1367ae24e1b27b53921d0b32ee3df7acdvboxsync s->CTX_SUFF(vram_ptr)[addr] = val;
3956d0151065a11e49d2213b38a5efdad46807e0vboxsync Log3(("vga: odd/even: [0x%x]\n", addr));
016096e367cd20c3d3c3fd9a6650b55935c2e31dvboxsync s->plane_updated |= mask; /* only used to detect font change */
016096e367cd20c3d3c3fd9a6650b55935c2e31dvboxsync vga_set_dirty(s, addr);
b2bc8de1367ae24e1b27b53921d0b32ee3df7acdvboxsync }
b2bc8de1367ae24e1b27b53921d0b32ee3df7acdvboxsync } else {
b2bc8de1367ae24e1b27b53921d0b32ee3df7acdvboxsync /* standard VGA latched access */
b2bc8de1367ae24e1b27b53921d0b32ee3df7acdvboxsync VERIFY_VRAM_WRITE_OFF_RETURN(s, addr * 4 + 3);
016096e367cd20c3d3c3fd9a6650b55935c2e31dvboxsync
04cdb3815f28f5dbf0e7dffc7957fec59260e76fvboxsync#ifdef IN_RING0
04cdb3815f28f5dbf0e7dffc7957fec59260e76fvboxsync if (((++s->cLatchAccesses) & s->uMaskLatchAccess) == s->uMaskLatchAccess)
04cdb3815f28f5dbf0e7dffc7957fec59260e76fvboxsync {
04cdb3815f28f5dbf0e7dffc7957fec59260e76fvboxsync static uint32_t const s_aMask[5] = { 0x3ff, 0x1ff, 0x7f, 0x3f, 0x1f};
04cdb3815f28f5dbf0e7dffc7957fec59260e76fvboxsync static uint64_t const s_aDelta[5] = {10000000, 5000000, 2500000, 1250000, 625000};
04cdb3815f28f5dbf0e7dffc7957fec59260e76fvboxsync if (PDMDevHlpCanEmulateIoBlock(s->CTX_SUFF(pDevIns)))
b2bc8de1367ae24e1b27b53921d0b32ee3df7acdvboxsync {
b2bc8de1367ae24e1b27b53921d0b32ee3df7acdvboxsync uint64_t u64CurTime = RTTimeSystemNanoTS();
fe554d9c0e3a6de4ba221610ac95a44c7d288e01vboxsync
fe554d9c0e3a6de4ba221610ac95a44c7d288e01vboxsync /* About 1000 (or more) accesses per 10 ms will trigger a reschedule
63787aca0a2a16ec959a5294148726ccf898ddf1vboxsync * to the recompiler
63787aca0a2a16ec959a5294148726ccf898ddf1vboxsync */
63787aca0a2a16ec959a5294148726ccf898ddf1vboxsync if (u64CurTime - s->u64LastLatchedAccess < s_aDelta[s->iMask])
8742e4a4ddb7b62d21d96d56dd1baf01c9f22cecvboxsync {
63787aca0a2a16ec959a5294148726ccf898ddf1vboxsync s->u64LastLatchedAccess = 0;
8742e4a4ddb7b62d21d96d56dd1baf01c9f22cecvboxsync s->iMask = RT_MIN(s->iMask + 1U, RT_ELEMENTS(s_aMask) - 1U);
63787aca0a2a16ec959a5294148726ccf898ddf1vboxsync s->uMaskLatchAccess = s_aMask[s->iMask];
63787aca0a2a16ec959a5294148726ccf898ddf1vboxsync s->cLatchAccesses = s->uMaskLatchAccess - 1;
8742e4a4ddb7b62d21d96d56dd1baf01c9f22cecvboxsync return VINF_EM_RAW_EMULATE_IO_BLOCK;
8742e4a4ddb7b62d21d96d56dd1baf01c9f22cecvboxsync }
8742e4a4ddb7b62d21d96d56dd1baf01c9f22cecvboxsync if (s->u64LastLatchedAccess)
63787aca0a2a16ec959a5294148726ccf898ddf1vboxsync {
762a68c2bb3ccde807330e3d1cb05f8b244a5f72vboxsync Log2(("Reset mask (was %d) delta %RX64 (limit %x)\n", s->iMask, u64CurTime - s->u64LastLatchedAccess, s_aDelta[s->iMask]));
04cdb3815f28f5dbf0e7dffc7957fec59260e76fvboxsync if (s->iMask)
04cdb3815f28f5dbf0e7dffc7957fec59260e76fvboxsync s->iMask--;
04cdb3815f28f5dbf0e7dffc7957fec59260e76fvboxsync s->uMaskLatchAccess = s_aMask[s->iMask];
3ebd5757516d21eccdad25ddd456d2913c2fb215vboxsync }
3ebd5757516d21eccdad25ddd456d2913c2fb215vboxsync s->u64LastLatchedAccess = u64CurTime;
016096e367cd20c3d3c3fd9a6650b55935c2e31dvboxsync }
016096e367cd20c3d3c3fd9a6650b55935c2e31dvboxsync else
016096e367cd20c3d3c3fd9a6650b55935c2e31dvboxsync {
016096e367cd20c3d3c3fd9a6650b55935c2e31dvboxsync s->u64LastLatchedAccess = 0;
016096e367cd20c3d3c3fd9a6650b55935c2e31dvboxsync s->iMask = 0;
016096e367cd20c3d3c3fd9a6650b55935c2e31dvboxsync s->uMaskLatchAccess = s_aMask[s->iMask];
016096e367cd20c3d3c3fd9a6650b55935c2e31dvboxsync s->cLatchAccesses = 0;
016096e367cd20c3d3c3fd9a6650b55935c2e31dvboxsync }
3ebd5757516d21eccdad25ddd456d2913c2fb215vboxsync }
016096e367cd20c3d3c3fd9a6650b55935c2e31dvboxsync#endif
016096e367cd20c3d3c3fd9a6650b55935c2e31dvboxsync
fe554d9c0e3a6de4ba221610ac95a44c7d288e01vboxsync write_mode = s->gr[5] & 3;
016096e367cd20c3d3c3fd9a6650b55935c2e31dvboxsync switch(write_mode) {
016096e367cd20c3d3c3fd9a6650b55935c2e31dvboxsync default:
016096e367cd20c3d3c3fd9a6650b55935c2e31dvboxsync case 0:
3ebd5757516d21eccdad25ddd456d2913c2fb215vboxsync /* rotate */
016096e367cd20c3d3c3fd9a6650b55935c2e31dvboxsync b = s->gr[3] & 7;
3ebd5757516d21eccdad25ddd456d2913c2fb215vboxsync val = ((val >> b) | (val << (8 - b))) & 0xff;
016096e367cd20c3d3c3fd9a6650b55935c2e31dvboxsync val |= val << 8;
b2bc8de1367ae24e1b27b53921d0b32ee3df7acdvboxsync val |= val << 16;
3ebd5757516d21eccdad25ddd456d2913c2fb215vboxsync
016096e367cd20c3d3c3fd9a6650b55935c2e31dvboxsync /* apply set/reset mask */
016096e367cd20c3d3c3fd9a6650b55935c2e31dvboxsync set_mask = mask16[s->gr[1]];
016096e367cd20c3d3c3fd9a6650b55935c2e31dvboxsync val = (val & ~set_mask) | (mask16[s->gr[0]] & set_mask);
016096e367cd20c3d3c3fd9a6650b55935c2e31dvboxsync bit_mask = s->gr[8];
b2bc8de1367ae24e1b27b53921d0b32ee3df7acdvboxsync break;
016096e367cd20c3d3c3fd9a6650b55935c2e31dvboxsync case 1:
016096e367cd20c3d3c3fd9a6650b55935c2e31dvboxsync val = s->latch;
016096e367cd20c3d3c3fd9a6650b55935c2e31dvboxsync goto do_write;
016096e367cd20c3d3c3fd9a6650b55935c2e31dvboxsync case 2:
b2bc8de1367ae24e1b27b53921d0b32ee3df7acdvboxsync val = mask16[val & 0x0f];
3ebd5757516d21eccdad25ddd456d2913c2fb215vboxsync bit_mask = s->gr[8];
fe554d9c0e3a6de4ba221610ac95a44c7d288e01vboxsync break;
fe554d9c0e3a6de4ba221610ac95a44c7d288e01vboxsync case 3:
fe554d9c0e3a6de4ba221610ac95a44c7d288e01vboxsync /* rotate */
fe554d9c0e3a6de4ba221610ac95a44c7d288e01vboxsync b = s->gr[3] & 7;
8287c906b9b1d215824d4cdf6c1eaf40681ebfa8vboxsync val = (val >> b) | (val << (8 - b));
572f495840b063427930dbb6f5a81f3147286effvboxsync
8287c906b9b1d215824d4cdf6c1eaf40681ebfa8vboxsync bit_mask = s->gr[8] & val;
016096e367cd20c3d3c3fd9a6650b55935c2e31dvboxsync val = mask16[s->gr[0]];
016096e367cd20c3d3c3fd9a6650b55935c2e31dvboxsync break;
016096e367cd20c3d3c3fd9a6650b55935c2e31dvboxsync }
8287c906b9b1d215824d4cdf6c1eaf40681ebfa8vboxsync
8287c906b9b1d215824d4cdf6c1eaf40681ebfa8vboxsync /* apply logical operation */
8287c906b9b1d215824d4cdf6c1eaf40681ebfa8vboxsync func_select = s->gr[3] >> 3;
8287c906b9b1d215824d4cdf6c1eaf40681ebfa8vboxsync switch(func_select) {
572f495840b063427930dbb6f5a81f3147286effvboxsync case 0:
572f495840b063427930dbb6f5a81f3147286effvboxsync default:
572f495840b063427930dbb6f5a81f3147286effvboxsync /* nothing to do */
572f495840b063427930dbb6f5a81f3147286effvboxsync break;
63e3a547845f7c31bb4e892a66684b560dc63611vboxsync case 1:
63e3a547845f7c31bb4e892a66684b560dc63611vboxsync /* and */
8287c906b9b1d215824d4cdf6c1eaf40681ebfa8vboxsync val &= s->latch;
8287c906b9b1d215824d4cdf6c1eaf40681ebfa8vboxsync break;
8287c906b9b1d215824d4cdf6c1eaf40681ebfa8vboxsync case 2:
8287c906b9b1d215824d4cdf6c1eaf40681ebfa8vboxsync /* or */
8287c906b9b1d215824d4cdf6c1eaf40681ebfa8vboxsync val |= s->latch;
8287c906b9b1d215824d4cdf6c1eaf40681ebfa8vboxsync break;
8287c906b9b1d215824d4cdf6c1eaf40681ebfa8vboxsync case 3:
8287c906b9b1d215824d4cdf6c1eaf40681ebfa8vboxsync /* xor */
3ebd5757516d21eccdad25ddd456d2913c2fb215vboxsync val ^= s->latch;
3ebd5757516d21eccdad25ddd456d2913c2fb215vboxsync break;
3ebd5757516d21eccdad25ddd456d2913c2fb215vboxsync }
8287c906b9b1d215824d4cdf6c1eaf40681ebfa8vboxsync
8287c906b9b1d215824d4cdf6c1eaf40681ebfa8vboxsync /* apply bit mask */
8287c906b9b1d215824d4cdf6c1eaf40681ebfa8vboxsync bit_mask |= bit_mask << 8;
601d153b9b7960b2a58cc013b6ac3aa00b95bdeavboxsync bit_mask |= bit_mask << 16;
601d153b9b7960b2a58cc013b6ac3aa00b95bdeavboxsync val = (val & bit_mask) | (s->latch & ~bit_mask);
8287c906b9b1d215824d4cdf6c1eaf40681ebfa8vboxsync
8287c906b9b1d215824d4cdf6c1eaf40681ebfa8vboxsync do_write:
3ebd5757516d21eccdad25ddd456d2913c2fb215vboxsync /* mask data according to sr[2] */
8287c906b9b1d215824d4cdf6c1eaf40681ebfa8vboxsync mask = s->sr[2];
016096e367cd20c3d3c3fd9a6650b55935c2e31dvboxsync s->plane_updated |= mask; /* only used to detect font change */
016096e367cd20c3d3c3fd9a6650b55935c2e31dvboxsync write_mask = mask16[mask];
016096e367cd20c3d3c3fd9a6650b55935c2e31dvboxsync ((uint32_t *)s->CTX_SUFF(vram_ptr))[addr] =
016096e367cd20c3d3c3fd9a6650b55935c2e31dvboxsync (((uint32_t *)s->CTX_SUFF(vram_ptr))[addr] & ~write_mask) |
b2bc8de1367ae24e1b27b53921d0b32ee3df7acdvboxsync (val & write_mask);
b2bc8de1367ae24e1b27b53921d0b32ee3df7acdvboxsync Log3(("vga: latch: [0x%x] mask=0x%08x val=0x%08x\n",
016096e367cd20c3d3c3fd9a6650b55935c2e31dvboxsync addr * 4, write_mask, val));
b2bc8de1367ae24e1b27b53921d0b32ee3df7acdvboxsync vga_set_dirty(s, (addr << 2));
9540eeb13face31ddc1c5f15338556fe46f18a77vboxsync }
016096e367cd20c3d3c3fd9a6650b55935c2e31dvboxsync
016096e367cd20c3d3c3fd9a6650b55935c2e31dvboxsync return VINF_SUCCESS;
016096e367cd20c3d3c3fd9a6650b55935c2e31dvboxsync}
016096e367cd20c3d3c3fd9a6650b55935c2e31dvboxsync
016096e367cd20c3d3c3fd9a6650b55935c2e31dvboxsync#if defined(IN_RING3)
a96f8709d113c056da40edb8e2591983226a9761vboxsynctypedef void vga_draw_glyph8_func(uint8_t *d, int linesize,
a96f8709d113c056da40edb8e2591983226a9761vboxsync const uint8_t *font_ptr, int h,
8287c906b9b1d215824d4cdf6c1eaf40681ebfa8vboxsync uint32_t fgcol, uint32_t bgcol,
016096e367cd20c3d3c3fd9a6650b55935c2e31dvboxsync int dscan);
016096e367cd20c3d3c3fd9a6650b55935c2e31dvboxsynctypedef void vga_draw_glyph9_func(uint8_t *d, int linesize,
016096e367cd20c3d3c3fd9a6650b55935c2e31dvboxsync const uint8_t *font_ptr, int h,
fb9af443dbf06990f4956d683286ddce29c4dca6vboxsync uint32_t fgcol, uint32_t bgcol, int dup9);
8287c906b9b1d215824d4cdf6c1eaf40681ebfa8vboxsynctypedef void vga_draw_line_func(VGAState *s1, uint8_t *d,
016096e367cd20c3d3c3fd9a6650b55935c2e31dvboxsync const uint8_t *s, int width);
016096e367cd20c3d3c3fd9a6650b55935c2e31dvboxsync
8287c906b9b1d215824d4cdf6c1eaf40681ebfa8vboxsyncstatic inline unsigned int rgb_to_pixel8(unsigned int r, unsigned int g, unsigned b)
016096e367cd20c3d3c3fd9a6650b55935c2e31dvboxsync{
b2bc8de1367ae24e1b27b53921d0b32ee3df7acdvboxsync return ((r >> 5) << 5) | ((g >> 5) << 2) | (b >> 6);
016096e367cd20c3d3c3fd9a6650b55935c2e31dvboxsync}
016096e367cd20c3d3c3fd9a6650b55935c2e31dvboxsync
fb9af443dbf06990f4956d683286ddce29c4dca6vboxsyncstatic inline unsigned int rgb_to_pixel15(unsigned int r, unsigned int g, unsigned b)
fb9af443dbf06990f4956d683286ddce29c4dca6vboxsync{
8287c906b9b1d215824d4cdf6c1eaf40681ebfa8vboxsync return ((r >> 3) << 10) | ((g >> 3) << 5) | (b >> 3);
016096e367cd20c3d3c3fd9a6650b55935c2e31dvboxsync}
016096e367cd20c3d3c3fd9a6650b55935c2e31dvboxsync
8287c906b9b1d215824d4cdf6c1eaf40681ebfa8vboxsyncstatic inline unsigned int rgb_to_pixel16(unsigned int r, unsigned int g, unsigned b)
016096e367cd20c3d3c3fd9a6650b55935c2e31dvboxsync{
016096e367cd20c3d3c3fd9a6650b55935c2e31dvboxsync return ((r >> 3) << 11) | ((g >> 2) << 5) | (b >> 3);
016096e367cd20c3d3c3fd9a6650b55935c2e31dvboxsync}
016096e367cd20c3d3c3fd9a6650b55935c2e31dvboxsync
8287c906b9b1d215824d4cdf6c1eaf40681ebfa8vboxsyncstatic inline unsigned int rgb_to_pixel32(unsigned int r, unsigned int g, unsigned b)
016096e367cd20c3d3c3fd9a6650b55935c2e31dvboxsync{
3ebd5757516d21eccdad25ddd456d2913c2fb215vboxsync return (r << 16) | (g << 8) | b;
016096e367cd20c3d3c3fd9a6650b55935c2e31dvboxsync}
016096e367cd20c3d3c3fd9a6650b55935c2e31dvboxsync
016096e367cd20c3d3c3fd9a6650b55935c2e31dvboxsync#define DEPTH 8
016096e367cd20c3d3c3fd9a6650b55935c2e31dvboxsync#include "DevVGATmpl.h"
016096e367cd20c3d3c3fd9a6650b55935c2e31dvboxsync
016096e367cd20c3d3c3fd9a6650b55935c2e31dvboxsync#define DEPTH 15
016096e367cd20c3d3c3fd9a6650b55935c2e31dvboxsync#include "DevVGATmpl.h"
016096e367cd20c3d3c3fd9a6650b55935c2e31dvboxsync
016096e367cd20c3d3c3fd9a6650b55935c2e31dvboxsync#define DEPTH 16
016096e367cd20c3d3c3fd9a6650b55935c2e31dvboxsync#include "DevVGATmpl.h"
016096e367cd20c3d3c3fd9a6650b55935c2e31dvboxsync
016096e367cd20c3d3c3fd9a6650b55935c2e31dvboxsync#define DEPTH 32
016096e367cd20c3d3c3fd9a6650b55935c2e31dvboxsync#include "DevVGATmpl.h"
016096e367cd20c3d3c3fd9a6650b55935c2e31dvboxsync
016096e367cd20c3d3c3fd9a6650b55935c2e31dvboxsyncstatic unsigned int rgb_to_pixel8_dup(unsigned int r, unsigned int g, unsigned b)
016096e367cd20c3d3c3fd9a6650b55935c2e31dvboxsync{
016096e367cd20c3d3c3fd9a6650b55935c2e31dvboxsync unsigned int col;
016096e367cd20c3d3c3fd9a6650b55935c2e31dvboxsync col = rgb_to_pixel8(r, g, b);
016096e367cd20c3d3c3fd9a6650b55935c2e31dvboxsync col |= col << 8;
016096e367cd20c3d3c3fd9a6650b55935c2e31dvboxsync col |= col << 16;
016096e367cd20c3d3c3fd9a6650b55935c2e31dvboxsync return col;
016096e367cd20c3d3c3fd9a6650b55935c2e31dvboxsync}
016096e367cd20c3d3c3fd9a6650b55935c2e31dvboxsync
016096e367cd20c3d3c3fd9a6650b55935c2e31dvboxsyncstatic unsigned int rgb_to_pixel15_dup(unsigned int r, unsigned int g, unsigned b)
016096e367cd20c3d3c3fd9a6650b55935c2e31dvboxsync{
016096e367cd20c3d3c3fd9a6650b55935c2e31dvboxsync unsigned int col;
016096e367cd20c3d3c3fd9a6650b55935c2e31dvboxsync col = rgb_to_pixel15(r, g, b);
016096e367cd20c3d3c3fd9a6650b55935c2e31dvboxsync col |= col << 16;
016096e367cd20c3d3c3fd9a6650b55935c2e31dvboxsync return col;
016096e367cd20c3d3c3fd9a6650b55935c2e31dvboxsync}
016096e367cd20c3d3c3fd9a6650b55935c2e31dvboxsync
016096e367cd20c3d3c3fd9a6650b55935c2e31dvboxsyncstatic unsigned int rgb_to_pixel16_dup(unsigned int r, unsigned int g, unsigned b)
016096e367cd20c3d3c3fd9a6650b55935c2e31dvboxsync{
016096e367cd20c3d3c3fd9a6650b55935c2e31dvboxsync unsigned int col;
016096e367cd20c3d3c3fd9a6650b55935c2e31dvboxsync col = rgb_to_pixel16(r, g, b);
016096e367cd20c3d3c3fd9a6650b55935c2e31dvboxsync col |= col << 16;
016096e367cd20c3d3c3fd9a6650b55935c2e31dvboxsync return col;
016096e367cd20c3d3c3fd9a6650b55935c2e31dvboxsync}
016096e367cd20c3d3c3fd9a6650b55935c2e31dvboxsync
016096e367cd20c3d3c3fd9a6650b55935c2e31dvboxsyncstatic unsigned int rgb_to_pixel32_dup(unsigned int r, unsigned int g, unsigned b)
016096e367cd20c3d3c3fd9a6650b55935c2e31dvboxsync{
016096e367cd20c3d3c3fd9a6650b55935c2e31dvboxsync unsigned int col;
016096e367cd20c3d3c3fd9a6650b55935c2e31dvboxsync col = rgb_to_pixel32(r, g, b);
016096e367cd20c3d3c3fd9a6650b55935c2e31dvboxsync return col;
016096e367cd20c3d3c3fd9a6650b55935c2e31dvboxsync}
016096e367cd20c3d3c3fd9a6650b55935c2e31dvboxsync
016096e367cd20c3d3c3fd9a6650b55935c2e31dvboxsync/* return true if the palette was modified */
016096e367cd20c3d3c3fd9a6650b55935c2e31dvboxsyncstatic int update_palette16(VGAState *s)
016096e367cd20c3d3c3fd9a6650b55935c2e31dvboxsync{
016096e367cd20c3d3c3fd9a6650b55935c2e31dvboxsync int full_update, i;
016096e367cd20c3d3c3fd9a6650b55935c2e31dvboxsync uint32_t v, col, *palette;
016096e367cd20c3d3c3fd9a6650b55935c2e31dvboxsync
016096e367cd20c3d3c3fd9a6650b55935c2e31dvboxsync full_update = 0;
016096e367cd20c3d3c3fd9a6650b55935c2e31dvboxsync palette = s->last_palette;
016096e367cd20c3d3c3fd9a6650b55935c2e31dvboxsync for(i = 0; i < 16; i++) {
016096e367cd20c3d3c3fd9a6650b55935c2e31dvboxsync v = s->ar[i];
016096e367cd20c3d3c3fd9a6650b55935c2e31dvboxsync if (s->ar[0x10] & 0x80)
016096e367cd20c3d3c3fd9a6650b55935c2e31dvboxsync v = ((s->ar[0x14] & 0xf) << 4) | (v & 0xf);
016096e367cd20c3d3c3fd9a6650b55935c2e31dvboxsync else
016096e367cd20c3d3c3fd9a6650b55935c2e31dvboxsync v = ((s->ar[0x14] & 0xc) << 4) | (v & 0x3f);
016096e367cd20c3d3c3fd9a6650b55935c2e31dvboxsync v = v * 3;
016096e367cd20c3d3c3fd9a6650b55935c2e31dvboxsync col = s->rgb_to_pixel(c6_to_8(s->palette[v]),
b2bc8de1367ae24e1b27b53921d0b32ee3df7acdvboxsync c6_to_8(s->palette[v + 1]),
016096e367cd20c3d3c3fd9a6650b55935c2e31dvboxsync c6_to_8(s->palette[v + 2]));
if (col != palette[i]) {
full_update = 1;
palette[i] = col;
}
}
return full_update;
}
/* return true if the palette was modified */
static int update_palette256(VGAState *s)
{
int full_update, i;
uint32_t v, col, *palette;
int wide_dac;
full_update = 0;
palette = s->last_palette;
v = 0;
wide_dac = (s->vbe_regs[VBE_DISPI_INDEX_ENABLE] & (VBE_DISPI_ENABLED | VBE_DISPI_8BIT_DAC))
== (VBE_DISPI_ENABLED | VBE_DISPI_8BIT_DAC);
for(i = 0; i < 256; i++) {
if (wide_dac)
col = s->rgb_to_pixel(s->palette[v],
s->palette[v + 1],
s->palette[v + 2]);
else
col = s->rgb_to_pixel(c6_to_8(s->palette[v]),
c6_to_8(s->palette[v + 1]),
c6_to_8(s->palette[v + 2]));
if (col != palette[i]) {
full_update = 1;
palette[i] = col;
}
v += 3;
}
return full_update;
}
static void vga_get_offsets(VGAState *s,
uint32_t *pline_offset,
uint32_t *pstart_addr,
uint32_t *pline_compare)
{
uint32_t start_addr, line_offset, line_compare;
#ifdef CONFIG_BOCHS_VBE
if (s->vbe_regs[VBE_DISPI_INDEX_ENABLE] & VBE_DISPI_ENABLED) {
line_offset = s->vbe_line_offset;
start_addr = s->vbe_start_addr;
line_compare = 65535;
} else
#endif
{
/* compute line_offset in bytes */
line_offset = s->cr[0x13];
line_offset <<= 3;
if (!(s->cr[0x14] & 0x40) && !(s->cr[0x17] & 0x40))
{
/* Word mode. Used for odd/even modes. */
line_offset *= 2;
}
/* starting address */
start_addr = s->cr[0x0d] | (s->cr[0x0c] << 8);
/* line compare */
line_compare = s->cr[0x18] |
((s->cr[0x07] & 0x10) << 4) |
((s->cr[0x09] & 0x40) << 3);
}
*pline_offset = line_offset;
*pstart_addr = start_addr;
*pline_compare = line_compare;
}
/* update start_addr and line_offset. Return TRUE if modified */
static int update_basic_params(VGAState *s)
{
int full_update;
uint32_t start_addr, line_offset, line_compare;
full_update = 0;
s->get_offsets(s, &line_offset, &start_addr, &line_compare);
if (line_offset != s->line_offset ||
start_addr != s->start_addr ||
line_compare != s->line_compare) {
s->line_offset = line_offset;
s->start_addr = start_addr;
s->line_compare = line_compare;
full_update = 1;
}
return full_update;
}
static inline int get_depth_index(int depth)
{
switch(depth) {
default:
case 8:
return 0;
case 15:
return 1;
case 16:
return 2;
case 32:
return 3;
}
}
static vga_draw_glyph8_func *vga_draw_glyph8_table[4] = {
vga_draw_glyph8_8,
vga_draw_glyph8_16,
vga_draw_glyph8_16,
vga_draw_glyph8_32,
};
static vga_draw_glyph8_func *vga_draw_glyph16_table[4] = {
vga_draw_glyph16_8,
vga_draw_glyph16_16,
vga_draw_glyph16_16,
vga_draw_glyph16_32,
};
static vga_draw_glyph9_func *vga_draw_glyph9_table[4] = {
vga_draw_glyph9_8,
vga_draw_glyph9_16,
vga_draw_glyph9_16,
vga_draw_glyph9_32,
};
static const uint8_t cursor_glyph[32 * 4] = {
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
};
/*
* Text mode update
* Missing:
* - underline
* - flashing
*/
static int vga_draw_text(VGAState *s, int full_update, bool fFailOnResize, bool reset_dirty)
{
int cx, cy, cheight, cw, ch, cattr, height, width, ch_attr;
int cx_min, cx_max, linesize, x_incr;
int cx_min_upd, cx_max_upd, cy_start;
uint32_t offset, fgcol, bgcol, v, cursor_offset;
uint8_t *d1, *d, *src, *s1, *dest, *cursor_ptr;
const uint8_t *font_ptr, *font_base[2];
int dup9, line_offset, depth_index, dscan;
uint32_t *palette;
uint32_t *ch_attr_ptr;
vga_draw_glyph8_func *vga_draw_glyph8;
vga_draw_glyph9_func *vga_draw_glyph9;
full_update |= update_palette16(s);
palette = s->last_palette;
/* compute font data address (in plane 2) */
v = s->sr[3];
offset = (((v >> 4) & 1) | ((v << 1) & 6)) * 8192 * 4 + 2;
if (offset != s->font_offsets[0]) {
s->font_offsets[0] = offset;
full_update = 1;
}
font_base[0] = s->CTX_SUFF(vram_ptr) + offset;
offset = (((v >> 5) & 1) | ((v >> 1) & 6)) * 8192 * 4 + 2;
font_base[1] = s->CTX_SUFF(vram_ptr) + offset;
if (offset != s->font_offsets[1]) {
s->font_offsets[1] = offset;
full_update = 1;
}
if (s->plane_updated & (1 << 2)) {
/* if the plane 2 was modified since the last display, it
indicates the font may have been modified */
s->plane_updated = 0;
full_update = 1;
}
full_update |= update_basic_params(s);
line_offset = s->line_offset;
s1 = s->CTX_SUFF(vram_ptr) + (s->start_addr * 8); /** @todo r=bird: Add comment why we do *8 instead of *4, it's not so obvious... */
/* double scanning - not for 9-wide modes */
dscan = (s->cr[9] >> 7) & 1;
/* total width & height */
cheight = (s->cr[9] & 0x1f) + 1;
cw = 8;
if (!(s->sr[1] & 0x01))
cw = 9;
if (s->sr[1] & 0x08)
cw = 16; /* NOTE: no 18 pixel wide */
x_incr = cw * ((s->pDrv->cBits + 7) >> 3);
width = (s->cr[0x01] + 1);
if (s->cr[0x06] == 100) {
/* ugly hack for CGA 160x100x16 - explain me the logic */
height = 100;
} else {
height = s->cr[0x12] |
((s->cr[0x07] & 0x02) << 7) |
((s->cr[0x07] & 0x40) << 3);
height = (height + 1) / cheight;
}
if ((height * width) > CH_ATTR_SIZE) {
/* better than nothing: exit if transient size is too big */
return VINF_SUCCESS;
}
if (width != (int)s->last_width || height != (int)s->last_height ||
cw != s->last_cw || cheight != s->last_ch) {
if (fFailOnResize)
{
/* The caller does not want to call the pfnResize. */
return VERR_TRY_AGAIN;
}
s->last_scr_width = width * cw;
s->last_scr_height = height * cheight;
/* For text modes the direct use of guest VRAM is not implemented, so bpp and cbLine are 0 here. */
int rc = s->pDrv->pfnResize(s->pDrv, 0, NULL, 0, s->last_scr_width, s->last_scr_height);
s->last_width = width;
s->last_height = height;
s->last_ch = cheight;
s->last_cw = cw;
full_update = 1;
if (rc == VINF_VGA_RESIZE_IN_PROGRESS)
return rc;
AssertRC(rc);
}
cursor_offset = ((s->cr[0x0e] << 8) | s->cr[0x0f]) - s->start_addr;
if (cursor_offset != s->cursor_offset ||
s->cr[0xa] != s->cursor_start ||
s->cr[0xb] != s->cursor_end) {
/* if the cursor position changed, we update the old and new
chars */
if (s->cursor_offset < CH_ATTR_SIZE)
s->last_ch_attr[s->cursor_offset] = ~0;
if (cursor_offset < CH_ATTR_SIZE)
s->last_ch_attr[cursor_offset] = ~0;
s->cursor_offset = cursor_offset;
s->cursor_start = s->cr[0xa];
s->cursor_end = s->cr[0xb];
}
cursor_ptr = s->CTX_SUFF(vram_ptr) + (s->start_addr + cursor_offset) * 8;
depth_index = get_depth_index(s->pDrv->cBits);
if (cw == 16)
vga_draw_glyph8 = vga_draw_glyph16_table[depth_index];
else
vga_draw_glyph8 = vga_draw_glyph8_table[depth_index];
vga_draw_glyph9 = vga_draw_glyph9_table[depth_index];
dest = s->pDrv->pu8Data;
linesize = s->pDrv->cbScanline;
ch_attr_ptr = s->last_ch_attr;
cy_start = -1;
cx_max_upd = -1;
cx_min_upd = width;
for(cy = 0; cy < height; cy = cy + (1 << dscan)) {
d1 = dest;
src = s1;
cx_min = width;
cx_max = -1;
for(cx = 0; cx < width; cx++) {
ch_attr = *(uint16_t *)src;
if (full_update || ch_attr != (int)*ch_attr_ptr) {
if (cx < cx_min)
cx_min = cx;
if (cx > cx_max)
cx_max = cx;
if (reset_dirty)
*ch_attr_ptr = ch_attr;
#ifdef WORDS_BIGENDIAN
ch = ch_attr >> 8;
cattr = ch_attr & 0xff;
#else
ch = ch_attr & 0xff;
cattr = ch_attr >> 8;
#endif
font_ptr = font_base[(cattr >> 3) & 1];
font_ptr += 32 * 4 * ch;
bgcol = palette[cattr >> 4];
fgcol = palette[cattr & 0x0f];
if (cw != 9) {
vga_draw_glyph8(d1, linesize,
font_ptr, cheight, fgcol, bgcol, dscan);
} else {
dup9 = 0;
if (ch >= 0xb0 && ch <= 0xdf && (s->ar[0x10] & 0x04))
dup9 = 1;
vga_draw_glyph9(d1, linesize,
font_ptr, cheight, fgcol, bgcol, dup9);
}
if (src == cursor_ptr &&
!(s->cr[0x0a] & 0x20)) {
int line_start, line_last, h;
/* draw the cursor */
line_start = s->cr[0x0a] & 0x1f;
line_last = s->cr[0x0b] & 0x1f;
/* XXX: check that */
if (line_last > cheight - 1)
line_last = cheight - 1;
if (line_last >= line_start && line_start < cheight) {
h = line_last - line_start + 1;
d = d1 + (linesize * line_start << dscan);
if (cw != 9) {
vga_draw_glyph8(d, linesize,
cursor_glyph, h, fgcol, bgcol, dscan);
} else {
vga_draw_glyph9(d, linesize,
cursor_glyph, h, fgcol, bgcol, 1);
}
}
}
}
d1 += x_incr;
src += 8; /* Every second byte of a plane is used in text mode. */
ch_attr_ptr++;
}
if (cx_max != -1) {
/* Keep track of the bounding rectangle for updates. */
if (cy_start == -1)
cy_start = cy;
if (cx_min_upd > cx_min)
cx_min_upd = cx_min;
if (cx_max_upd < cx_max)
cx_max_upd = cx_max;
} else if (cy_start >= 0) {
/* Flush updates to display. */
s->pDrv->pfnUpdateRect(s->pDrv, cx_min_upd * cw, cy_start * cheight,
(cx_max_upd - cx_min_upd + 1) * cw, (cy - cy_start) * cheight);
cy_start = -1;
cx_max_upd = -1;
cx_min_upd = width;
}
dest += linesize * cheight << dscan;
s1 += line_offset;
}
if (cy_start >= 0)
/* Flush any remaining changes to display. */
s->pDrv->pfnUpdateRect(s->pDrv, cx_min_upd * cw, cy_start * cheight,
(cx_max_upd - cx_min_upd + 1) * cw, (cy - cy_start) * cheight);
return VINF_SUCCESS;
}
enum {
VGA_DRAW_LINE2,
VGA_DRAW_LINE2D2,
VGA_DRAW_LINE4,
VGA_DRAW_LINE4D2,
VGA_DRAW_LINE8D2,
VGA_DRAW_LINE8,
VGA_DRAW_LINE15,
VGA_DRAW_LINE16,
VGA_DRAW_LINE24,
VGA_DRAW_LINE32,
VGA_DRAW_LINE_NB
};
static vga_draw_line_func *vga_draw_line_table[4 * VGA_DRAW_LINE_NB] = {
vga_draw_line2_8,
vga_draw_line2_16,
vga_draw_line2_16,
vga_draw_line2_32,
vga_draw_line2d2_8,
vga_draw_line2d2_16,
vga_draw_line2d2_16,
vga_draw_line2d2_32,
vga_draw_line4_8,
vga_draw_line4_16,
vga_draw_line4_16,
vga_draw_line4_32,
vga_draw_line4d2_8,
vga_draw_line4d2_16,
vga_draw_line4d2_16,
vga_draw_line4d2_32,
vga_draw_line8d2_8,
vga_draw_line8d2_16,
vga_draw_line8d2_16,
vga_draw_line8d2_32,
vga_draw_line8_8,
vga_draw_line8_16,
vga_draw_line8_16,
vga_draw_line8_32,
vga_draw_line15_8,
vga_draw_line15_15,
vga_draw_line15_16,
vga_draw_line15_32,
vga_draw_line16_8,
vga_draw_line16_15,
vga_draw_line16_16,
vga_draw_line16_32,
vga_draw_line24_8,
vga_draw_line24_15,
vga_draw_line24_16,
vga_draw_line24_32,
vga_draw_line32_8,
vga_draw_line32_15,
vga_draw_line32_16,
vga_draw_line32_32,
};
static int vga_get_bpp(VGAState *s)
{
int ret;
#ifdef CONFIG_BOCHS_VBE
if (s->vbe_regs[VBE_DISPI_INDEX_ENABLE] & VBE_DISPI_ENABLED) {
ret = s->vbe_regs[VBE_DISPI_INDEX_BPP];
} else
#endif
{
ret = 0;
}
return ret;
}
static void vga_get_resolution(VGAState *s, int *pwidth, int *pheight)
{
int width, height;
#ifdef CONFIG_BOCHS_VBE
if (s->vbe_regs[VBE_DISPI_INDEX_ENABLE] & VBE_DISPI_ENABLED) {
width = s->vbe_regs[VBE_DISPI_INDEX_XRES];
height = s->vbe_regs[VBE_DISPI_INDEX_YRES];
} else
#endif
{
width = (s->cr[0x01] + 1) * 8;
height = s->cr[0x12] |
((s->cr[0x07] & 0x02) << 7) |
((s->cr[0x07] & 0x40) << 3);
height = (height + 1);
}
*pwidth = width;
*pheight = height;
}
/**
* Performs the display driver resizing when in graphics mode.
*
* This will recalc / update any status data depending on the driver
* properties (bit depth mostly).
*
* @returns VINF_SUCCESS on success.
* @returns VINF_VGA_RESIZE_IN_PROGRESS if the operation wasn't complete.
* @param s Pointer to the vga status.
* @param cx The width.
* @param cy The height.
*/
static int vga_resize_graphic(VGAState *s, int cx, int cy, int v)
{
const unsigned cBits = s->get_bpp(s);
int rc;
AssertReturn(cx, VERR_INVALID_PARAMETER);
AssertReturn(cy, VERR_INVALID_PARAMETER);
AssertPtrReturn(s, VERR_INVALID_POINTER);
AssertReturn(s->line_offset, VERR_INTERNAL_ERROR);
#if 0 //def VBOX_WITH_VDMA
/* @todo: we get a second resize here when VBVA is on, while we actually should not */
/* do not do pfnResize in case VBVA is on since all mode changes are performed over VBVA
* we are checking for VDMA state here to ensure this code works only for WDDM driver,
* although we should avoid calling pfnResize for XPDM as well, since pfnResize is actually an extra resize
* event and generally only pfnVBVAxxx calls should be used with HGSMI + VBVA
*
* The reason for doing this for WDDM driver only now is to avoid regressions of the current code */
PVBOXVDMAHOST pVdma = s->pVdma;
if (pVdma && vboxVDMAIsEnabled(pVdma))
rc = VINF_SUCCESS;
else
#endif
{
/* Skip the resize if the values are not valid. */
if (s->start_addr * 4 + s->line_offset * cy < s->vram_size)
/* Take into account the programmed start address (in DWORDs) of the visible screen. */
rc = s->pDrv->pfnResize(s->pDrv, cBits, s->CTX_SUFF(vram_ptr) + s->start_addr * 4, s->line_offset, cx, cy);
else
{
/* Change nothing in the VGA state. Lets hope the guest will eventually programm correct values. */
return VERR_TRY_AGAIN;
}
}
/* last stuff */
s->last_bpp = cBits;
s->last_scr_width = cx;
s->last_scr_height = cy;
s->last_width = cx;
s->last_height = cy;
if (rc == VINF_VGA_RESIZE_IN_PROGRESS)
return rc;
AssertRC(rc);
/* update palette */
switch (s->pDrv->cBits)
{
case 32: s->rgb_to_pixel = rgb_to_pixel32_dup; break;
case 16:
default: s->rgb_to_pixel = rgb_to_pixel16_dup; break;
case 15: s->rgb_to_pixel = rgb_to_pixel15_dup; break;
case 8: s->rgb_to_pixel = rgb_to_pixel8_dup; break;
}
if (s->shift_control == 0)
update_palette16(s);
else if (s->shift_control == 1)
update_palette16(s);
return VINF_SUCCESS;
}
/*
* graphic modes
*/
static int vga_draw_graphic(VGAState *s, int full_update, bool fFailOnResize, bool reset_dirty)
{
int y1, y2, y, update, page_min, page_max, linesize, y_start, double_scan;
int width, height, shift_control, line_offset, page0, page1, bwidth, bits;
int disp_width, multi_run;
uint8_t *d;
uint32_t v, addr1, addr;
vga_draw_line_func *vga_draw_line;
int offsets_changed;
offsets_changed = update_basic_params(s);
full_update |= offsets_changed;
s->get_resolution(s, &width, &height);
disp_width = width;
shift_control = (s->gr[0x05] >> 5) & 3;
double_scan = (s->cr[0x09] >> 7);
multi_run = double_scan;
if (shift_control != s->shift_control ||
double_scan != s->double_scan) {
full_update = 1;
s->shift_control = shift_control;
s->double_scan = double_scan;
}
if (shift_control == 0) {
full_update |= update_palette16(s);
if (s->sr[0x01] & 8) {
v = VGA_DRAW_LINE4D2;
disp_width <<= 1;
} else {
v = VGA_DRAW_LINE4;
}
bits = 4;
} else if (shift_control == 1) {
full_update |= update_palette16(s);
if (s->sr[0x01] & 8) {
v = VGA_DRAW_LINE2D2;
disp_width <<= 1;
} else {
v = VGA_DRAW_LINE2;
}
bits = 4;
} else {
switch(s->get_bpp(s)) {
default:
case 0:
full_update |= update_palette256(s);
v = VGA_DRAW_LINE8D2;
bits = 4;
break;
case 8:
full_update |= update_palette256(s);
v = VGA_DRAW_LINE8;
bits = 8;
break;
case 15:
v = VGA_DRAW_LINE15;
bits = 16;
break;
case 16:
v = VGA_DRAW_LINE16;
bits = 16;
break;
case 24:
v = VGA_DRAW_LINE24;
bits = 24;
break;
case 32:
v = VGA_DRAW_LINE32;
bits = 32;
break;
}
}
if ( disp_width != (int)s->last_width
|| height != (int)s->last_height
|| s->get_bpp(s) != (int)s->last_bpp
|| (offsets_changed && !s->fRenderVRAM))
{
if (fFailOnResize)
{
/* The caller does not want to call the pfnResize. */
return VERR_TRY_AGAIN;
}
int rc = vga_resize_graphic(s, disp_width, height, v);
if (rc != VINF_SUCCESS) /* Return any rc, particularly VINF_VGA_RESIZE_IN_PROGRESS, to the caller. */
return rc;
full_update = 1;
}
vga_draw_line = vga_draw_line_table[v * 4 + get_depth_index(s->pDrv->cBits)];
if (s->cursor_invalidate)
s->cursor_invalidate(s);
line_offset = s->line_offset;
#if 0
Log(("w=%d h=%d v=%d line_offset=%d cr[0x09]=0x%02x cr[0x17]=0x%02x linecmp=%d sr[0x01]=0x%02x\n",
width, height, v, line_offset, s->cr[9], s->cr[0x17], s->line_compare, s->sr[0x01]));
#endif
addr1 = (s->start_addr * 4);
bwidth = (width * bits + 7) / 8; /* The visible width of a scanline. */
y_start = -1;
page_min = 0x7fffffff;
page_max = -1;
d = s->pDrv->pu8Data;
linesize = s->pDrv->cbScanline;
y1 = 0;
y2 = s->cr[0x09] & 0x1F; /* starting row scan count */
for(y = 0; y < height; y++) {
addr = addr1;
/* CGA/MDA compatibility. Note that these addresses are all
* shifted left by two compared to VGA specs.
*/
if (!(s->cr[0x17] & 1)) {
addr = (addr & ~(1 << 15)) | ((y1 & 1) << 15);
}
if (!(s->cr[0x17] & 2)) {
addr = (addr & ~(1 << 16)) | ((y1 & 2) << 15);
}
page0 = addr & TARGET_PAGE_MASK;
page1 = (addr + bwidth - 1) & TARGET_PAGE_MASK;
update = full_update | vga_is_dirty(s, page0) | vga_is_dirty(s, page1);
if (page1 - page0 > TARGET_PAGE_SIZE) {
/* if wide line, can use another page */
update |= vga_is_dirty(s, page0 + TARGET_PAGE_SIZE);
}
/* explicit invalidation for the hardware cursor */
update |= (s->invalidated_y_table[y >> 5] >> (y & 0x1f)) & 1;
if (update) {
if (y_start < 0)
y_start = y;
if (page0 < page_min)
page_min = page0;
if (page1 > page_max)
page_max = page1;
if (s->fRenderVRAM)
vga_draw_line(s, d, s->CTX_SUFF(vram_ptr) + addr, width);
if (s->cursor_draw_line)
s->cursor_draw_line(s, d, y);
} else {
if (y_start >= 0) {
/* flush to display */
s->pDrv->pfnUpdateRect(s->pDrv, 0, y_start, disp_width, y - y_start);
y_start = -1;
}
}
if (!multi_run) {
y1++;
multi_run = double_scan;
if (y2 == 0) {
y2 = s->cr[0x09] & 0x1F;
addr1 += line_offset;
} else {
--y2;
}
} else {
multi_run--;
}
/* line compare acts on the displayed lines */
if ((uint32_t)y == s->line_compare)
addr1 = 0;
d += linesize;
}
if (y_start >= 0) {
/* flush to display */
s->pDrv->pfnUpdateRect(s->pDrv, 0, y_start, disp_width, y - y_start);
}
/* reset modified pages */
if (page_max != -1 && reset_dirty) {
vga_reset_dirty(s, page_min, page_max + TARGET_PAGE_SIZE);
}
memset(s->invalidated_y_table, 0, ((height + 31) >> 5) * 4);
return VINF_SUCCESS;
}
static void vga_draw_blank(VGAState *s, int full_update)
{
int i, w, val;
uint8_t *d;
uint32_t cbScanline = s->pDrv->cbScanline;
if (s->pDrv->pu8Data == s->vram_ptrR3) /* Do not clear the VRAM itself. */
return;
if (!full_update)
return;
if (s->last_scr_width <= 0 || s->last_scr_height <= 0)
return;
if (s->pDrv->cBits == 8)
val = s->rgb_to_pixel(0, 0, 0);
else
val = 0;
w = s->last_scr_width * ((s->pDrv->cBits + 7) >> 3);
d = s->pDrv->pu8Data;
for(i = 0; i < (int)s->last_scr_height; i++) {
memset(d, val, w);
d += cbScanline;
}
s->pDrv->pfnUpdateRect(s->pDrv, 0, 0, s->last_scr_width, s->last_scr_height);
}
static DECLCALLBACK(void) voidUpdateRect(PPDMIDISPLAYCONNECTOR pInterface, uint32_t x, uint32_t y, uint32_t cx, uint32_t cy)
{
}
#define GMODE_TEXT 0
#define GMODE_GRAPH 1
#define GMODE_BLANK 2
static int vga_update_display(PVGASTATE s, bool fUpdateAll, bool fFailOnResize, bool reset_dirty)
{
int rc = VINF_SUCCESS;
int full_update, graphic_mode;
if (s->pDrv->cBits == 0) {
/* nothing to do */
} else {
switch(s->pDrv->cBits) {
case 8:
s->rgb_to_pixel = rgb_to_pixel8_dup;
break;
case 15:
s->rgb_to_pixel = rgb_to_pixel15_dup;
break;
default:
case 16:
s->rgb_to_pixel = rgb_to_pixel16_dup;
break;
case 32:
s->rgb_to_pixel = rgb_to_pixel32_dup;
break;
}
if (fUpdateAll) {
/* A full update is requested. Special processing for a "blank" mode is required, because
* the request must process all pending resolution changes.
*
* Appropriate vga_draw_graphic or vga_draw_text function, which checks the resolution change,
* must be called even if the screen has been blanked, but then the function should do no actual
* screen update. To do this, pfnUpdateRect is replaced with a nop.
*/
typedef DECLCALLBACK(void) FNUPDATERECT(PPDMIDISPLAYCONNECTOR pInterface, uint32_t x, uint32_t y, uint32_t cx, uint32_t cy);
typedef FNUPDATERECT *PFNUPDATERECT;
PFNUPDATERECT pfnUpdateRect = NULL;
/* Detect the "screen blank" conditions. */
int fBlank = 0;
if (!(s->ar_index & 0x20) || (s->sr[0x01] & 0x20)) {
fBlank = 1;
}
if (fBlank) {
/* Provide a void pfnUpdateRect callback. */
if (s->pDrv) {
pfnUpdateRect = s->pDrv->pfnUpdateRect;
s->pDrv->pfnUpdateRect = voidUpdateRect;
}
}
/* Do a complete redraw, which will pick up a new screen resolution. */
if (s->gr[6] & 1) {
s->graphic_mode = GMODE_GRAPH;
rc = vga_draw_graphic(s, 1, false, reset_dirty);
} else {
s->graphic_mode = GMODE_TEXT;
rc = vga_draw_text(s, 1, false, reset_dirty);
}
if (fBlank) {
/* Set the current mode and restore the callback. */
s->graphic_mode = GMODE_BLANK;
if (s->pDrv) {
s->pDrv->pfnUpdateRect = pfnUpdateRect;
}
}
return rc;
}
full_update = 0;
if (!(s->ar_index & 0x20) || (s->sr[0x01] & 0x20)) {
graphic_mode = GMODE_BLANK;
} else {
graphic_mode = s->gr[6] & 1;
}
if (graphic_mode != s->graphic_mode) {
s->graphic_mode = graphic_mode;
full_update = 1;
}
switch(graphic_mode) {
case GMODE_TEXT:
rc = vga_draw_text(s, full_update, fFailOnResize, reset_dirty);
break;
case GMODE_GRAPH:
rc = vga_draw_graphic(s, full_update, fFailOnResize, reset_dirty);
break;
case GMODE_BLANK:
default:
vga_draw_blank(s, full_update);
break;
}
}
return rc;
}
static void vga_save(QEMUFile *f, void *opaque)
{
VGAState *s = (VGAState*)opaque;
int i;
qemu_put_be32s(f, &s->latch);
qemu_put_8s(f, &s->sr_index);
qemu_put_buffer(f, s->sr, 8);
qemu_put_8s(f, &s->gr_index);
qemu_put_buffer(f, s->gr, 16);
qemu_put_8s(f, &s->ar_index);
qemu_put_buffer(f, s->ar, 21);
qemu_put_be32s(f, &s->ar_flip_flop);
qemu_put_8s(f, &s->cr_index);
qemu_put_buffer(f, s->cr, 256);
qemu_put_8s(f, &s->msr);
qemu_put_8s(f, &s->fcr);
qemu_put_8s(f, &s->st00);
qemu_put_8s(f, &s->st01);
qemu_put_8s(f, &s->dac_state);
qemu_put_8s(f, &s->dac_sub_index);
qemu_put_8s(f, &s->dac_read_index);
qemu_put_8s(f, &s->dac_write_index);
qemu_put_buffer(f, s->dac_cache, 3);
qemu_put_buffer(f, s->palette, 768);
qemu_put_be32s(f, &s->bank_offset);
#ifdef CONFIG_BOCHS_VBE
qemu_put_byte(f, 1);
qemu_put_be16s(f, &s->vbe_index);
for(i = 0; i < VBE_DISPI_INDEX_NB_SAVED; i++)
qemu_put_be16s(f, &s->vbe_regs[i]);
qemu_put_be32s(f, &s->vbe_start_addr);
qemu_put_be32s(f, &s->vbe_line_offset);
#else
qemu_put_byte(f, 0);
#endif
}
static int vga_load(QEMUFile *f, void *opaque, int version_id)
{
VGAState *s = (VGAState*)opaque;
int is_vbe, i;
uint32_t u32Dummy;
qemu_get_be32s(f, &s->latch);
qemu_get_8s(f, &s->sr_index);
qemu_get_buffer(f, s->sr, 8);
qemu_get_8s(f, &s->gr_index);
qemu_get_buffer(f, s->gr, 16);
qemu_get_8s(f, &s->ar_index);
qemu_get_buffer(f, s->ar, 21);
qemu_get_be32s(f, (uint32_t *)&s->ar_flip_flop);
qemu_get_8s(f, &s->cr_index);
qemu_get_buffer(f, s->cr, 256);
qemu_get_8s(f, &s->msr);
qemu_get_8s(f, &s->fcr);
qemu_get_8s(f, &s->st00);
qemu_get_8s(f, &s->st01);
qemu_get_8s(f, &s->dac_state);
qemu_get_8s(f, &s->dac_sub_index);
qemu_get_8s(f, &s->dac_read_index);
qemu_get_8s(f, &s->dac_write_index);
qemu_get_buffer(f, s->dac_cache, 3);
qemu_get_buffer(f, s->palette, 768);
qemu_get_be32s(f, (uint32_t *)&s->bank_offset);
is_vbe = qemu_get_byte(f);
#ifdef CONFIG_BOCHS_VBE
if (!is_vbe)
{
Log(("vga_load: !is_vbe !!\n"));
return VERR_SSM_DATA_UNIT_FORMAT_CHANGED;
}
qemu_get_be16s(f, &s->vbe_index);
for(i = 0; i < VBE_DISPI_INDEX_NB_SAVED; i++)
qemu_get_be16s(f, &s->vbe_regs[i]);
qemu_get_be32s(f, &s->vbe_start_addr);
qemu_get_be32s(f, &s->vbe_line_offset);
if (version_id < 2)
qemu_get_be32s(f, &u32Dummy);
s->vbe_bank_max = (s->vram_size >> 16) - 1;
#else
if (is_vbe)
{
Log(("vga_load: is_vbe !!\n"));
return VERR_SSM_DATA_UNIT_FORMAT_CHANGED;
}
#endif
/* force refresh */
s->graphic_mode = -1;
return 0;
}
/* see vgaR3Construct */
static void vga_init_expand(void)
{
int i, j, v, b;
for(i = 0;i < 256; i++) {
v = 0;
for(j = 0; j < 8; j++) {
v |= ((i >> j) & 1) << (j * 4);
}
expand4[i] = v;
v = 0;
for(j = 0; j < 4; j++) {
v |= ((i >> (2 * j)) & 3) << (j * 4);
}
expand2[i] = v;
}
for(i = 0; i < 16; i++) {
v = 0;
for(j = 0; j < 4; j++) {
b = ((i >> j) & 1);
v |= b << (2 * j);
v |= b << (2 * j + 1);
}
expand4to8[i] = v;
}
}
#endif /* !IN_RING0 */
/* -=-=-=-=-=- all contexts -=-=-=-=-=- */
/**
* Port I/O Handler for VGA OUT operations.
*
* @returns VBox status code.
*
* @param pDevIns The device instance.
* @param pvUser User argument - ignored.
* @param Port Port number used for the IN operation.
* @param u32 The value to output.
* @param cb The value size in bytes.
*/
PDMBOTHCBDECL(int) vgaIOPortWrite(PPDMDEVINS pDevIns, void *pvUser, RTIOPORT Port, uint32_t u32, unsigned cb)
{
VGAState *s = PDMINS_2_DATA(pDevIns, PVGASTATE);
int rc = PDMCritSectEnter(&s->lock, VINF_IOM_HC_IOPORT_WRITE);
if (rc != VINF_SUCCESS)
return rc;
NOREF(pvUser);
if (cb == 1)
vga_ioport_write(s, Port, u32);
else if (cb == 2)
{
vga_ioport_write(s, Port, u32 & 0xff);
vga_ioport_write(s, Port + 1, u32 >> 8);
}
PDMCritSectLeave(&s->lock);
return VINF_SUCCESS;
}
/**
* Port I/O Handler for VGA IN operations.
*
* @returns VBox status code.
*
* @param pDevIns The device instance.
* @param pvUser User argument - ignored.
* @param Port Port number used for the IN operation.
* @param pu32 Where to store the result.
* @param cb Number of bytes read.
*/
PDMBOTHCBDECL(int) vgaIOPortRead(PPDMDEVINS pDevIns, void *pvUser, RTIOPORT Port, uint32_t *pu32, unsigned cb)
{
VGAState *s = PDMINS_2_DATA(pDevIns, PVGASTATE);
NOREF(pvUser);
int rc = PDMCritSectEnter(&s->lock, VINF_IOM_HC_IOPORT_READ);
if (rc != VINF_SUCCESS)
return rc;
rc = VERR_IOM_IOPORT_UNUSED;
if (cb == 1)
{
*pu32 = vga_ioport_read(s, Port);
rc = VINF_SUCCESS;
}
else if (cb == 2)
{
*pu32 = vga_ioport_read(s, Port)
| (vga_ioport_read(s, Port + 1) << 8);
rc = VINF_SUCCESS;
}
PDMCritSectLeave(&s->lock);
return rc;
}
/**
* Port I/O Handler for VBE OUT operations.
*
* @returns VBox status code.
*
* @param pDevIns The device instance.
* @param pvUser User argument - ignored.
* @param Port Port number used for the IN operation.
* @param u32 The value to output.
* @param cb The value size in bytes.
*/
PDMBOTHCBDECL(int) vgaIOPortWriteVBEData(PPDMDEVINS pDevIns, void *pvUser, RTIOPORT Port, uint32_t u32, unsigned cb)
{
VGAState *s = PDMINS_2_DATA(pDevIns, PVGASTATE);
int rc = PDMCritSectEnter(&s->lock, VINF_IOM_HC_IOPORT_WRITE);
if (rc != VINF_SUCCESS)
return rc;
NOREF(pvUser);
#ifndef IN_RING3
/*
* This has to be done on the host in order to execute the connector callbacks.
*/
if ( s->vbe_index == VBE_DISPI_INDEX_ENABLE
|| s->vbe_index == VBE_DISPI_INDEX_VBOX_VIDEO)
{
Log(("vgaIOPortWriteVBEData: VBE_DISPI_INDEX_ENABLE - Switching to host...\n"));
PDMCritSectLeave(&s->lock);
return VINF_IOM_HC_IOPORT_WRITE;
}
#endif
#ifdef VBE_BYTEWISE_IO
if (cb == 1)
{
if (!s->fWriteVBEData)
{
if ( (s->vbe_index == VBE_DISPI_INDEX_ENABLE)
&& (u32 & VBE_DISPI_ENABLED))
{
s->fWriteVBEData = false;
rc = vbe_ioport_write_data(s, Port, u32 & 0xFF);
PDMCritSectLeave(&s->lock);
return rc;
}
else
{
s->cbWriteVBEData = u32 & 0xFF;
s->fWriteVBEData = true;
PDMCritSectLeave(&s->lock);
return VINF_SUCCESS;
}
}
else
{
u32 = (s->cbWriteVBEData << 8) | (u32 & 0xFF);
s->fWriteVBEData = false;
cb = 2;
}
}
#endif
if (cb == 2 || cb == 4)
{
//#ifdef IN_RC
// /*
// * The VBE_DISPI_INDEX_ENABLE memsets the entire frame buffer.
// * Since we're not mapping the entire framebuffer any longer that
// * has to be done on the host.
// */
// if ( (s->vbe_index == VBE_DISPI_INDEX_ENABLE)
// && (u32 & VBE_DISPI_ENABLED))
// {
// Log(("vgaIOPortWriteVBEData: VBE_DISPI_INDEX_ENABLE & VBE_DISPI_ENABLED - Switching to host...\n"));
// return VINF_IOM_HC_IOPORT_WRITE;
// }
//#endif
rc = vbe_ioport_write_data(s, Port, u32);
PDMCritSectLeave(&s->lock);
return rc;
}
else
AssertMsgFailed(("vgaIOPortWriteVBEData: Port=%#x cb=%d u32=%#x\n", Port, cb, u32));
PDMCritSectLeave(&s->lock);
return VINF_SUCCESS;
}
/**
* Port I/O Handler for VBE OUT operations.
*
* @returns VBox status code.
*
* @param pDevIns The device instance.
* @param pvUser User argument - ignored.
* @param Port Port number used for the IN operation.
* @param u32 The value to output.
* @param cb The value size in bytes.
*/
PDMBOTHCBDECL(int) vgaIOPortWriteVBEIndex(PPDMDEVINS pDevIns, void *pvUser, RTIOPORT Port, uint32_t u32, unsigned cb)
{
NOREF(pvUser);
VGAState *s = PDMINS_2_DATA(pDevIns, PVGASTATE);
int rc = PDMCritSectEnter(&s->lock, VINF_IOM_HC_IOPORT_WRITE);
if (rc != VINF_SUCCESS)
return rc;
#ifdef VBE_BYTEWISE_IO
if (cb == 1)
{
if (!s->fWriteVBEIndex)
{
s->cbWriteVBEIndex = u32 & 0x00FF;
s->fWriteVBEIndex = true;
PDMCritSectLeave(&s->lock);
return VINF_SUCCESS;
}
else
{
s->fWriteVBEIndex = false;
vbe_ioport_write_index(s, Port, (s->cbWriteVBEIndex << 8) | (u32 & 0x00FF));
PDMCritSectLeave(&s->lock);
return VINF_SUCCESS;
}
}
else
#endif
if (cb == 2)
vbe_ioport_write_index(s, Port, u32);
else
AssertMsgFailed(("vgaIOPortWriteVBEIndex: Port=%#x cb=%d u32=%#x\n", Port, cb, u32));
PDMCritSectLeave(&s->lock);
return VINF_SUCCESS;
}
/**
* Port I/O Handler for VBE IN operations.
*
* @returns VBox status code.
*
* @param pDevIns The device instance.
* @param pvUser User argument - ignored.
* @param Port Port number used for the IN operation.
* @param pu32 Where to store the result.
* @param cb Number of bytes to read.
*/
PDMBOTHCBDECL(int) vgaIOPortReadVBEData(PPDMDEVINS pDevIns, void *pvUser, RTIOPORT Port, uint32_t *pu32, unsigned cb)
{
NOREF(pvUser);
VGAState *s = PDMINS_2_DATA(pDevIns, PVGASTATE);
int rc = PDMCritSectEnter(&s->lock, VINF_IOM_HC_IOPORT_READ);
if (rc != VINF_SUCCESS)
return rc;
#ifdef VBE_BYTEWISE_IO
if (cb == 1)
{
if (!s->fReadVBEData)
{
*pu32 = (vbe_ioport_read_data(s, Port) >> 8) & 0xFF;
s->fReadVBEData = true;
PDMCritSectLeave(&s->lock);
return VINF_SUCCESS;
}
else
{
*pu32 = vbe_ioport_read_data(s, Port) & 0xFF;
s->fReadVBEData = false;
PDMCritSectLeave(&s->lock);
return VINF_SUCCESS;
}
}
else
#endif
if (cb == 2)
{
*pu32 = vbe_ioport_read_data(s, Port);
PDMCritSectLeave(&s->lock);
return VINF_SUCCESS;
}
else if (cb == 4)
{
/* Quick hack for getting the vram size. */
*pu32 = s->vram_size;
PDMCritSectLeave(&s->lock);
return VINF_SUCCESS;
}
AssertMsgFailed(("vgaIOPortReadVBEData: Port=%#x cb=%d\n", Port, cb));
PDMCritSectLeave(&s->lock);
return VERR_IOM_IOPORT_UNUSED;
}
/**
* Port I/O Handler for VBE IN operations.
*
* @returns VBox status code.
*
* @param pDevIns The device instance.
* @param pvUser User argument - ignored.
* @param Port Port number used for the IN operation.
* @param pu32 Where to store the result.
* @param cb Number of bytes to read.
*/
PDMBOTHCBDECL(int) vgaIOPortReadVBEIndex(PPDMDEVINS pDevIns, void *pvUser, RTIOPORT Port, uint32_t *pu32, unsigned cb)
{
NOREF(pvUser);
VGAState *s = PDMINS_2_DATA(pDevIns, PVGASTATE);
int rc = PDMCritSectEnter(&s->lock, VINF_IOM_HC_IOPORT_READ);
if (rc != VINF_SUCCESS)
return rc;
#ifdef VBE_BYTEWISE_IO
if (cb == 1)
{
if (!s->fReadVBEIndex)
{
*pu32 = (vbe_ioport_read_index(s, Port) >> 8) & 0xFF;
s->fReadVBEIndex = true;
PDMCritSectLeave(&s->lock);
return VINF_SUCCESS;
}
else
{
*pu32 = vbe_ioport_read_index(s, Port) & 0xFF;
s->fReadVBEIndex = false;
PDMCritSectLeave(&s->lock);
return VINF_SUCCESS;
}
}
else
#endif
if (cb == 2)
{
*pu32 = vbe_ioport_read_index(s, Port);
PDMCritSectLeave(&s->lock);
return VINF_SUCCESS;
}
PDMCritSectLeave(&s->lock);
AssertMsgFailed(("vgaIOPortReadVBEIndex: Port=%#x cb=%d\n", Port, cb));
return VERR_IOM_IOPORT_UNUSED;
}
#ifdef VBOX_WITH_HGSMI
#ifdef IN_RING3
/**
* Port I/O Handler for HGSMI OUT operations.
*
* @returns VBox status code.
*
* @param pDevIns The device instance.
* @param pvUser User argument - ignored.
* @param Port Port number used for the operation.
* @param u32 The value to output.
* @param cb The value size in bytes.
*/
static DECLCALLBACK(int) vgaR3IOPortHGSMIWrite(PPDMDEVINS pDevIns, void *pvUser, RTIOPORT Port, uint32_t u32, unsigned cb)
{
LogFlowFunc(("Port 0x%x, u32 0x%x, cb %d\n", Port, u32, cb));
VGAState *s = PDMINS_2_DATA(pDevIns, PVGASTATE);
int rc = PDMCritSectEnter(&s->lock, VERR_SEM_BUSY);
if (rc != VINF_SUCCESS)
return rc;
NOREF(pvUser);
if (cb == 4)
{
switch (Port)
{
case VGA_PORT_HGSMI_HOST: /* Host */
{
#if defined(VBOX_WITH_VIDEOHWACCEL) || defined(VBOX_WITH_VDMA) || defined(VBOX_WITH_WDDM)
if(u32 == HGSMIOFFSET_VOID)
{
PDMDevHlpPCISetIrq(pDevIns, 0, PDM_IRQ_LEVEL_LOW);
HGSMIClearHostGuestFlags(s->pHGSMI, HGSMIHOSTFLAGS_IRQ);
}
else
#endif
{
HGSMIHostWrite(s->pHGSMI, u32);
}
} break;
case VGA_PORT_HGSMI_GUEST: /* Guest */
{
HGSMIGuestWrite(s->pHGSMI, u32);
} break;
default:
{
#ifdef DEBUG_sunlover
AssertMsgFailed(("vgaR3IOPortHGSMIWrite: Port=%#x cb=%d u32=%#x\n", Port, cb, u32));
#endif
} break;
}
}
else
{
#ifdef DEBUG_sunlover
AssertMsgFailed(("vgaR3IOPortHGSMIWrite: Port=%#x cb=%d u32=%#x\n", Port, cb, u32));
#endif
}
PDMCritSectLeave(&s->lock);
return VINF_SUCCESS;
}
/**
* Port I/O Handler for HGSMI IN operations.
*
* @returns VBox status code.
*
* @param pDevIns The device instance.
* @param pvUser User argument - ignored.
* @param Port Port number used for the operation.
* @param pu32 Where to store the result.
* @param cb Number of bytes to read.
*/
static DECLCALLBACK(int) vgaR3IOPortHGSMIRead(PPDMDEVINS pDevIns, void *pvUser, RTIOPORT Port, uint32_t *pu32, unsigned cb)
{
LogFlowFunc(("Port 0x%x, cb %d\n", Port, cb));
VGAState *s = PDMINS_2_DATA(pDevIns, PVGASTATE);
int rc = PDMCritSectEnter(&s->lock, VERR_SEM_BUSY);
if (rc != VINF_SUCCESS)
return rc;
NOREF(pvUser);
if (cb == 4)
{
switch (Port)
{
case VGA_PORT_HGSMI_HOST: /* Host */
{
*pu32 = HGSMIHostRead(s->pHGSMI);
} break;
case VGA_PORT_HGSMI_GUEST: /* Guest */
{
*pu32 = HGSMIGuestRead(s->pHGSMI);
} break;
default:
{
#ifdef DEBUG_sunlover
AssertMsgFailed(("vgaR3IOPortHGSMIRead: Port=%#x cb=%d\n", Port, cb));
#endif
rc = VERR_IOM_IOPORT_UNUSED;
} break;
}
}
else
{
#ifdef DEBUG_sunlover
Log(("vgaR3IOPortHGSMIRead: Port=%#x cb=%d\n", Port, cb));
#endif
rc = VERR_IOM_IOPORT_UNUSED;
}
PDMCritSectLeave(&s->lock);
return rc;
}
#endif /* IN_RING3 */
#endif /* VBOX_WITH_HGSMI */
/* -=-=-=-=-=- Guest Context -=-=-=-=-=- */
/*
* Internal. For use inside VGAGCMemoryFillWrite only.
* Macro for apply logical operation and bit mask.
*/
#define APPLY_LOGICAL_AND_MASK(s, val, bit_mask) \
/* apply logical operation */ \
switch(s->gr[3] >> 3) \
{ \
case 0: \
default: \
/* nothing to do */ \
break; \
case 1: \
/* and */ \
val &= s->latch; \
break; \
case 2: \
/* or */ \
val |= s->latch; \
break; \
case 3: \
/* xor */ \
val ^= s->latch; \
break; \
} \
/* apply bit mask */ \
val = (val & bit_mask) | (s->latch & ~bit_mask)
/**
* Legacy VGA memory (0xa0000 - 0xbffff) write hook, to be called from IOM and from the inside of VGADeviceGC.cpp.
* This is the advanced version of vga_mem_writeb function.
*
* @returns VBox status code.
* @param pThis VGA device structure
* @param pvUser User argument - ignored.
* @param GCPhysAddr Physical address of memory to write.
* @param u32Item Data to write, up to 4 bytes.
* @param cbItem Size of data Item, only 1/2/4 bytes is allowed for now.
* @param cItems Number of data items to write.
*/
static int vgaInternalMMIOFill(PVGASTATE pThis, void *pvUser, RTGCPHYS GCPhysAddr, uint32_t u32Item, unsigned cbItem, unsigned cItems)
{
uint32_t b;
uint32_t write_mask, bit_mask, set_mask;
uint32_t aVal[4];
unsigned i;
NOREF(pvUser);
for (i = 0; i < cbItem; i++)
{
aVal[i] = u32Item & 0xff;
u32Item >>= 8;
}
/* convert to VGA memory offset */
/// @todo add check for the end of region
GCPhysAddr &= 0x1ffff;
switch((pThis->gr[6] >> 2) & 3) {
case 0:
break;
case 1:
if (GCPhysAddr >= 0x10000)
return VINF_SUCCESS;
GCPhysAddr += pThis->bank_offset;
break;
case 2:
GCPhysAddr -= 0x10000;
if (GCPhysAddr >= 0x8000)
return VINF_SUCCESS;
break;
default:
case 3:
GCPhysAddr -= 0x18000;
if (GCPhysAddr >= 0x8000)
return VINF_SUCCESS;
break;
}
if (pThis->sr[4] & 0x08) {
/* chain 4 mode : simplest access */
VERIFY_VRAM_WRITE_OFF_RETURN(pThis, GCPhysAddr + cItems * cbItem - 1);
while (cItems-- > 0)
for (i = 0; i < cbItem; i++)
{
if (pThis->sr[2] & (1 << (GCPhysAddr & 3)))
{
pThis->CTX_SUFF(vram_ptr)[GCPhysAddr] = aVal[i];
vga_set_dirty(pThis, GCPhysAddr);
}
GCPhysAddr++;
}
} else if (pThis->gr[5] & 0x10) {
/* odd/even mode (aka text mode mapping) */
VERIFY_VRAM_WRITE_OFF_RETURN(pThis, GCPhysAddr * 2 + cItems * cbItem - 1);
while (cItems-- > 0)
for (i = 0; i < cbItem; i++)
{
unsigned plane = (pThis->gr[4] & 2) | (GCPhysAddr & 1);
if (pThis->sr[2] & (1 << plane)) {
RTGCPHYS PhysAddr2 = ((GCPhysAddr & ~1) << 2) | plane;
pThis->CTX_SUFF(vram_ptr)[PhysAddr2] = aVal[i];
vga_set_dirty(pThis, PhysAddr2);
}
GCPhysAddr++;
}
} else {
/* standard VGA latched access */
VERIFY_VRAM_WRITE_OFF_RETURN(pThis, GCPhysAddr + cItems * cbItem - 1);
switch(pThis->gr[5] & 3) {
default:
case 0:
/* rotate */
b = pThis->gr[3] & 7;
bit_mask = pThis->gr[8];
bit_mask |= bit_mask << 8;
bit_mask |= bit_mask << 16;
set_mask = mask16[pThis->gr[1]];
for (i = 0; i < cbItem; i++)
{
aVal[i] = ((aVal[i] >> b) | (aVal[i] << (8 - b))) & 0xff;
aVal[i] |= aVal[i] << 8;
aVal[i] |= aVal[i] << 16;
/* apply set/reset mask */
aVal[i] = (aVal[i] & ~set_mask) | (mask16[pThis->gr[0]] & set_mask);
APPLY_LOGICAL_AND_MASK(pThis, aVal[i], bit_mask);
}
break;
case 1:
for (i = 0; i < cbItem; i++)
aVal[i] = pThis->latch;
break;
case 2:
bit_mask = pThis->gr[8];
bit_mask |= bit_mask << 8;
bit_mask |= bit_mask << 16;
for (i = 0; i < cbItem; i++)
{
aVal[i] = mask16[aVal[i] & 0x0f];
APPLY_LOGICAL_AND_MASK(pThis, aVal[i], bit_mask);
}
break;
case 3:
/* rotate */
b = pThis->gr[3] & 7;
for (i = 0; i < cbItem; i++)
{
aVal[i] = (aVal[i] >> b) | (aVal[i] << (8 - b));
bit_mask = pThis->gr[8] & aVal[i];
bit_mask |= bit_mask << 8;
bit_mask |= bit_mask << 16;
aVal[i] = mask16[pThis->gr[0]];
APPLY_LOGICAL_AND_MASK(pThis, aVal[i], bit_mask);
}
break;
}
/* mask data according to sr[2] */
write_mask = mask16[pThis->sr[2]];
/* actually write data */
if (cbItem == 1)
{
/* The most frequently case is 1 byte I/O. */
while (cItems-- > 0)
{
((uint32_t *)pThis->CTX_SUFF(vram_ptr))[GCPhysAddr] = (((uint32_t *)pThis->CTX_SUFF(vram_ptr))[GCPhysAddr] & ~write_mask) | (aVal[0] & write_mask);
vga_set_dirty(pThis, GCPhysAddr << 2);
GCPhysAddr++;
}
}
else if (cbItem == 2)
{
/* The second case is 2 bytes I/O. */
while (cItems-- > 0)
{
((uint32_t *)pThis->CTX_SUFF(vram_ptr))[GCPhysAddr] = (((uint32_t *)pThis->CTX_SUFF(vram_ptr))[GCPhysAddr] & ~write_mask) | (aVal[0] & write_mask);
vga_set_dirty(pThis, GCPhysAddr << 2);
GCPhysAddr++;
((uint32_t *)pThis->CTX_SUFF(vram_ptr))[GCPhysAddr] = (((uint32_t *)pThis->CTX_SUFF(vram_ptr))[GCPhysAddr] & ~write_mask) | (aVal[1] & write_mask);
vga_set_dirty(pThis, GCPhysAddr << 2);
GCPhysAddr++;
}
}
else
{
/* And the rest is 4 bytes. */
Assert(cbItem == 4);
while (cItems-- > 0)
for (i = 0; i < cbItem; i++)
{
((uint32_t *)pThis->CTX_SUFF(vram_ptr))[GCPhysAddr] = (((uint32_t *)pThis->CTX_SUFF(vram_ptr))[GCPhysAddr] & ~write_mask) | (aVal[i] & write_mask);
vga_set_dirty(pThis, GCPhysAddr << 2);
GCPhysAddr++;
}
}
}
return VINF_SUCCESS;
}
/**
* Legacy VGA memory (0xa0000 - 0xbffff) write hook, to be called from IOM and from the inside of VGADeviceGC.cpp.
* This is the advanced version of vga_mem_writeb function.
*
* @returns VBox status code.
* @param pDevIns Pointer device instance.
* @param pvUser User argument - ignored.
* @param GCPhysAddr Physical address of memory to write.
* @param u32Item Data to write, up to 4 bytes.
* @param cbItem Size of data Item, only 1/2/4 bytes is allowed for now.
* @param cItems Number of data items to write.
*/
PDMBOTHCBDECL(int) vgaMMIOFill(PPDMDEVINS pDevIns, void *pvUser, RTGCPHYS GCPhysAddr, uint32_t u32Item, unsigned cbItem, unsigned cItems)
{
PVGASTATE pThis = PDMINS_2_DATA(pDevIns, PVGASTATE);
int rc = PDMCritSectEnter(&pThis->lock, VINF_IOM_HC_MMIO_WRITE);
if (rc != VINF_SUCCESS)
return rc;
rc = vgaInternalMMIOFill(pThis, pvUser, GCPhysAddr, u32Item, cbItem, cItems);
PDMCritSectLeave(&pThis->lock);
return rc;
}
#undef APPLY_LOGICAL_AND_MASK
/**
* Legacy VGA memory (0xa0000 - 0xbffff) read hook, to be called from IOM.
*
* @returns VBox status code.
* @param pDevIns Pointer device instance.
* @param pvUser User argument - ignored.
* @param GCPhysAddr Physical address of memory to read.
* @param pv Where to store read data.
* @param cb Bytes to read.
*/
PDMBOTHCBDECL(int) vgaMMIORead(PPDMDEVINS pDevIns, void *pvUser, RTGCPHYS GCPhysAddr, void *pv, unsigned cb)
{
PVGASTATE pThis = PDMINS_2_DATA(pDevIns, PVGASTATE);
STAM_PROFILE_START(&pThis->CTX_MID_Z(Stat,MemoryRead), a);
NOREF(pvUser);
int rc = PDMCritSectEnter(&pThis->lock, VINF_IOM_HC_MMIO_READ);
if (rc != VINF_SUCCESS)
return rc;
switch (cb)
{
case 1:
*(uint8_t *)pv = vga_mem_readb(pThis, GCPhysAddr, &rc); break;
case 2:
*(uint16_t *)pv = vga_mem_readb(pThis, GCPhysAddr, &rc)
| (vga_mem_readb(pThis, GCPhysAddr + 1, &rc) << 8);
break;
case 4:
*(uint32_t *)pv = vga_mem_readb(pThis, GCPhysAddr, &rc)
| (vga_mem_readb(pThis, GCPhysAddr + 1, &rc) << 8)
| (vga_mem_readb(pThis, GCPhysAddr + 2, &rc) << 16)
| (vga_mem_readb(pThis, GCPhysAddr + 3, &rc) << 24);
break;
case 8:
*(uint64_t *)pv = (uint64_t)vga_mem_readb(pThis, GCPhysAddr, &rc)
| ((uint64_t)vga_mem_readb(pThis, GCPhysAddr + 1, &rc) << 8)
| ((uint64_t)vga_mem_readb(pThis, GCPhysAddr + 2, &rc) << 16)
| ((uint64_t)vga_mem_readb(pThis, GCPhysAddr + 3, &rc) << 24)
| ((uint64_t)vga_mem_readb(pThis, GCPhysAddr + 4, &rc) << 32)
| ((uint64_t)vga_mem_readb(pThis, GCPhysAddr + 5, &rc) << 40)
| ((uint64_t)vga_mem_readb(pThis, GCPhysAddr + 6, &rc) << 48)
| ((uint64_t)vga_mem_readb(pThis, GCPhysAddr + 7, &rc) << 56);
break;
default:
{
uint8_t *pu8Data = (uint8_t *)pv;
while (cb-- > 0)
{
*pu8Data++ = vga_mem_readb(pThis, GCPhysAddr++, &rc);
if (RT_UNLIKELY(rc != VINF_SUCCESS))
break;
}
}
}
STAM_PROFILE_STOP(&pThis->CTX_MID_Z(Stat,MemoryRead), a);
PDMCritSectLeave(&pThis->lock);
return rc;
}
/**
* Legacy VGA memory (0xa0000 - 0xbffff) write hook, to be called from IOM.
*
* @returns VBox status code.
* @param pDevIns Pointer device instance.
* @param pvUser User argument - ignored.
* @param GCPhysAddr Physical address of memory to write.
* @param pv Pointer to data.
* @param cb Bytes to write.
*/
PDMBOTHCBDECL(int) vgaMMIOWrite(PPDMDEVINS pDevIns, void *pvUser, RTGCPHYS GCPhysAddr, void *pv, unsigned cb)
{
PVGASTATE pThis = PDMINS_2_DATA(pDevIns, PVGASTATE);
uint8_t *pu8 = (uint8_t *)pv;
STAM_PROFILE_START(&pThis->CTX_MID_Z(Stat,MemoryWrite), a);
int rc = PDMCritSectEnter(&pThis->lock, VINF_IOM_HC_MMIO_WRITE);
if (rc != VINF_SUCCESS)
return rc;
switch (cb)
{
case 1:
rc = vga_mem_writeb(pThis, GCPhysAddr, *pu8);
break;
#if 1
case 2:
rc = vga_mem_writeb(pThis, GCPhysAddr + 0, pu8[0]);
if (RT_LIKELY(rc == VINF_SUCCESS))
rc = vga_mem_writeb(pThis, GCPhysAddr + 1, pu8[1]);
break;
case 4:
rc = vga_mem_writeb(pThis, GCPhysAddr + 0, pu8[0]);
if (RT_LIKELY(rc == VINF_SUCCESS))
rc = vga_mem_writeb(pThis, GCPhysAddr + 1, pu8[1]);
if (RT_LIKELY(rc == VINF_SUCCESS))
rc = vga_mem_writeb(pThis, GCPhysAddr + 2, pu8[2]);
if (RT_LIKELY(rc == VINF_SUCCESS))
rc = vga_mem_writeb(pThis, GCPhysAddr + 3, pu8[3]);
break;
case 8:
rc = vga_mem_writeb(pThis, GCPhysAddr + 0, pu8[0]);
if (RT_LIKELY(rc == VINF_SUCCESS))
rc = vga_mem_writeb(pThis, GCPhysAddr + 1, pu8[1]);
if (RT_LIKELY(rc == VINF_SUCCESS))
rc = vga_mem_writeb(pThis, GCPhysAddr + 2, pu8[2]);
if (RT_LIKELY(rc == VINF_SUCCESS))
rc = vga_mem_writeb(pThis, GCPhysAddr + 3, pu8[3]);
if (RT_LIKELY(rc == VINF_SUCCESS))
rc = vga_mem_writeb(pThis, GCPhysAddr + 4, pu8[4]);
if (RT_LIKELY(rc == VINF_SUCCESS))
rc = vga_mem_writeb(pThis, GCPhysAddr + 5, pu8[5]);
if (RT_LIKELY(rc == VINF_SUCCESS))
rc = vga_mem_writeb(pThis, GCPhysAddr + 6, pu8[6]);
if (RT_LIKELY(rc == VINF_SUCCESS))
rc = vga_mem_writeb(pThis, GCPhysAddr + 7, pu8[7]);
break;
#else
case 2:
rc = vgaMMIOFill(pDevIns, GCPhysAddr, *(uint16_t *)pv, 2, 1);
break;
case 4:
rc = vgaMMIOFill(pDevIns, GCPhysAddr, *(uint32_t *)pv, 4, 1);
break;
case 8:
rc = vgaMMIOFill(pDevIns, GCPhysAddr, *(uint64_t *)pv, 8, 1);
break;
#endif
default:
while (cb-- > 0 && rc == VINF_SUCCESS)
rc = vga_mem_writeb(pThis, GCPhysAddr++, *pu8++);
break;
}
STAM_PROFILE_STOP(&pThis->CTX_MID_Z(Stat,MemoryWrite), a);
PDMCritSectLeave(&pThis->lock);
return rc;
}
/**
* Handle LFB access.
* @returns VBox status code.
* @param pVM VM handle.
* @param pThis VGA device instance data.
* @param GCPhys The access physical address.
* @param GCPtr The access virtual address (only GC).
*/
static int vgaLFBAccess(PVM pVM, PVGASTATE pThis, RTGCPHYS GCPhys, RTGCPTR GCPtr)
{
int rc = PDMCritSectEnter(&pThis->lock, VINF_EM_RAW_EMULATE_INSTR);
if (rc != VINF_SUCCESS)
return rc;
/*
* Set page dirty bit.
*/
vga_set_dirty(pThis, GCPhys - pThis->GCPhysVRAM);
pThis->fLFBUpdated = true;
/*
* Turn of the write handler for this particular page and make it R/W.
* Then return telling the caller to restart the guest instruction.
* ASSUME: the guest always maps video memory RW.
*/
rc = PGMHandlerPhysicalPageTempOff(pVM, pThis->GCPhysVRAM, GCPhys);
if (RT_SUCCESS(rc))
{
#ifndef IN_RING3
rc = PGMShwMakePageWritable(PDMDevHlpGetVMCPU(pThis->CTX_SUFF(pDevIns)), GCPtr,
PGM_MK_PG_IS_MMIO2 | PGM_MK_PG_IS_WRITE_FAULT);
PDMCritSectLeave(&pThis->lock);
AssertMsgReturn( rc == VINF_SUCCESS
/* In the SMP case the page table might be removed while we wait for the PGM lock in the trap handler. */
|| rc == VERR_PAGE_TABLE_NOT_PRESENT
|| rc == VERR_PAGE_NOT_PRESENT,
("PGMShwModifyPage -> GCPtr=%RGv rc=%d\n", GCPtr, rc),
rc);
#else /* IN_RING3 : We don't have any virtual page address of the access here. */
PDMCritSectLeave(&pThis->lock);
Assert(GCPtr == 0);
#endif
return VINF_SUCCESS;
}
PDMCritSectLeave(&pThis->lock);
AssertMsgFailed(("PGMHandlerPhysicalPageTempOff -> rc=%d\n", rc));
return rc;
}
#ifdef IN_RC
/**
* #PF Handler for VBE LFB access.
*
* @returns VBox status code (appropriate for GC return).
* @param pVM VM Handle.
* @param uErrorCode CPU Error code.
* @param pRegFrame Trap register frame.
* @param pvFault The fault address (cr2).
* @param GCPhysFault The GC physical address corresponding to pvFault.
* @param pvUser User argument, ignored.
*/
PDMBOTHCBDECL(int) vgaGCLFBAccessHandler(PVM pVM, RTGCUINT uErrorCode, PCPUMCTXCORE pRegFrame, RTGCPTR pvFault, RTGCPHYS GCPhysFault, void *pvUser)
{
PVGASTATE pThis = (PVGASTATE)pvUser;
Assert(pThis);
Assert(GCPhysFault >= pThis->GCPhysVRAM);
AssertMsg(uErrorCode & X86_TRAP_PF_RW, ("uErrorCode=%#x\n", uErrorCode));
return vgaLFBAccess(pVM, pThis, GCPhysFault, pvFault);
}
#elif IN_RING0
/**
* #PF Handler for VBE LFB access.
*
* @returns VBox status code (appropriate for GC return).
* @param pVM VM Handle.
* @param uErrorCode CPU Error code.
* @param pRegFrame Trap register frame.
* @param pvFault The fault address (cr2).
* @param GCPhysFault The GC physical address corresponding to pvFault.
* @param pvUser User argument, ignored.
*/
PDMBOTHCBDECL(int) vgaR0LFBAccessHandler(PVM pVM, RTGCUINT uErrorCode, PCPUMCTXCORE pRegFrame, RTGCPTR pvFault, RTGCPHYS GCPhysFault, void *pvUser)
{
PVGASTATE pThis = (PVGASTATE)pvUser;
Assert(pThis);
Assert(GCPhysFault >= pThis->GCPhysVRAM);
AssertMsg(uErrorCode & X86_TRAP_PF_RW, ("uErrorCode=%#x\n", uErrorCode));
return vgaLFBAccess(pVM, pThis, GCPhysFault, pvFault);
}
#else /* IN_RING3 */
/**
* HC access handler for the LFB.
*
* @returns VINF_SUCCESS if the handler have carried out the operation.
* @returns VINF_PGM_HANDLER_DO_DEFAULT if the caller should carry out the access operation.
* @param pVM VM Handle.
* @param GCPhys The physical address the guest is writing to.
* @param pvPhys The HC mapping of that address.
* @param pvBuf What the guest is reading/writing.
* @param cbBuf How much it's reading/writing.
* @param enmAccessType The access type.
* @param pvUser User argument.
*/
static DECLCALLBACK(int) vgaR3LFBAccessHandler(PVM pVM, RTGCPHYS GCPhys, void *pvPhys, void *pvBuf, size_t cbBuf, PGMACCESSTYPE enmAccessType, void *pvUser)
{
PVGASTATE pThis = (PVGASTATE)pvUser;
int rc;
Assert(pThis);
Assert(GCPhys >= pThis->GCPhysVRAM);
rc = vgaLFBAccess(pVM, pThis, GCPhys, 0);
if (RT_SUCCESS(rc))
return VINF_PGM_HANDLER_DO_DEFAULT;
AssertMsg(rc <= VINF_SUCCESS, ("rc=%Rrc\n", rc));
return rc;
}
#endif /* IN_RING3 */
/* -=-=-=-=-=- All rings: VGA BIOS I/Os -=-=-=-=-=- */
/**
* Port I/O Handler for VGA BIOS IN operations.
*
* @returns VBox status code.
*
* @param pDevIns The device instance.
* @param pvUser User argument - ignored.
* @param Port Port number used for the IN operation.
* @param pu32 Where to store the result.
* @param cb Number of bytes read.
*/
PDMBOTHCBDECL(int) vgaIOPortReadBIOS(PPDMDEVINS pDevIns, void *pvUser, RTIOPORT Port, uint32_t *pu32, unsigned cb)
{
NOREF(pDevIns);
NOREF(pvUser);
NOREF(Port);
NOREF(pu32);
NOREF(cb);
return VERR_IOM_IOPORT_UNUSED;
}
/**
* Port I/O Handler for VGA BIOS OUT operations.
*
* @returns VBox status code.
*
* @param pDevIns The device instance.
* @param pvUser User argument - ignored.
* @param Port Port number used for the IN operation.
* @param u32 The value to output.
* @param cb The value size in bytes.
*/
PDMBOTHCBDECL(int) vgaIOPortWriteBIOS(PPDMDEVINS pDevIns, void *pvUser, RTIOPORT Port, uint32_t u32, unsigned cb)
{
static int lastWasNotNewline = 0; /* We are only called in a single-threaded way */
PVGASTATE pThis = PDMINS_2_DATA(pDevIns, PVGASTATE);
int rc = PDMCritSectEnter(&pThis->lock, VINF_IOM_HC_IOPORT_WRITE);
if (rc != VINF_SUCCESS)
return rc;
/*
* VGA BIOS char printing.
*/
if ( cb == 1
&& Port == VBE_PRINTF_PORT)
{
#if 0
switch (u32)
{
case '\r': Log(("vgabios: <return>\n")); break;
case '\n': Log(("vgabios: <newline>\n")); break;
case '\t': Log(("vgabios: <tab>\n")); break;
default:
Log(("vgabios: %c\n", u32));
}
#else
if (lastWasNotNewline == 0)
Log(("vgabios: "));
if (u32 != '\r') /* return - is only sent in conjunction with '\n' */
Log(("%c", u32));
if (u32 == '\n')
lastWasNotNewline = 0;
else
lastWasNotNewline = 1;
#endif
PDMCritSectLeave(&pThis->lock);
return VINF_SUCCESS;
}
PDMCritSectLeave(&pThis->lock);
/* not in use. */
return VERR_IOM_IOPORT_UNUSED;
}
/* -=-=-=-=-=- Ring 3 -=-=-=-=-=- */
#ifdef IN_RING3
# ifdef VBE_NEW_DYN_LIST
/**
* Port I/O Handler for VBE Extra OUT operations.
*
* @returns VBox status code.
*
* @param pDevIns The device instance.
* @param pvUser User argument - ignored.
* @param Port Port number used for the IN operation.
* @param u32 The value to output.
* @param cb The value size in bytes.
*/
PDMBOTHCBDECL(int) vbeIOPortWriteVBEExtra(PPDMDEVINS pDevIns, void *pvUser, RTIOPORT Port, uint32_t u32, unsigned cb)
{
PVGASTATE pThis = PDMINS_2_DATA(pDevIns, PVGASTATE);
NOREF(pvUser);
NOREF(Port);
int rc = PDMCritSectEnter(&pThis->lock, VINF_IOM_HC_IOPORT_WRITE);
if (rc != VINF_SUCCESS)
return rc;
if (cb == 2)
{
Log(("vbeIOPortWriteVBEExtra: addr=%#RX32\n", u32));
pThis->u16VBEExtraAddress = u32;
}
else
Log(("vbeIOPortWriteVBEExtra: Ignoring invalid cb=%d writes to the VBE Extra port!!!\n", cb));
PDMCritSectLeave(&pThis->lock);
return VINF_SUCCESS;
}
/**
* Port I/O Handler for VBE Extra IN operations.
*
* @returns VBox status code.
*
* @param pDevIns The device instance.
* @param pvUser User argument - ignored.
* @param Port Port number used for the IN operation.
* @param pu32 Where to store the result.
* @param cb Number of bytes read.
*/
PDMBOTHCBDECL(int) vbeIOPortReadVBEExtra(PPDMDEVINS pDevIns, void *pvUser, RTIOPORT Port, uint32_t *pu32, unsigned cb)
{
PVGASTATE pThis = PDMINS_2_DATA(pDevIns, PVGASTATE);
NOREF(pvUser);
NOREF(Port);
int rc = PDMCritSectEnter(&pThis->lock, VINF_IOM_HC_IOPORT_READ);
if (rc != VINF_SUCCESS)
return rc;
if (pThis->u16VBEExtraAddress == 0xffff)
{
Log(("vbeIOPortReadVBEExtra: Requested number of 64k video banks\n"));
*pu32 = pThis->vram_size / _64K;
rc = VINF_SUCCESS;
}
else
if ( pThis->u16VBEExtraAddress >= pThis->cbVBEExtraData
|| pThis->u16VBEExtraAddress + cb > pThis->cbVBEExtraData)
{
*pu32 = 0;
Log(("vbeIOPortReadVBEExtra: Requested address is out of VBE data!!! Address=%#x(%d) cbVBEExtraData=%#x(%d)\n",
pThis->u16VBEExtraAddress, pThis->u16VBEExtraAddress, pThis->cbVBEExtraData, pThis->cbVBEExtraData));
rc = VINF_SUCCESS;
}
else
if (cb == 1)
{
*pu32 = pThis->pu8VBEExtraData[pThis->u16VBEExtraAddress] & 0xFF;
Log(("vbeIOPortReadVBEExtra: cb=%#x %.*Rhxs\n", cb, cb, pu32));
rc = VINF_SUCCESS;
}
else
if (cb == 2)
{
*pu32 = pThis->pu8VBEExtraData[pThis->u16VBEExtraAddress]
| pThis->pu8VBEExtraData[pThis->u16VBEExtraAddress + 1] << 8;
Log(("vbeIOPortReadVBEExtra: cb=%#x %.*Rhxs\n", cb, cb, pu32));
rc = VINF_SUCCESS;
}
else
{
Log(("vbeIOPortReadVBEExtra: Invalid cb=%d read from the VBE Extra port!!!\n", cb));
rc = VERR_IOM_IOPORT_UNUSED;
}
PDMCritSectLeave(&pThis->lock);
return rc;
}
# endif /* VBE_NEW_DYN_LIST */
/**
* Parse the logo bitmap data at init time.
*
* @returns VBox status code.
*
* @param pThis The VGA instance data.
*/
static int vbeParseBitmap(PVGASTATE pThis)
{
uint16_t i;
PBMPINFO bmpInfo;
POS2HDR pOs2Hdr;
POS22HDR pOs22Hdr;
PWINHDR pWinHdr;
/*
* Get bitmap header data
*/
bmpInfo = (PBMPINFO)(pThis->pu8Logo + sizeof(LOGOHDR));
pWinHdr = (PWINHDR)(pThis->pu8Logo + sizeof(LOGOHDR) + sizeof(BMPINFO));
if (bmpInfo->Type == BMP_ID)
{
switch (pWinHdr->Size)
{
case BMP_HEADER_OS21:
pOs2Hdr = (POS2HDR)pWinHdr;
pThis->cxLogo = pOs2Hdr->Width;
pThis->cyLogo = pOs2Hdr->Height;
pThis->cLogoPlanes = pOs2Hdr->Planes;
pThis->cLogoBits = pOs2Hdr->BitCount;
pThis->LogoCompression = BMP_COMPRESS_NONE;
pThis->cLogoUsedColors = 0;
break;
case BMP_HEADER_OS22:
pOs22Hdr = (POS22HDR)pWinHdr;
pThis->cxLogo = pOs22Hdr->Width;
pThis->cyLogo = pOs22Hdr->Height;
pThis->cLogoPlanes = pOs22Hdr->Planes;
pThis->cLogoBits = pOs22Hdr->BitCount;
pThis->LogoCompression = pOs22Hdr->Compression;
pThis->cLogoUsedColors = pOs22Hdr->ClrUsed;
break;
case BMP_HEADER_WIN3:
pThis->cxLogo = pWinHdr->Width;
pThis->cyLogo = pWinHdr->Height;
pThis->cLogoPlanes = pWinHdr->Planes;
pThis->cLogoBits = pWinHdr->BitCount;
pThis->LogoCompression = pWinHdr->Compression;
pThis->cLogoUsedColors = pWinHdr->ClrUsed;
break;
default:
AssertMsgFailed(("Unsupported bitmap header.\n"));
break;
}
if (pThis->cxLogo > LOGO_MAX_WIDTH || pThis->cyLogo > LOGO_MAX_HEIGHT)
{
AssertMsgFailed(("Bitmap %ux%u is too big.\n", pThis->cxLogo, pThis->cyLogo));
return VERR_INVALID_PARAMETER;
}
if (pThis->cLogoPlanes != 1)
{
AssertMsgFailed(("Bitmap planes %u != 1.\n", pThis->cLogoPlanes));
return VERR_INVALID_PARAMETER;
}
if (pThis->cLogoBits != 4 && pThis->cLogoBits != 8 && pThis->cLogoBits != 24)
{
AssertMsgFailed(("Unsupported %u depth.\n", pThis->cLogoBits));
return VERR_INVALID_PARAMETER;
}
if (pThis->cLogoUsedColors > 256)
{
AssertMsgFailed(("Unsupported %u colors.\n", pThis->cLogoUsedColors));
return VERR_INVALID_PARAMETER;
}
if (pThis->LogoCompression != BMP_COMPRESS_NONE)
{
AssertMsgFailed(("Unsupported %u compression.\n", pThis->LogoCompression));
return VERR_INVALID_PARAMETER;
}
/*
* Read bitmap palette
*/
if (!pThis->cLogoUsedColors)
pThis->cLogoPalEntries = 1 << (pThis->cLogoPlanes * pThis->cLogoBits);
else
pThis->cLogoPalEntries = pThis->cLogoUsedColors;
if (pThis->cLogoPalEntries)
{
const uint8_t *pu8Pal = pThis->pu8Logo + sizeof(LOGOHDR) + sizeof(BMPINFO) + pWinHdr->Size; /* ASSUMES Size location (safe) */
for (i = 0; i < pThis->cLogoPalEntries; i++)
{
uint16_t j;
uint32_t u32Pal = 0;
for (j = 0; j < 3; j++)
{
uint8_t b = *pu8Pal++;
u32Pal <<= 8;
u32Pal |= b;
}
pu8Pal++; /* skip unused byte */
pThis->au32LogoPalette[i] = u32Pal;
}
}
/*
* Bitmap data offset
*/
pThis->pu8LogoBitmap = pThis->pu8Logo + sizeof(LOGOHDR) + bmpInfo->Offset;
}
return VINF_SUCCESS;
}
/**
* Show logo bitmap data.
*
* @returns VBox status code.
*
* @param cbDepth Logo depth.
* @param xLogo Logo X position.
* @param yLogo Logo Y position.
* @param cxLogo Logo width.
* @param cyLogo Logo height.
* @param iStep Fade in/fade out step.
* @param pu32Palette Palette data.
* @param pu8Src Source buffer.
* @param pu8Dst Destination buffer.
*/
static void vbeShowBitmap(uint16_t cBits, uint16_t xLogo, uint16_t yLogo, uint16_t cxLogo, uint16_t cyLogo, uint8_t iStep,
const uint32_t *pu32Palette, const uint8_t *pu8Src, uint8_t *pu8Dst)
{
uint16_t i;
size_t cbPadBytes = 0;
size_t cbLineDst = LOGO_MAX_WIDTH * 4;
uint16_t cyLeft = cyLogo;
pu8Dst += xLogo * 4 + yLogo * cbLineDst;
switch (cBits)
{
case 1:
pu8Dst += cyLogo * cbLineDst;
cbPadBytes = 0;
break;
case 4:
if (((cxLogo % 8) == 0) || ((cxLogo % 8) > 6))
cbPadBytes = 0;
else if ((cxLogo % 8) <= 2)
cbPadBytes = 3;
else if ((cxLogo % 8) <= 4)
cbPadBytes = 2;
else
cbPadBytes = 1;
break;
case 8:
cbPadBytes = ((cxLogo % 4) == 0) ? 0 : (4 - (cxLogo % 4));
break;
case 24:
cbPadBytes = cxLogo % 4;
break;
}
uint8_t j = 0, c = 0;
while (cyLeft-- > 0)
{
uint8_t *pu8TmpPtr = pu8Dst;
if (cBits != 1)
j = 0;
for (i = 0; i < cxLogo; i++)
{
uint8_t pix;
switch (cBits)
{
case 1:
{
if (!j)
c = *pu8Src++;
pix = (c & 1) ? 0xFF : 0;
c >>= 1;
if (pix)
{
*pu8TmpPtr++ = pix * iStep / LOGO_SHOW_STEPS;
*pu8TmpPtr++ = pix * iStep / LOGO_SHOW_STEPS;
*pu8TmpPtr++ = pix * iStep / LOGO_SHOW_STEPS;
*pu8TmpPtr++;
}
else
{
pu8TmpPtr += 4;
}
j = (j + 1) % 8;
break;
}
case 4:
{
if (!j)
c = *pu8Src++;
pix = (c >> 4) & 0xF;
c <<= 4;
uint32_t u32Pal = pu32Palette[pix];
pix = (u32Pal >> 16) & 0xFF;
*pu8TmpPtr++ = pix * iStep / LOGO_SHOW_STEPS;
pix = (u32Pal >> 8) & 0xFF;
*pu8TmpPtr++ = pix * iStep / LOGO_SHOW_STEPS;
pix = u32Pal & 0xFF;
*pu8TmpPtr++ = pix * iStep / LOGO_SHOW_STEPS;
*pu8TmpPtr++;
j = (j + 1) % 2;
break;
}
case 8:
{
uint32_t u32Pal = pu32Palette[*pu8Src++];
pix = (u32Pal >> 16) & 0xFF;
*pu8TmpPtr++ = pix * iStep / LOGO_SHOW_STEPS;
pix = (u32Pal >> 8) & 0xFF;
*pu8TmpPtr++ = pix * iStep / LOGO_SHOW_STEPS;
pix = u32Pal & 0xFF;
*pu8TmpPtr++ = pix * iStep / LOGO_SHOW_STEPS;
*pu8TmpPtr++;
break;
}
case 24:
*pu8TmpPtr++ = *pu8Src++ * iStep / LOGO_SHOW_STEPS;
*pu8TmpPtr++ = *pu8Src++ * iStep / LOGO_SHOW_STEPS;
*pu8TmpPtr++ = *pu8Src++ * iStep / LOGO_SHOW_STEPS;
*pu8TmpPtr++;
break;
}
}
pu8Dst -= cbLineDst;
pu8Src += cbPadBytes;
}
}
/**
* Port I/O Handler for BIOS Logo OUT operations.
*
* @returns VBox status code.
*
* @param pDevIns The device instance.
* @param pvUser User argument - ignored.
* @param Port Port number used for the IN operation.
* @param u32 The value to output.
* @param cb The value size in bytes.
*/
PDMBOTHCBDECL(int) vbeIOPortWriteCMDLogo(PPDMDEVINS pDevIns, void *pvUser, RTIOPORT Port, uint32_t u32, unsigned cb)
{
PVGASTATE pThis = PDMINS_2_DATA(pDevIns, PVGASTATE);
NOREF(pvUser);
NOREF(Port);
Log(("vbeIOPortWriteCMDLogo: cb=%d u32=%#04x(%#04d) (byte)\n", cb, u32, u32));
if (cb == 2)
{
/* Get the logo command */
switch (u32 & 0xFF00)
{
case LOGO_CMD_SET_OFFSET:
pThis->offLogoData = u32 & 0xFF;
break;
case LOGO_CMD_SHOW_BMP:
{
uint8_t iStep = u32 & 0xFF;
const uint8_t *pu8Src = pThis->pu8LogoBitmap;
uint8_t *pu8Dst;
PLOGOHDR pLogoHdr = (PLOGOHDR)pThis->pu8Logo;
uint32_t offDirty = 0;
uint16_t xLogo = (LOGO_MAX_WIDTH - pThis->cxLogo) / 2;
uint16_t yLogo = LOGO_MAX_HEIGHT - (LOGO_MAX_HEIGHT - pThis->cyLogo) / 2;
/* Check VRAM size */
if (pThis->vram_size < LOGO_MAX_SIZE)
break;
if (pThis->vram_size >= LOGO_MAX_SIZE * 2)
pu8Dst = pThis->vram_ptrR3 + LOGO_MAX_SIZE;
else
pu8Dst = pThis->vram_ptrR3;
/* Clear screen - except on power on... */
if (!pThis->fLogoClearScreen)
{
uint32_t *pu32TmpPtr = (uint32_t *)pu8Dst;
/* Clear vram */
for (int i = 0; i < LOGO_MAX_WIDTH; i++)
{
for (int j = 0; j < LOGO_MAX_HEIGHT; j++)
*pu32TmpPtr++ = 0;
}
pThis->fLogoClearScreen = true;
}
/* Show the bitmap. */
vbeShowBitmap(pThis->cLogoBits, xLogo, yLogo,
pThis->cxLogo, pThis->cyLogo,
iStep, &pThis->au32LogoPalette[0],
pu8Src, pu8Dst);
/* Show the 'Press F12...' text. */
if (pLogoHdr->fu8ShowBootMenu == 2)
vbeShowBitmap(1, LOGO_F12TEXT_X, LOGO_F12TEXT_Y,
LOGO_F12TEXT_WIDTH, LOGO_F12TEXT_HEIGHT,
iStep, &pThis->au32LogoPalette[0],
&g_abLogoF12BootText[0], pu8Dst);
/* Blit the offscreen buffer. */
if (pThis->vram_size >= LOGO_MAX_SIZE * 2)
{
uint32_t *pu32TmpDst = (uint32_t *)pThis->vram_ptrR3;
uint32_t *pu32TmpSrc = (uint32_t *)(pThis->vram_ptrR3 + LOGO_MAX_SIZE);
for (int i = 0; i < LOGO_MAX_WIDTH; i++)
{
for (int j = 0; j < LOGO_MAX_HEIGHT; j++)
*pu32TmpDst++ = *pu32TmpSrc++;
}
}
/* Set the dirty flags. */
while (offDirty <= LOGO_MAX_SIZE)
{
vga_set_dirty(pThis, offDirty);
offDirty += PAGE_SIZE;
}
break;
}
default:
Log(("vbeIOPortWriteCMDLogo: invalid command %d\n", u32));
pThis->LogoCommand = LOGO_CMD_NOP;
break;
}
return VINF_SUCCESS;
}
Log(("vbeIOPortWriteCMDLogo: Ignoring invalid cb=%d writes to the VBE Extra port!!!\n", cb));
return VINF_SUCCESS;
}
/**
* Port I/O Handler for BIOS Logo IN operations.
*
* @returns VBox status code.
*
* @param pDevIns The device instance.
* @param pvUser User argument - ignored.
* @param Port Port number used for the IN operation.
* @param pu32 Where to store the result.
* @param cb Number of bytes read.
*/
PDMBOTHCBDECL(int) vbeIOPortReadCMDLogo(PPDMDEVINS pDevIns, void *pvUser, RTIOPORT Port, uint32_t *pu32, unsigned cb)
{
PVGASTATE pThis = PDMINS_2_DATA(pDevIns, PVGASTATE);
NOREF(pvUser);
NOREF(Port);
PRTUINT64U p;
if (pThis->offLogoData + cb > pThis->cbLogo)
{
Log(("vbeIOPortReadCMDLogo: Requested address is out of Logo data!!! offLogoData=%#x(%d) cbLogo=%#x(%d)\n",
pThis->offLogoData, pThis->offLogoData, pThis->cbLogo, pThis->cbLogo));
return VINF_SUCCESS;
}
p = (PRTUINT64U)&pThis->pu8Logo[pThis->offLogoData];
switch (cb)
{
case 1: *pu32 = p->au8[0]; break;
case 2: *pu32 = p->au16[0]; break;
case 4: *pu32 = p->au32[0]; break;
//case 8: *pu32 = p->au64[0]; break;
default: AssertFailed(); break;
}
Log(("vbeIOPortReadCMDLogo: LogoOffset=%#x(%d) cb=%#x %.*Rhxs\n", pThis->offLogoData, pThis->offLogoData, cb, cb, pu32));
pThis->LogoCommand = LOGO_CMD_NOP;
pThis->offLogoData += cb;
return VINF_SUCCESS;
}
/**
* Info handler, device version. Dumps several interesting bits of the
* VGA state that are difficult to decode from the registers.
*
* @param pDevIns Device instance which registered the info.
* @param pHlp Callback functions for doing output.
* @param pszArgs Argument string. Optional and specific to the handler.
*/
static DECLCALLBACK(void) vgaInfoState(PPDMDEVINS pDevIns, PCDBGFINFOHLP pHlp, const char *pszArgs)
{
PVGASTATE s = PDMINS_2_DATA(pDevIns, PVGASTATE);
int is_graph, double_scan;
int w, h, char_height, char_dots;
int val, vfreq_hz, hfreq_hz;
vga_retrace_s *r = &s->retrace_state;
const char *clocks[] = { "25.175 MHz", "28.322 MHz", "External", "Reserved?!" };
is_graph = s->gr[6] & 1;
char_dots = (s->sr[0x01] & 1) ? 8 : 9;
double_scan = s->cr[9] >> 7;
pHlp->pfnPrintf(pHlp, "pixel clock: %s\n", clocks[(s->msr >> 2) & 3]);
pHlp->pfnPrintf(pHlp, "double scanning %s\n", double_scan ? "on" : "off");
pHlp->pfnPrintf(pHlp, "double clocking %s\n", s->sr[1] & 0x08 ? "on" : "off");
val = s->cr[0] + 5;
pHlp->pfnPrintf(pHlp, "htotal: %d px (%d cclk)\n", val * char_dots, val);
val = s->cr[6] + ((s->cr[7] & 1) << 8) + ((s->cr[7] & 0x20) << 4) + 2;
pHlp->pfnPrintf(pHlp, "vtotal: %d px\n", val);
val = s->cr[1] + 1;
w = val * char_dots;
pHlp->pfnPrintf(pHlp, "hdisp : %d px (%d cclk)\n", w, val);
val = s->cr[0x12] + ((s->cr[7] & 2) << 7) + ((s->cr[7] & 0x40) << 4) + 1;
h = val;
pHlp->pfnPrintf(pHlp, "vdisp : %d px\n", val);
val = (s->cr[0xc] << 8) + s->cr[0xd];
pHlp->pfnPrintf(pHlp, "start : %#x\n", val);
if (!is_graph)
{
val = (s->cr[9] & 0x1f) + 1;
char_height = val;
pHlp->pfnPrintf(pHlp, "char height %d\n", val);
pHlp->pfnPrintf(pHlp, "text mode %dx%d\n", w / char_dots, h / (char_height << double_scan));
}
if (s->fRealRetrace)
{
val = r->hb_start;
pHlp->pfnPrintf(pHlp, "hblank start: %d px (%d cclk)\n", val * char_dots, val);
val = r->hb_end;
pHlp->pfnPrintf(pHlp, "hblank end : %d px (%d cclk)\n", val * char_dots, val);
pHlp->pfnPrintf(pHlp, "vblank start: %d px, end: %d px\n", r->vb_start, r->vb_end);
pHlp->pfnPrintf(pHlp, "vsync start : %d px, end: %d px\n", r->vs_start, r->vs_end);
pHlp->pfnPrintf(pHlp, "cclks per frame: %d\n", r->frame_cclks);
pHlp->pfnPrintf(pHlp, "cclk time (ns) : %d\n", r->cclk_ns);
vfreq_hz = 1000000000 / r->frame_ns;
hfreq_hz = 1000000000 / r->h_total_ns;
pHlp->pfnPrintf(pHlp, "vfreq: %d Hz, hfreq: %d.%03d kHz\n",
vfreq_hz, hfreq_hz / 1000, hfreq_hz % 1000);
}
}
/**
* Info handler, device version. Dumps VGA memory formatted as
* ASCII text, no attributes. Only looks at the first page.
*
* @param pDevIns Device instance which registered the info.
* @param pHlp Callback functions for doing output.
* @param pszArgs Argument string. Optional and specific to the handler.
*/
static DECLCALLBACK(void) vgaInfoText(PPDMDEVINS pDevIns, PCDBGFINFOHLP pHlp, const char *pszArgs)
{
PVGASTATE pThis = PDMINS_2_DATA(pDevIns, PVGASTATE);
if (!(pThis->gr[6] & 1))
{
uint8_t *pbSrc = pThis->vram_ptrR3;
if (pbSrc)
{
/*
* Figure out the display size and where the text is.
*
* Note! We're cutting quite a few corners here and this code could
* do with some brushing up. Dumping from the start of the
* frame buffer is done intentionally so that we're more
* likely to obtain the full scrollback of a linux panic.
*/
uint32_t cbLine;
uint32_t offStart;
uint32_t uLineCompareIgn;
uint32_t uVDisp;
uint32_t uCharHeight;
uint32_t uLines;
uint32_t uDblScan;
vga_get_offsets(pThis, &cbLine, &offStart, &uLineCompareIgn);
if (!cbLine)
cbLine = 80 * 8;
uVDisp = pThis->cr[0x12] + ((pThis->cr[7] & 2) << 7) + ((pThis->cr[7] & 0x40) << 4) + 1;
uCharHeight = (pThis->cr[9] & 0x1f) + 1;
uDblScan = pThis->cr[9] >> 7;
uLines = uVDisp / (uCharHeight << uDblScan);
if (uLines < 25)
uLines = 25;
uint32_t cRows = offStart / cbLine + uLines;
uint32_t cCols = cbLine / 8;
if (cRows * cCols * 8 <= pThis->vram_size)
{
/*
* Do the dumping.
*/
uint32_t row, col;
for (col = 0; col < cCols; ++col)
pHlp->pfnPrintf(pHlp, "-");
pHlp->pfnPrintf(pHlp, "\n");
for (row = 0; row < cRows; ++row)
{
if (offStart != 0 && pbSrc == pThis->vram_ptrR3 + offStart)
for (col = 0; col < cCols; ++col)
pHlp->pfnPrintf(pHlp, "-");
for (col = 0; col < cCols; ++col)
{
if (RT_C_IS_PRINT(*pbSrc))
pHlp->pfnPrintf(pHlp, "%c", *pbSrc);
else
pHlp->pfnPrintf(pHlp, ".");
pbSrc += 8; /* chars are spaced 8 bytes apart */
}
pbSrc += cbLine & 7;
pHlp->pfnPrintf(pHlp, "\n");
}
for (col = 0; col < cCols; ++col)
pHlp->pfnPrintf(pHlp, "-");
pHlp->pfnPrintf(pHlp, "\n");
}
else
pHlp->pfnPrintf(pHlp, "Outside VRAM! (%ux%u)\n", cRows, cCols);
}
else
pHlp->pfnPrintf(pHlp, "VGA memory not available!\n");
}
else
pHlp->pfnPrintf(pHlp, "Not in text mode!\n");
}
/**
* Info handler, device version. Dumps VGA Sequencer registers.
*
* @param pDevIns Device instance which registered the info.
* @param pHlp Callback functions for doing output.
* @param pszArgs Argument string. Optional and specific to the handler.
*/
static DECLCALLBACK(void) vgaInfoSR(PPDMDEVINS pDevIns, PCDBGFINFOHLP pHlp, const char *pszArgs)
{
PVGASTATE s = PDMINS_2_DATA(pDevIns, PVGASTATE);
unsigned i;
pHlp->pfnPrintf(pHlp, "VGA Sequencer (3C5): SR index 3C4:%02X\n", s->sr_index);
Assert(sizeof(s->sr) >= 8);
for (i = 0; i < 5; ++i)
{
pHlp->pfnPrintf(pHlp, " SR%02X:%02X", i, s->sr[i]);
}
pHlp->pfnPrintf(pHlp, "\n");
}
/**
* Info handler, device version. Dumps VGA CRTC registers.
*
* @param pDevIns Device instance which registered the info.
* @param pHlp Callback functions for doing output.
* @param pszArgs Argument string. Optional and specific to the handler.
*/
static DECLCALLBACK(void) vgaInfoCR(PPDMDEVINS pDevIns, PCDBGFINFOHLP pHlp, const char *pszArgs)
{
PVGASTATE s = PDMINS_2_DATA(pDevIns, PVGASTATE);
unsigned i;
pHlp->pfnPrintf(pHlp, "VGA CRTC (3D5): CRTC index 3D4:%02X\n", s->cr_index);
Assert(sizeof(s->cr) >= 24);
for (i = 0; i < 10; ++i)
{
pHlp->pfnPrintf(pHlp, " CR%02X:%02X", i, s->cr[i]);
}
pHlp->pfnPrintf(pHlp, "\n");
for (i = 10; i < 20; ++i)
{
pHlp->pfnPrintf(pHlp, " CR%02X:%02X", i, s->cr[i]);
}
pHlp->pfnPrintf(pHlp, "\n");
for (i = 20; i < 25; ++i)
{
pHlp->pfnPrintf(pHlp, " CR%02X:%02X", i, s->cr[i]);
}
pHlp->pfnPrintf(pHlp, "\n");
}
/**
* Info handler, device version. Dumps VGA Graphics Controller registers.
*
* @param pDevIns Device instance which registered the info.
* @param pHlp Callback functions for doing output.
* @param pszArgs Argument string. Optional and specific to the handler.
*/
static DECLCALLBACK(void) vgaInfoGR(PPDMDEVINS pDevIns, PCDBGFINFOHLP pHlp, const char *pszArgs)
{
PVGASTATE s = PDMINS_2_DATA(pDevIns, PVGASTATE);
unsigned i;
pHlp->pfnPrintf(pHlp, "VGA Graphics Controller (3CF): GR index 3CE:%02X\n", s->gr_index);
Assert(sizeof(s->gr) >= 9);
for (i = 0; i < 9; ++i)
{
pHlp->pfnPrintf(pHlp, " GR%02X:%02X", i, s->gr[i]);
}
pHlp->pfnPrintf(pHlp, "\n");
}
/**
* Info handler, device version. Dumps VGA Sequencer registers.
*
* @param pDevIns Device instance which registered the info.
* @param pHlp Callback functions for doing output.
* @param pszArgs Argument string. Optional and specific to the handler.
*/
static DECLCALLBACK(void) vgaInfoAR(PPDMDEVINS pDevIns, PCDBGFINFOHLP pHlp, const char *pszArgs)
{
PVGASTATE s = PDMINS_2_DATA(pDevIns, PVGASTATE);
unsigned i;
pHlp->pfnPrintf(pHlp, "VGA Attribute Controller (3C0): index reg %02X, flip-flop: %d (%s)\n",
s->ar_index, s->ar_flip_flop, s->ar_flip_flop ? "data" : "index" );
Assert(sizeof(s->ar) >= 0x14);
pHlp->pfnPrintf(pHlp, " Palette:");
for (i = 0; i < 0x10; ++i)
{
pHlp->pfnPrintf(pHlp, " %02X", i, s->ar[i]);
}
pHlp->pfnPrintf(pHlp, "\n");
for (i = 0x10; i <= 0x14; ++i)
{
pHlp->pfnPrintf(pHlp, " AR%02X:%02X", i, s->ar[i]);
}
pHlp->pfnPrintf(pHlp, "\n");
}
/**
* Info handler, device version. Dumps VGA DAC registers.
*
* @param pDevIns Device instance which registered the info.
* @param pHlp Callback functions for doing output.
* @param pszArgs Argument string. Optional and specific to the handler.
*/
static DECLCALLBACK(void) vgaInfoDAC(PPDMDEVINS pDevIns, PCDBGFINFOHLP pHlp, const char *pszArgs)
{
PVGASTATE s = PDMINS_2_DATA(pDevIns, PVGASTATE);
unsigned i;
pHlp->pfnPrintf(pHlp, "VGA DAC contents:\n");
for (i = 0; i < 0x100; ++i)
{
pHlp->pfnPrintf(pHlp, " %02X: %02X %02X %02X\n",
i, s->palette[i*3+0], s->palette[i*3+1], s->palette[i*3+2]);
}
}
/**
* Info handler, device version. Dumps VBE registers.
*
* @param pDevIns Device instance which registered the info.
* @param pHlp Callback functions for doing output.
* @param pszArgs Argument string. Optional and specific to the handler.
*/
static DECLCALLBACK(void) vgaInfoVBE(PPDMDEVINS pDevIns, PCDBGFINFOHLP pHlp, const char *pszArgs)
{
PVGASTATE s = PDMINS_2_DATA(pDevIns, PVGASTATE);
if (!(s->vbe_regs[VBE_DISPI_INDEX_ENABLE] & VBE_DISPI_ENABLED))
{
pHlp->pfnPrintf(pHlp, "VBE disabled\n");
return;
}
pHlp->pfnPrintf(pHlp, "VBE state (chip ID 0x%04x):\n", s->vbe_regs[VBE_DISPI_INDEX_ID]);
pHlp->pfnPrintf(pHlp, " Display resolution: %d x %d @ %dbpp\n",
s->vbe_regs[VBE_DISPI_INDEX_XRES], s->vbe_regs[VBE_DISPI_INDEX_YRES],
s->vbe_regs[VBE_DISPI_INDEX_BPP]);
pHlp->pfnPrintf(pHlp, " Virtual resolution: %d x %d\n",
s->vbe_regs[VBE_DISPI_INDEX_VIRT_WIDTH], s->vbe_regs[VBE_DISPI_INDEX_VIRT_HEIGHT]);
pHlp->pfnPrintf(pHlp, " Display start addr: %d, %d\n",
s->vbe_regs[VBE_DISPI_INDEX_X_OFFSET], s->vbe_regs[VBE_DISPI_INDEX_Y_OFFSET]);
pHlp->pfnPrintf(pHlp, " Linear scanline pitch: 0x%04x\n", s->vbe_line_offset);
pHlp->pfnPrintf(pHlp, " Linear display start : 0x%04x\n", s->vbe_start_addr);
pHlp->pfnPrintf(pHlp, " Selected bank: 0x%04x\n", s->vbe_regs[VBE_DISPI_INDEX_BANK]);
}
/* -=-=-=-=-=- Ring 3: IBase -=-=-=-=-=- */
/**
* @interface_method_impl{PDMIBASE,pfnQueryInterface}
*/
static DECLCALLBACK(void *) vgaPortQueryInterface(PPDMIBASE pInterface, const char *pszIID)
{
PVGASTATE pThis = RT_FROM_MEMBER(pInterface, VGASTATE, IBase);
PDMIBASE_RETURN_INTERFACE(pszIID, PDMIBASE, &pThis->IBase);
PDMIBASE_RETURN_INTERFACE(pszIID, PDMIDISPLAYPORT, &pThis->IPort);
#if defined(VBOX_WITH_HGSMI) && (defined(VBOX_WITH_VIDEOHWACCEL) || defined(VBOX_WITH_CRHGSMI))
PDMIBASE_RETURN_INTERFACE(pszIID, PDMIDISPLAYVBVACALLBACKS, &pThis->IVBVACallbacks);
#endif
return NULL;
}
/* -=-=-=-=-=- Ring 3: Dummy IDisplayConnector -=-=-=-=-=- */
/**
* Resize the display.
* This is called when the resolution changes. This usually happens on
* request from the guest os, but may also happen as the result of a reset.
*
* @param pInterface Pointer to this interface.
* @param cx New display width.
* @param cy New display height
* @thread The emulation thread.
*/
static DECLCALLBACK(int) vgaDummyResize(PPDMIDISPLAYCONNECTOR pInterface, uint32_t bpp, void *pvVRAM, uint32_t cbLine, uint32_t cx, uint32_t cy)
{
return VINF_SUCCESS;
}
/**
* Update a rectangle of the display.
* PDMIDISPLAYPORT::pfnUpdateDisplay is the caller.
*
* @param pInterface Pointer to this interface.
* @param x The upper left corner x coordinate of the rectangle.
* @param y The upper left corner y coordinate of the rectangle.
* @param cx The width of the rectangle.
* @param cy The height of the rectangle.
* @thread The emulation thread.
*/
static DECLCALLBACK(void) vgaDummyUpdateRect(PPDMIDISPLAYCONNECTOR pInterface, uint32_t x, uint32_t y, uint32_t cx, uint32_t cy)
{
}
/**
* Refresh the display.
*
* The interval between these calls is set by
* PDMIDISPLAYPORT::pfnSetRefreshRate(). The driver should call
* PDMIDISPLAYPORT::pfnUpdateDisplay() if it wishes to refresh the
* display. PDMIDISPLAYPORT::pfnUpdateDisplay calls pfnUpdateRect with
* the changed rectangles.
*
* @param pInterface Pointer to this interface.
* @thread The emulation thread.
*/
static DECLCALLBACK(void) vgaDummyRefresh(PPDMIDISPLAYCONNECTOR pInterface)
{
}
/* -=-=-=-=-=- Ring 3: IDisplayPort -=-=-=-=-=- */
/** Converts a display port interface pointer to a vga state pointer. */
#define IDISPLAYPORT_2_VGASTATE(pInterface) ( (PVGASTATE)((uintptr_t)pInterface - RT_OFFSETOF(VGASTATE, IPort)) )
/**
* Update the display with any changed regions.
*
* @param pInterface Pointer to this interface.
* @see PDMIKEYBOARDPORT::pfnUpdateDisplay() for details.
*/
static DECLCALLBACK(int) vgaPortUpdateDisplay(PPDMIDISPLAYPORT pInterface)
{
PVGASTATE pThis = IDISPLAYPORT_2_VGASTATE(pInterface);
PDMDEV_ASSERT_EMT(VGASTATE2DEVINS(pThis));
PPDMDEVINS pDevIns = pThis->CTX_SUFF(pDevIns);
int rc = PDMCritSectEnter(&pThis->lock, VERR_SEM_BUSY);
AssertRC(rc);
#ifndef VBOX_WITH_HGSMI
/* This should be called only in non VBVA mode. */
#else
if (VBVAUpdateDisplay (pThis) == VINF_SUCCESS)
{
PDMCritSectLeave(&pThis->lock);
return VINF_SUCCESS;
}
#endif /* VBOX_WITH_HGSMI */
STAM_COUNTER_INC(&pThis->StatUpdateDisp);
if (pThis->fHasDirtyBits && pThis->GCPhysVRAM && pThis->GCPhysVRAM != NIL_RTGCPHYS32)
{
PGMHandlerPhysicalReset(PDMDevHlpGetVM(pDevIns), pThis->GCPhysVRAM);
pThis->fHasDirtyBits = false;
}
if (pThis->fRemappedVGA)
{
IOMMMIOResetRegion(PDMDevHlpGetVM(pDevIns), 0x000a0000);
pThis->fRemappedVGA = false;
}
rc = vga_update_display(pThis, false, false, true);
if (rc != VINF_SUCCESS)
{
PDMCritSectLeave(&pThis->lock);
return rc;
}
PDMCritSectLeave(&pThis->lock);
return VINF_SUCCESS;
}
/* Internal worker called under pThis->lock. */
static int updateDisplayAll(PVGASTATE pThis)
{
PPDMDEVINS pDevIns = pThis->CTX_SUFF(pDevIns);
/* The dirty bits array has been just cleared, reset handlers as well. */
if (pThis->GCPhysVRAM && pThis->GCPhysVRAM != NIL_RTGCPHYS32)
{
PGMHandlerPhysicalReset(PDMDevHlpGetVM(pDevIns), pThis->GCPhysVRAM);
}
if (pThis->fRemappedVGA)
{
IOMMMIOResetRegion(PDMDevHlpGetVM(pDevIns), 0x000a0000);
pThis->fRemappedVGA = false;
}
pThis->graphic_mode = -1; /* force full update */
return vga_update_display(pThis, true, false, true);
}
/**
* Update the entire display.
*
* @param pInterface Pointer to this interface.
* @see PDMIKEYBOARDPORT::pfnUpdateDisplayAll() for details.
*/
static DECLCALLBACK(int) vgaPortUpdateDisplayAll(PPDMIDISPLAYPORT pInterface)
{
PVGASTATE pThis = IDISPLAYPORT_2_VGASTATE(pInterface);
PDMDEV_ASSERT_EMT(VGASTATE2DEVINS(pThis));
PPDMDEVINS pDevIns = pThis->CTX_SUFF(pDevIns);
/* This is called both in VBVA mode and normal modes. */
#ifdef DEBUG_sunlover
LogFlow(("vgaPortUpdateDisplayAll\n"));
#endif /* DEBUG_sunlover */
int rc = PDMCritSectEnter(&pThis->lock, VERR_SEM_BUSY);
AssertRC(rc);
rc = updateDisplayAll(pThis);
PDMCritSectLeave(&pThis->lock);
return rc;
}
/**
* Sets the refresh rate and restart the timer.
*
* @returns VBox status code.
* @param pInterface Pointer to this interface.
* @param cMilliesInterval Number of millis between two refreshes.
* @see PDMIKEYBOARDPORT::pfnSetRefreshRate() for details.
*/
static DECLCALLBACK(int) vgaPortSetRefreshRate(PPDMIDISPLAYPORT pInterface, uint32_t cMilliesInterval)
{
PVGASTATE pThis = IDISPLAYPORT_2_VGASTATE(pInterface);
pThis->cMilliesRefreshInterval = cMilliesInterval;
if (cMilliesInterval)
return TMTimerSetMillies(pThis->RefreshTimer, cMilliesInterval);
return TMTimerStop(pThis->RefreshTimer);
}
/** @copydoc PDMIDISPLAYPORT::pfnQueryColorDepth */
static DECLCALLBACK(int) vgaPortQueryColorDepth(PPDMIDISPLAYPORT pInterface, uint32_t *pcBits)
{
PVGASTATE pThis = IDISPLAYPORT_2_VGASTATE(pInterface);
if (!pcBits)
return VERR_INVALID_PARAMETER;
*pcBits = vga_get_bpp(pThis);
return VINF_SUCCESS;
}
/**
* Create a 32-bbp screenshot of the display. Size of the bitmap scanline in bytes is 4*width.
*
* @param pInterface Pointer to this interface.
* @param ppu8Data Where to store the pointer to the allocated buffer.
* @param pcbData Where to store the actual size of the bitmap.
* @param pcx Where to store the width of the bitmap.
* @param pcy Where to store the height of the bitmap.
* @see PDMIDISPLAYPORT::pfnTakeScreenshot() for details.
*/
static DECLCALLBACK(int) vgaPortTakeScreenshot(PPDMIDISPLAYPORT pInterface, uint8_t **ppu8Data, size_t *pcbData, uint32_t *pcx, uint32_t *pcy)
{
PVGASTATE pThis = IDISPLAYPORT_2_VGASTATE(pInterface);
PDMDEV_ASSERT_EMT(VGASTATE2DEVINS(pThis));
LogFlow(("vgaPortTakeScreenshot: ppu8Data=%p pcbData=%p pcx=%p pcy=%p\n", ppu8Data, pcbData, pcx, pcy));
/*
* Validate input.
*/
if (!RT_VALID_PTR(ppu8Data) || !RT_VALID_PTR(pcbData) || !RT_VALID_PTR(pcx) || !RT_VALID_PTR(pcy))
return VERR_INVALID_PARAMETER;
int rc = PDMCritSectEnter(&pThis->lock, VERR_SEM_BUSY);
AssertRCReturn(rc, rc);
/*
* Get screenshot. This function will fail if a resize is required.
* So there is not need to do a 'updateDisplayAll' before taking screenshot.
*/
/*
* The display connector interface is temporarily replaced with the fake one.
*/
PDMIDISPLAYCONNECTOR Connector;
memset(&Connector, 0, sizeof (PDMIDISPLAYCONNECTOR));
/*
* Allocate the buffer for 32 bits per pixel bitmap.
*/
size_t cbRequired = pThis->last_scr_width * 4 * pThis->last_scr_height;
/* The size can't be zero or greater than the size of the VRAM.
* Inconsistent VGA device state can cause the incorrect size values.
*/
if (cbRequired && cbRequired <= pThis->vram_size)
{
uint8_t *pu8Data = (uint8_t *)RTMemAlloc(cbRequired);
if (pu8Data == NULL)
{
rc = VERR_NO_MEMORY;
}
else
{
/*
* Only 3 methods, assigned below, will be called during the screenshot update.
* All other are already set to NULL.
*/
Connector.pu8Data = pu8Data;
Connector.cBits = 32;
Connector.cx = pThis->last_scr_width;
Connector.cy = pThis->last_scr_height;
Connector.cbScanline = Connector.cx * 4;
Connector.pfnRefresh = vgaDummyRefresh;
Connector.pfnResize = vgaDummyResize;
Connector.pfnUpdateRect = vgaDummyUpdateRect;
/* Save & replace state data. */
PPDMIDISPLAYCONNECTOR pConnectorSaved = pThis->pDrv;
int32_t graphic_mode_saved = pThis->graphic_mode;
bool fRenderVRAMSaved = pThis->fRenderVRAM;
pThis->pDrv = &Connector;
pThis->graphic_mode = -1; /* force a full refresh. */
pThis->fRenderVRAM = 1; /* force the guest VRAM rendering to the given buffer. */
/* Make the screenshot.
*
* The second parameter is 'false' because the current display state is being rendered to an
* external buffer using a fake connector. That is if display is blanked, we expect a black
* screen in the external buffer.
* If there is a pending resize, the function will fail.
*/
rc = vga_update_display(pThis, false, true, false);
/* Restore. */
pThis->pDrv = pConnectorSaved;
pThis->graphic_mode = graphic_mode_saved;
pThis->fRenderVRAM = fRenderVRAMSaved;
if (rc == VINF_SUCCESS)
{
/*
* Return the result.
*/
*ppu8Data = pu8Data;
*pcbData = cbRequired;
*pcx = Connector.cx;
*pcy = Connector.cy;
}
else
{
/* If we do not return a success, then the data buffer must be freed. */
RTMemFree(pu8Data);
}
}
}
else
rc = VERR_NOT_SUPPORTED;
PDMCritSectLeave(&pThis->lock);
LogFlow(("vgaPortTakeScreenshot: returns %Rrc (cbData=%d cx=%d cy=%d)\n", rc, cbRequired, Connector.cx, Connector.cy));
return rc;
}
/**
* Free a screenshot buffer allocated in vgaPortTakeScreenshot.
*
* @param pInterface Pointer to this interface.
* @param pu8Data Pointer returned by vgaPortTakeScreenshot.
* @see PDMIDISPLAYPORT::pfnFreeScreenshot() for details.
*/
static DECLCALLBACK(void) vgaPortFreeScreenshot(PPDMIDISPLAYPORT pInterface, uint8_t *pu8Data)
{
NOREF(pInterface);
LogFlow(("vgaPortFreeScreenshot: pu8Data=%p\n", pu8Data));
RTMemFree(pu8Data);
}
/**
* Copy bitmap to the display.
*
* @param pInterface Pointer to this interface.
* @param pvData Pointer to the bitmap bits.
* @param x The upper left corner x coordinate of the destination rectangle.
* @param y The upper left corner y coordinate of the destination rectangle.
* @param cx The width of the source and destination rectangles.
* @param cy The height of the source and destination rectangles.
* @see PDMIDISPLAYPORT::pfnDisplayBlt() for details.
*/
static DECLCALLBACK(int) vgaPortDisplayBlt(PPDMIDISPLAYPORT pInterface, const void *pvData, uint32_t x, uint32_t y, uint32_t cx, uint32_t cy)
{
PVGASTATE pThis = IDISPLAYPORT_2_VGASTATE(pInterface);
int rc = VINF_SUCCESS;
PDMDEV_ASSERT_EMT(VGASTATE2DEVINS(pThis));
LogFlow(("vgaPortDisplayBlt: pvData=%p x=%d y=%d cx=%d cy=%d\n", pvData, x, y, cx, cy));
rc = PDMCritSectEnter(&pThis->lock, VERR_SEM_BUSY);
AssertRC(rc);
/*
* Validate input.
*/
if ( pvData
&& x < pThis->pDrv->cx
&& cx <= pThis->pDrv->cx
&& cx + x <= pThis->pDrv->cx
&& y < pThis->pDrv->cy
&& cy <= pThis->pDrv->cy
&& cy + y <= pThis->pDrv->cy)
{
/*
* Determine bytes per pixel in the destination buffer.
*/
size_t cbPixelDst = 0;
switch (pThis->pDrv->cBits)
{
case 8:
cbPixelDst = 1;
break;
case 15:
case 16:
cbPixelDst = 2;
break;
case 24:
cbPixelDst = 3;
break;
case 32:
cbPixelDst = 4;
break;
default:
rc = VERR_INVALID_PARAMETER;
break;
}
if (RT_SUCCESS(rc))
{
/*
* The blitting loop.
*/
size_t cbLineSrc = cx * 4; /* 32 bits per pixel. */
uint8_t *pu8Src = (uint8_t *)pvData;
size_t cbLineDst = pThis->pDrv->cbScanline;
uint8_t *pu8Dst = pThis->pDrv->pu8Data + y * cbLineDst + x * cbPixelDst;
uint32_t cyLeft = cy;
vga_draw_line_func *pfnVgaDrawLine = vga_draw_line_table[VGA_DRAW_LINE32 * 4 + get_depth_index(pThis->pDrv->cBits)];
Assert(pfnVgaDrawLine);
while (cyLeft-- > 0)
{
pfnVgaDrawLine(pThis, pu8Dst, pu8Src, cx);
pu8Dst += cbLineDst;
pu8Src += cbLineSrc;
}
/*
* Invalidate the area.
*/
pThis->pDrv->pfnUpdateRect(pThis->pDrv, x, y, cx, cy);
}
}
else
rc = VERR_INVALID_PARAMETER;
PDMCritSectLeave(&pThis->lock);
LogFlow(("vgaPortDisplayBlt: returns %Rrc\n", rc));
return rc;
}
static DECLCALLBACK(void) vgaPortUpdateDisplayRect (PPDMIDISPLAYPORT pInterface, int32_t x, int32_t y, uint32_t w, uint32_t h)
{
uint32_t v;
vga_draw_line_func *vga_draw_line;
uint32_t cbPixelDst;
uint32_t cbLineDst;
uint8_t *pu8Dst;
uint32_t cbPixelSrc;
uint32_t cbLineSrc;
uint8_t *pu8Src;
uint32_t u32OffsetSrc, u32Dummy;
PVGASTATE s = IDISPLAYPORT_2_VGASTATE(pInterface);
#ifdef DEBUG_sunlover
LogFlow(("vgaPortUpdateDisplayRect: %d,%d %dx%d\n", x, y, w, h));
#endif /* DEBUG_sunlover */
Assert(pInterface);
Assert(s->pDrv);
Assert(s->pDrv->pu8Data);
/* Check if there is something to do at all. */
if (!s->fRenderVRAM)
{
/* The framebuffer uses the guest VRAM directly. */
#ifdef DEBUG_sunlover
LogFlow(("vgaPortUpdateDisplayRect: nothing to do fRender is false.\n"));
#endif /* DEBUG_sunlover */
return;
}
int rc = PDMCritSectEnter(&s->lock, VERR_SEM_BUSY);
AssertRC(rc);
/* Correct negative x and y coordinates. */
if (x < 0)
{
x += w; /* Compute xRight which is also the new width. */
w = (x < 0) ? 0 : x;
x = 0;
}
if (y < 0)
{
y += h; /* Compute yBottom, which is also the new height. */
h = (y < 0) ? 0 : y;
y = 0;
}
/* Also check if coords are greater than the display resolution. */
if (x + w > s->pDrv->cx)
{
// x < 0 is not possible here
w = s->pDrv->cx > (uint32_t)x? s->pDrv->cx - x: 0;
}
if (y + h > s->pDrv->cy)
{
// y < 0 is not possible here
h = s->pDrv->cy > (uint32_t)y? s->pDrv->cy - y: 0;
}
#ifdef DEBUG_sunlover
LogFlow(("vgaPortUpdateDisplayRect: %d,%d %dx%d (corrected coords)\n", x, y, w, h));
#endif /* DEBUG_sunlover */
/* Check if there is something to do at all. */
if (w == 0 || h == 0)
{
/* Empty rectangle. */
#ifdef DEBUG_sunlover
LogFlow(("vgaPortUpdateDisplayRect: nothing to do: %dx%d\n", w, h));
#endif /* DEBUG_sunlover */
PDMCritSectLeave(&s->lock);
return;
}
/** @todo This method should be made universal and not only for VBVA.
* VGA_DRAW_LINE* must be selected and src/dst address calculation
* changed.
*/
/* Choose the rendering function. */
switch(s->get_bpp(s))
{
default:
case 0:
/* A LFB mode is already disabled, but the callback is still called
* by Display because VBVA buffer is being flushed.
* Nothing to do, just return.
*/
PDMCritSectLeave(&s->lock);
return;
case 8:
v = VGA_DRAW_LINE8;
break;
case 15:
v = VGA_DRAW_LINE15;
break;
case 16:
v = VGA_DRAW_LINE16;
break;
case 24:
v = VGA_DRAW_LINE24;
break;
case 32:
v = VGA_DRAW_LINE32;
break;
}
vga_draw_line = vga_draw_line_table[v * 4 + get_depth_index(s->pDrv->cBits)];
/* Compute source and destination addresses and pitches. */
cbPixelDst = (s->pDrv->cBits + 7) / 8;
cbLineDst = s->pDrv->cbScanline;
pu8Dst = s->pDrv->pu8Data + y * cbLineDst + x * cbPixelDst;
cbPixelSrc = (s->get_bpp(s) + 7) / 8;
s->get_offsets (s, &cbLineSrc, &u32OffsetSrc, &u32Dummy);
/* Assume that rendering is performed only on visible part of VRAM.
* This is true because coordinates were verified.
*/
pu8Src = s->vram_ptrR3;
pu8Src += u32OffsetSrc * 4 + y * cbLineSrc + x * cbPixelSrc;
/* Render VRAM to framebuffer. */
#ifdef DEBUG_sunlover
LogFlow(("vgaPortUpdateDisplayRect: dst: %p, %d, %d. src: %p, %d, %d\n", pu8Dst, cbLineDst, cbPixelDst, pu8Src, cbLineSrc, cbPixelSrc));
#endif /* DEBUG_sunlover */
while (h-- > 0)
{
vga_draw_line (s, pu8Dst, pu8Src, w);
pu8Dst += cbLineDst;
pu8Src += cbLineSrc;
}
PDMCritSectLeave(&s->lock);
#ifdef DEBUG_sunlover
LogFlow(("vgaPortUpdateDisplayRect: completed.\n"));
#endif /* DEBUG_sunlover */
}
static DECLCALLBACK(int) vgaPortCopyRect (PPDMIDISPLAYPORT pInterface,
uint32_t w,
uint32_t h,
const uint8_t *pu8Src,
int32_t xSrc,
int32_t ySrc,
uint32_t u32SrcWidth,
uint32_t u32SrcHeight,
uint32_t u32SrcLineSize,
uint32_t u32SrcBitsPerPixel,
uint8_t *pu8Dst,
int32_t xDst,
int32_t yDst,
uint32_t u32DstWidth,
uint32_t u32DstHeight,
uint32_t u32DstLineSize,
uint32_t u32DstBitsPerPixel)
{
uint32_t v;
vga_draw_line_func *vga_draw_line;
uint32_t cbPixelDst;
uint32_t cbLineDst;
uint8_t *pu8DstPtr;
uint32_t cbPixelSrc;
uint32_t cbLineSrc;
const uint8_t *pu8SrcPtr;
#ifdef DEBUG_sunlover
LogFlow(("vgaPortCopyRect: %d,%d %dx%d -> %d,%d\n", xSrc, ySrc, w, h, xDst, yDst));
#endif /* DEBUG_sunlover */
PVGASTATE s = IDISPLAYPORT_2_VGASTATE(pInterface);
Assert(pInterface);
Assert(s->pDrv);
int32_t xSrcCorrected = xSrc;
int32_t ySrcCorrected = ySrc;
uint32_t wCorrected = w;
uint32_t hCorrected = h;
/* Correct source coordinates to be within the source bitmap. */
if (xSrcCorrected < 0)
{
xSrcCorrected += wCorrected; /* Compute xRight which is also the new width. */
wCorrected = (xSrcCorrected < 0) ? 0 : xSrcCorrected;
xSrcCorrected = 0;
}
if (ySrcCorrected < 0)
{
ySrcCorrected += hCorrected; /* Compute yBottom, which is also the new height. */
hCorrected = (ySrcCorrected < 0) ? 0 : ySrcCorrected;
ySrcCorrected = 0;
}
/* Also check if coords are greater than the display resolution. */
if (xSrcCorrected + wCorrected > u32SrcWidth)
{
/* xSrcCorrected < 0 is not possible here */
wCorrected = u32SrcWidth > (uint32_t)xSrcCorrected? u32SrcWidth - xSrcCorrected: 0;
}
if (ySrcCorrected + hCorrected > u32SrcHeight)
{
/* y < 0 is not possible here */
hCorrected = u32SrcHeight > (uint32_t)ySrcCorrected? u32SrcHeight - ySrcCorrected: 0;
}
#ifdef DEBUG_sunlover
LogFlow(("vgaPortCopyRect: %d,%d %dx%d (corrected coords)\n", xSrcCorrected, ySrcCorrected, wCorrected, hCorrected));
#endif /* DEBUG_sunlover */
/* Check if there is something to do at all. */
if (wCorrected == 0 || hCorrected == 0)
{
/* Empty rectangle. */
#ifdef DEBUG_sunlover
LogFlow(("vgaPortUpdateDisplayRectEx: nothing to do: %dx%d\n", wCorrected, hCorrected));
#endif /* DEBUG_sunlover */
return VINF_SUCCESS;
}
/* Check that the corrected source rectangle is within the destination.
* Note: source rectangle is adjusted, but the target must be large enough.
*/
if ( xDst < 0
|| yDst < 0
|| xDst + wCorrected > u32DstWidth
|| yDst + hCorrected > u32DstHeight)
{
return VERR_INVALID_PARAMETER;
}
/* Choose the rendering function. */
switch(u32SrcBitsPerPixel)
{
default:
case 0:
/* Nothing to do, just return. */
return VINF_SUCCESS;
case 8:
v = VGA_DRAW_LINE8;
break;
case 15:
v = VGA_DRAW_LINE15;
break;
case 16:
v = VGA_DRAW_LINE16;
break;
case 24:
v = VGA_DRAW_LINE24;
break;
case 32:
v = VGA_DRAW_LINE32;
break;
}
int rc = PDMCritSectEnter(&s->lock, VERR_SEM_BUSY);
AssertRC(rc);
vga_draw_line = vga_draw_line_table[v * 4 + get_depth_index(u32DstBitsPerPixel)];
/* Compute source and destination addresses and pitches. */
cbPixelDst = (u32DstBitsPerPixel + 7) / 8;
cbLineDst = u32DstLineSize;
pu8DstPtr = pu8Dst + yDst * cbLineDst + xDst * cbPixelDst;
cbPixelSrc = (u32SrcBitsPerPixel + 7) / 8;
cbLineSrc = u32SrcLineSize;
pu8SrcPtr = pu8Src + ySrcCorrected * cbLineSrc + xSrcCorrected * cbPixelSrc;
#ifdef DEBUG_sunlover
LogFlow(("vgaPortCopyRect: dst: %p, %d, %d. src: %p, %d, %d\n", pu8DstPtr, cbLineDst, cbPixelDst, pu8SrcPtr, cbLineSrc, cbPixelSrc));
#endif /* DEBUG_sunlover */
while (hCorrected-- > 0)
{
vga_draw_line (s, pu8DstPtr, pu8SrcPtr, wCorrected);
pu8DstPtr += cbLineDst;
pu8SrcPtr += cbLineSrc;
}
PDMCritSectLeave(&s->lock);
#ifdef DEBUG_sunlover
LogFlow(("vgaPortCopyRect: completed.\n"));
#endif /* DEBUG_sunlover */
return VINF_SUCCESS;
}
static DECLCALLBACK(void) vgaPortSetRenderVRAM(PPDMIDISPLAYPORT pInterface, bool fRender)
{
PVGASTATE s = IDISPLAYPORT_2_VGASTATE(pInterface);
LogFlow(("vgaPortSetRenderVRAM: fRender = %d\n", fRender));
s->fRenderVRAM = fRender;
}
static DECLCALLBACK(void) vgaTimerRefresh(PPDMDEVINS pDevIns, PTMTIMER pTimer, void *pvUser)
{
PVGASTATE pThis = (PVGASTATE)pvUser;
if (pThis->pDrv)
pThis->pDrv->pfnRefresh(pThis->pDrv);
if (pThis->cMilliesRefreshInterval)
TMTimerSetMillies(pTimer, pThis->cMilliesRefreshInterval);
}
/* -=-=-=-=-=- Ring 3: PCI Device -=-=-=-=-=- */
/**
* Callback function for unmapping and/or mapping the VRAM MMIO2 region (called by the PCI bus).
*
* @return VBox status code.
* @param pPciDev Pointer to PCI device. Use pPciDev->pDevIns to get the device instance.
* @param iRegion The region number.
* @param GCPhysAddress Physical address of the region. If iType is PCI_ADDRESS_SPACE_IO, this is an
* I/O port, else it's a physical address.
* This address is *NOT* relative to pci_mem_base like earlier!
* @param enmType One of the PCI_ADDRESS_SPACE_* values.
*/
static DECLCALLBACK(int) vgaR3IORegionMap(PPCIDEVICE pPciDev, /*unsigned*/ int iRegion, RTGCPHYS GCPhysAddress, uint32_t cb, PCIADDRESSSPACE enmType)
{
int rc;
PPDMDEVINS pDevIns = pPciDev->pDevIns;
PVGASTATE pThis = PDMINS_2_DATA(pDevIns, PVGASTATE);
LogFlow(("vgaR3IORegionMap: iRegion=%d GCPhysAddress=%RGp cb=%#x enmType=%d\n", iRegion, GCPhysAddress, cb, enmType));
AssertReturn(iRegion == 0 && enmType == PCI_ADDRESS_SPACE_MEM_PREFETCH, VERR_INTERNAL_ERROR);
if (GCPhysAddress != NIL_RTGCPHYS)
{
/*
* Mapping the VRAM.
*/
rc = PDMDevHlpMMIO2Map(pDevIns, iRegion, GCPhysAddress);
AssertRC(rc);
if (RT_SUCCESS(rc))
{
rc = PGMR3HandlerPhysicalRegister(PDMDevHlpGetVM(pDevIns),
PGMPHYSHANDLERTYPE_PHYSICAL_WRITE,
GCPhysAddress, GCPhysAddress + (pThis->vram_size - 1),
vgaR3LFBAccessHandler, pThis,
g_DeviceVga.szR0Mod, "vgaR0LFBAccessHandler", pDevIns->pvInstanceDataR0,
g_DeviceVga.szRCMod, "vgaGCLFBAccessHandler", pDevIns->pvInstanceDataRC,
"VGA LFB");
AssertRC(rc);
if (RT_SUCCESS(rc))
pThis->GCPhysVRAM = GCPhysAddress;
}
}
else
{
/*
* Unmapping of the VRAM in progress.
* Deregister the access handler so PGM doesn't get upset.
*/
Assert(pThis->GCPhysVRAM);
rc = PGMHandlerPhysicalDeregister(PDMDevHlpGetVM(pDevIns), pThis->GCPhysVRAM);
AssertRC(rc);
pThis->GCPhysVRAM = 0;
}
return rc;
}
/* -=-=-=-=-=- Ring3: Misc Wrappers & Sidekicks -=-=-=-=-=- */
/**
* Saves a important bits of the VGA device config.
*
* @param pThis The VGA instance data.
* @param pSSM The saved state handle.
*/
static void vgaR3SaveConfig(PVGASTATE pThis, PSSMHANDLE pSSM)
{
SSMR3PutU32(pSSM, pThis->vram_size);
SSMR3PutU32(pSSM, pThis->cMonitors);
}
/**
* @copydoc FNSSMDEVLIVEEXEC
*/
static DECLCALLBACK(int) vgaR3LiveExec(PPDMDEVINS pDevIns, PSSMHANDLE pSSM, uint32_t uPass)
{
PVGASTATE pThis = PDMINS_2_DATA(pDevIns, PVGASTATE);
Assert(uPass == 0); NOREF(uPass);
vgaR3SaveConfig(pThis, pSSM);
return VINF_SSM_DONT_CALL_AGAIN;
}
/**
* @copydoc FNSSMDEVSAVEPREP
*/
static DECLCALLBACK(int) vgaR3SavePrep(PPDMDEVINS pDevIns, PSSMHANDLE pSSM)
{
#ifdef VBOX_WITH_VIDEOHWACCEL
return vboxVBVASaveStatePrep(pDevIns, pSSM);
#else
return VINF_SUCCESS;
#endif
}
static DECLCALLBACK(int) vgaR3SaveDone(PPDMDEVINS pDevIns, PSSMHANDLE pSSM)
{
#ifdef VBOX_WITH_VIDEOHWACCEL
return vboxVBVASaveStateDone(pDevIns, pSSM);
#else
return VINF_SUCCESS;
#endif
}
/**
* @copydoc FNSSMDEVSAVEEXEC
*/
static DECLCALLBACK(int) vgaR3SaveExec(PPDMDEVINS pDevIns, PSSMHANDLE pSSM)
{
PVGASTATE pThis = PDMINS_2_DATA(pDevIns, PVGASTATE);
#ifdef VBOX_WITH_VDMA
vboxVDMASaveStateExecPrep(pThis->pVdma, pSSM);
#endif
vgaR3SaveConfig(pThis, pSSM);
vga_save(pSSM, PDMINS_2_DATA(pDevIns, PVGASTATE));
#ifdef VBOX_WITH_HGSMI
SSMR3PutBool(pSSM, true);
int rc = vboxVBVASaveStateExec(pDevIns, pSSM);
# ifdef VBOX_WITH_VDMA
vboxVDMASaveStateExecDone(pThis->pVdma, pSSM);
# endif
return rc;
#else
SSMR3PutBool(pSSM, false);
return VINF_SUCCESS;
#endif
}
/**
* @copydoc FNSSMDEVSAVEEXEC
*/
static DECLCALLBACK(int) vgaR3LoadExec(PPDMDEVINS pDevIns, PSSMHANDLE pSSM, uint32_t uVersion, uint32_t uPass)
{
PVGASTATE pThis = PDMINS_2_DATA(pDevIns, PVGASTATE);
int rc;
if (uVersion < VGA_SAVEDSTATE_VERSION_ANCIENT || uVersion > VGA_SAVEDSTATE_VERSION)
return VERR_SSM_UNSUPPORTED_DATA_UNIT_VERSION;
if (uVersion > VGA_SAVEDSTATE_VERSION_HGSMI)
{
/* Check the config */
uint32_t cbVRam;
rc = SSMR3GetU32(pSSM, &cbVRam);
AssertRCReturn(rc, rc);
if (pThis->vram_size != cbVRam)
return SSMR3SetCfgError(pSSM, RT_SRC_POS, N_("VRAM size changed: config=%#x state=%#x"), pThis->vram_size, cbVRam);
uint32_t cMonitors;
rc = SSMR3GetU32(pSSM, &cMonitors);
AssertRCReturn(rc, rc);
if (pThis->cMonitors != cMonitors)
return SSMR3SetCfgError(pSSM, RT_SRC_POS, N_("Monitor count changed: config=%u state=%u"), pThis->cMonitors, cMonitors);
}
if (uPass == SSM_PASS_FINAL)
{
rc = vga_load(pSSM, pThis, uVersion);
if (RT_FAILURE(rc))
return rc;
bool fWithHgsmi = uVersion == VGA_SAVEDSTATE_VERSION_HGSMI;
if (uVersion > VGA_SAVEDSTATE_VERSION_HGSMI)
{
rc = SSMR3GetBool(pSSM, &fWithHgsmi);
AssertRCReturn(rc, rc);
}
if (fWithHgsmi)
{
#ifdef VBOX_WITH_HGSMI
rc = vboxVBVALoadStateExec(pDevIns, pSSM, uVersion);
AssertRCReturn(rc, rc);
#else
return SSMR3SetCfgError(pSSM, RT_SRC_POS, N_("HGSMI is not compiled in, but it is present in the saved state"));
#endif
}
}
return VINF_SUCCESS;
}
/**
* @copydoc FNSSMDEVLOADDONE
*/
static DECLCALLBACK(int) vgaR3LoadDone(PPDMDEVINS pDevIns, PSSMHANDLE pSSM)
{
#ifdef VBOX_WITH_HGSMI
return vboxVBVALoadStateDone(pDevIns, pSSM);
#else
return VINF_SUCCESS;
#endif
}
/* -=-=-=-=-=- Ring 3: Device callbacks -=-=-=-=-=- */
/**
* Reset notification.
*
* @returns VBox status.
* @param pDevIns The device instance data.
*/
static DECLCALLBACK(void) vgaR3Reset(PPDMDEVINS pDevIns)
{
PVGASTATE pThis = PDMINS_2_DATA(pDevIns, PVGASTATE);
char *pchStart;
char *pchEnd;
LogFlow(("vgaReset\n"));
#ifdef VBOX_WITH_HGSMI
VBVAReset(pThis);
#endif /* VBOX_WITH_HGSMI */
/* Clear the VRAM ourselves. */
if (pThis->vram_ptrR3 && pThis->vram_size)
memset(pThis->vram_ptrR3, 0, pThis->vram_size);
/*
* Zero most of it.
*
* Unlike vga_reset we're leaving out a few members which we believe
* must remain unchanged....
*/
/* 1st part. */
pchStart = (char *)&pThis->latch;
pchEnd = (char *)&pThis->invalidated_y_table;
memset(pchStart, 0, pchEnd - pchStart);
/* 2nd part. */
pchStart = (char *)&pThis->last_palette;
pchEnd = (char *)&pThis->u32Marker;
memset(pchStart, 0, pchEnd - pchStart);
/*
* Restore and re-init some bits.
*/
pThis->get_bpp = vga_get_bpp;
pThis->get_offsets = vga_get_offsets;
pThis->get_resolution = vga_get_resolution;
pThis->graphic_mode = -1; /* Force full update. */
#ifdef CONFIG_BOCHS_VBE
pThis->vbe_regs[VBE_DISPI_INDEX_ID] = VBE_DISPI_ID0;
pThis->vbe_regs[VBE_DISPI_INDEX_VBOX_VIDEO] = 0;
pThis->vbe_bank_max = (pThis->vram_size >> 16) - 1;
#endif /* CONFIG_BOCHS_VBE */
/*
* Reset the LBF mapping.
*/
pThis->fLFBUpdated = false;
if ( ( pThis->fGCEnabled
|| pThis->fR0Enabled)
&& pThis->GCPhysVRAM
&& pThis->GCPhysVRAM != NIL_RTGCPHYS32)
{
int rc = PGMHandlerPhysicalReset(PDMDevHlpGetVM(pDevIns), pThis->GCPhysVRAM);
AssertRC(rc);
}
if (pThis->fRemappedVGA)
{
IOMMMIOResetRegion(PDMDevHlpGetVM(pDevIns), 0x000a0000);
pThis->fRemappedVGA = false;
}
/*
* Reset the logo data.
*/
pThis->LogoCommand = LOGO_CMD_NOP;
pThis->offLogoData = 0;
/* notify port handler */
if (pThis->pDrv)
pThis->pDrv->pfnReset(pThis->pDrv);
/* Reset latched access mask. */
pThis->uMaskLatchAccess = 0x3ff;
pThis->cLatchAccesses = 0;
pThis->u64LastLatchedAccess = 0;
pThis->iMask = 0;
/* Reset retrace emulation. */
memset(&pThis->retrace_state, 0, sizeof(pThis->retrace_state));
}
/**
* Device relocation callback.
*
* @param pDevIns Pointer to the device instance.
* @param offDelta The relocation delta relative to the old location.
*
* @see FNPDMDEVRELOCATE for details.
*/
static DECLCALLBACK(void) vgaR3Relocate(PPDMDEVINS pDevIns, RTGCINTPTR offDelta)
{
if (offDelta)
{
PVGASTATE pThis = PDMINS_2_DATA(pDevIns, PVGASTATE);
LogFlow(("vgaRelocate: offDelta = %08X\n", offDelta));
pThis->RCPtrLFBHandler += offDelta;
pThis->vram_ptrRC += offDelta;
pThis->pDevInsRC = PDMDEVINS_2_RCPTR(pDevIns);
}
}
/**
* Attach command.
*
* This is called to let the device attach to a driver for a specified LUN
* during runtime. This is not called during VM construction, the device
* constructor have to attach to all the available drivers.
*
* This is like plugging in the monitor after turning on the PC.
*
* @returns VBox status code.
* @param pDevIns The device instance.
* @param iLUN The logical unit which is being detached.
* @param fFlags Flags, combination of the PDMDEVATT_FLAGS_* \#defines.
*/
static DECLCALLBACK(int) vgaAttach(PPDMDEVINS pDevIns, unsigned iLUN, uint32_t fFlags)
{
PVGASTATE pThis = PDMINS_2_DATA(pDevIns, PVGASTATE);
AssertMsgReturn(fFlags & PDM_TACH_FLAGS_NOT_HOT_PLUG,
("VGA device does not support hotplugging\n"),
VERR_INVALID_PARAMETER);
switch (iLUN)
{
/* LUN #0: Display port. */
case 0:
{
int rc = PDMDevHlpDriverAttach(pDevIns, iLUN, &pThis->IBase, &pThis->pDrvBase, "Display Port");
if (RT_SUCCESS(rc))
{
pThis->pDrv = PDMIBASE_QUERY_INTERFACE(pThis->pDrvBase, PDMIDISPLAYCONNECTOR);
if (pThis->pDrv)
{
/* pThis->pDrv->pu8Data can be NULL when there is no framebuffer. */
if ( pThis->pDrv->pfnRefresh
&& pThis->pDrv->pfnResize
&& pThis->pDrv->pfnUpdateRect)
rc = VINF_SUCCESS;
else
{
Assert(pThis->pDrv->pfnRefresh);
Assert(pThis->pDrv->pfnResize);
Assert(pThis->pDrv->pfnUpdateRect);
pThis->pDrv = NULL;
pThis->pDrvBase = NULL;
rc = VERR_INTERNAL_ERROR;
}
#ifdef VBOX_WITH_VIDEOHWACCEL
if(rc == VINF_SUCCESS)
{
rc = vbvaVHWAConstruct(pThis);
if (rc != VERR_NOT_IMPLEMENTED)
AssertRC(rc);
}
#endif
}
else
{
AssertMsgFailed(("LUN #0 doesn't have a display connector interface! rc=%Rrc\n", rc));
pThis->pDrvBase = NULL;
rc = VERR_PDM_MISSING_INTERFACE;
}
}
else if (rc == VERR_PDM_NO_ATTACHED_DRIVER)
{
Log(("%s/%d: warning: no driver attached to LUN #0!\n", pDevIns->pReg->szName, pDevIns->iInstance));
rc = VINF_SUCCESS;
}
else
AssertLogRelMsgFailed(("Failed to attach LUN #0! rc=%Rrc\n", rc));
return rc;
}
default:
AssertMsgFailed(("Invalid LUN #%d\n", iLUN));
return VERR_PDM_NO_SUCH_LUN;
}
}
/**
* Detach notification.
*
* This is called when a driver is detaching itself from a LUN of the device.
* The device should adjust it's state to reflect this.
*
* This is like unplugging the monitor while the PC is still running.
*
* @param pDevIns The device instance.
* @param iLUN The logical unit which is being detached.
* @param fFlags Flags, combination of the PDMDEVATT_FLAGS_* \#defines.
*/
static DECLCALLBACK(void) vgaDetach(PPDMDEVINS pDevIns, unsigned iLUN, uint32_t fFlags)
{
/*
* Reset the interfaces and update the controller state.
*/
PVGASTATE pThis = PDMINS_2_DATA(pDevIns, PVGASTATE);
AssertMsg(fFlags & PDM_TACH_FLAGS_NOT_HOT_PLUG,
("VGA device does not support hotplugging\n"));
switch (iLUN)
{
/* LUN #0: Display port. */
case 0:
pThis->pDrv = NULL;
pThis->pDrvBase = NULL;
break;
default:
AssertMsgFailed(("Invalid LUN #%d\n", iLUN));
break;
}
}
/**
* Destruct a device instance.
*
* Most VM resources are freed by the VM. This callback is provided so that any non-VM
* resources can be freed correctly.
*
* @param pDevIns The device instance data.
*/
static DECLCALLBACK(int) vgaR3Destruct(PPDMDEVINS pDevIns)
{
PDMDEV_CHECK_VERSIONS_RETURN_QUIET(pDevIns);
#ifdef VBE_NEW_DYN_LIST
PVGASTATE pThis = PDMINS_2_DATA(pDevIns, PVGASTATE);
LogFlow(("vgaR3Destruct:\n"));
#ifdef VBOX_WITH_VDMA
vboxVDMADestruct(pThis->pVdma);
#endif
/*
* Free MM heap pointers.
*/
if (pThis->pu8VBEExtraData)
{
MMR3HeapFree(pThis->pu8VBEExtraData);
pThis->pu8VBEExtraData = NULL;
}
#endif
if (pThis->pu8VgaBios)
{
MMR3HeapFree(pThis->pu8VgaBios);
pThis->pu8VgaBios = NULL;
}
if (pThis->pszVgaBiosFile)
{
MMR3HeapFree(pThis->pszVgaBiosFile);
pThis->pszVgaBiosFile = NULL;
}
if (pThis->pszLogoFile)
{
MMR3HeapFree(pThis->pszLogoFile);
pThis->pszLogoFile = NULL;
}
PDMR3CritSectDelete(&pThis->lock);
return VINF_SUCCESS;
}
/**
* Adjust VBE mode information
*
* Depending on the configured VRAM size, certain parts of VBE mode
* information must be updated.
*
* @param pThis The device instance data.
* @param pMode The mode information structure.
*/
static void vgaAdjustModeInfo(PVGASTATE pThis, ModeInfoListItem *pMode)
{
int maxPage;
int bpl;
/* For 4bpp modes, the planes are "stacked" on top of each other. */
bpl = pMode->info.BytesPerScanLine * pMode->info.NumberOfPlanes;
/* The "number of image pages" is really the max page index... */
maxPage = pThis->vram_size / (pMode->info.YResolution * bpl) - 1;
Assert(maxPage >= 0);
if (maxPage > 255)
maxPage = 255; /* 8-bit value. */
pMode->info.NumberOfImagePages = maxPage;
pMode->info.LinNumberOfPages = maxPage;
}
/**
* @interface_method_impl{PDMDEVREG,pfnConstruct}
*/
static DECLCALLBACK(int) vgaR3Construct(PPDMDEVINS pDevIns, int iInstance, PCFGMNODE pCfg)
{
static bool s_fExpandDone = false;
int rc;
unsigned i;
#ifdef VBE_NEW_DYN_LIST
uint32_t cCustomModes;
uint32_t cyReduction;
uint32_t cbPitch;
PVBEHEADER pVBEDataHdr;
ModeInfoListItem *pCurMode;
unsigned cb;
#endif
PDMDEV_CHECK_VERSIONS_RETURN(pDevIns);
PVGASTATE pThis = PDMINS_2_DATA(pDevIns, PVGASTATE);
PVM pVM = PDMDevHlpGetVM(pDevIns);
Assert(iInstance == 0);
Assert(pVM);
/*
* Init static data.
*/
if (!s_fExpandDone)
{
s_fExpandDone = true;
vga_init_expand();
}
/*
* Validate configuration.
*/
if (!CFGMR3AreValuesValid(pCfg, "VRamSize\0"
"MonitorCount\0"
"GCEnabled\0"
"R0Enabled\0"
"FadeIn\0"
"FadeOut\0"
"LogoTime\0"
"LogoFile\0"
"ShowBootMenu\0"
"BiosRom\0"
"RealRetrace\0"
"CustomVideoModes\0"
"HeightReduction\0"
"CustomVideoMode1\0"
"CustomVideoMode2\0"
"CustomVideoMode3\0"
"CustomVideoMode4\0"
"CustomVideoMode5\0"
"CustomVideoMode6\0"
"CustomVideoMode7\0"
"CustomVideoMode8\0"
"CustomVideoMode9\0"
"CustomVideoMode10\0"
"CustomVideoMode11\0"
"CustomVideoMode12\0"
"CustomVideoMode13\0"
"CustomVideoMode14\0"
"CustomVideoMode15\0"
"CustomVideoMode16\0"
"MaxBiosXRes\0"
"MaxBiosYRes\0"))
return PDMDEV_SET_ERROR(pDevIns, VERR_PDM_DEVINS_UNKNOWN_CFG_VALUES,
N_("Invalid configuration for vga device"));
/*
* Init state data.
*/
rc = CFGMR3QueryU32Def(pCfg, "VRamSize", &pThis->vram_size, VGA_VRAM_DEFAULT);
AssertLogRelRCReturn(rc, rc);
if (pThis->vram_size > VGA_VRAM_MAX)
return PDMDevHlpVMSetError(pDevIns, VERR_INVALID_PARAMETER, RT_SRC_POS,
"VRamSize is too large, %#x, max %#x", pThis->vram_size, VGA_VRAM_MAX);
if (pThis->vram_size < VGA_VRAM_MIN)
return PDMDevHlpVMSetError(pDevIns, VERR_INVALID_PARAMETER, RT_SRC_POS,
"VRamSize is too small, %#x, max %#x", pThis->vram_size, VGA_VRAM_MIN);
if (pThis->vram_size & (_256K - 1)) /* Make sure there are no partial banks even in planar modes. */
return PDMDevHlpVMSetError(pDevIns, VERR_INVALID_PARAMETER, RT_SRC_POS,
"VRamSize is not a multiple of 256K (%#x)", pThis->vram_size);
rc = CFGMR3QueryU32Def(pCfg, "MonitorCount", &pThis->cMonitors, 1);
AssertLogRelRCReturn(rc, rc);
rc = CFGMR3QueryBoolDef(pCfg, "GCEnabled", &pThis->fGCEnabled, true);
AssertLogRelRCReturn(rc, rc);
rc = CFGMR3QueryBoolDef(pCfg, "R0Enabled", &pThis->fR0Enabled, true);
AssertLogRelRCReturn(rc, rc);
Log(("VGA: VRamSize=%#x fGCenabled=%RTbool fR0Enabled=%RTbool\n", pThis->vram_size, pThis->fGCEnabled, pThis->fR0Enabled));
pThis->pDevInsR3 = pDevIns;
pThis->pDevInsR0 = PDMDEVINS_2_R0PTR(pDevIns);
pThis->pDevInsRC = PDMDEVINS_2_RCPTR(pDevIns);
vgaR3Reset(pDevIns);
/* The PCI devices configuration. */
PCIDevSetVendorId( &pThis->Dev, 0x80ee); /* PCI vendor, just a free bogus value */
PCIDevSetDeviceId( &pThis->Dev, 0xbeef);
PCIDevSetClassSub( &pThis->Dev, 0x00); /* VGA controller */
PCIDevSetClassBase( &pThis->Dev, 0x03);
PCIDevSetHeaderType(&pThis->Dev, 0x00);
#if defined(VBOX_WITH_HGSMI) && (defined(VBOX_WITH_VIDEOHWACCEL) || defined(VBOX_WITH_VDMA) || defined(VBOX_WITH_WDDM))
PCIDevSetInterruptPin(&pThis->Dev, 1);
#endif
/* The LBF access handler - error handling is better here than in the map function. */
rc = PDMR3LdrGetSymbolRCLazy(pVM, pDevIns->pReg->szRCMod, NULL, "vgaGCLFBAccessHandler", &pThis->RCPtrLFBHandler);
if (RT_FAILURE(rc))
{
AssertReleaseMsgFailed(("PDMR3LdrGetSymbolRC(, %s, \"vgaGCLFBAccessHandler\",) -> %Rrc\n", pDevIns->pReg->szRCMod, rc));
return rc;
}
/* the interfaces. */
pThis->IBase.pfnQueryInterface = vgaPortQueryInterface;
pThis->IPort.pfnUpdateDisplay = vgaPortUpdateDisplay;
pThis->IPort.pfnUpdateDisplayAll = vgaPortUpdateDisplayAll;
pThis->IPort.pfnQueryColorDepth = vgaPortQueryColorDepth;
pThis->IPort.pfnSetRefreshRate = vgaPortSetRefreshRate;
pThis->IPort.pfnTakeScreenshot = vgaPortTakeScreenshot;
pThis->IPort.pfnFreeScreenshot = vgaPortFreeScreenshot;
pThis->IPort.pfnDisplayBlt = vgaPortDisplayBlt;
pThis->IPort.pfnUpdateDisplayRect = vgaPortUpdateDisplayRect;
pThis->IPort.pfnCopyRect = vgaPortCopyRect;
pThis->IPort.pfnSetRenderVRAM = vgaPortSetRenderVRAM;
#if defined(VBOX_WITH_HGSMI)
# if defined(VBOX_WITH_VIDEOHWACCEL)
pThis->IVBVACallbacks.pfnVHWACommandCompleteAsynch = vbvaVHWACommandCompleteAsynch;
# endif
#if defined(VBOX_WITH_CRHGSMI)
pThis->IVBVACallbacks.pfnCrHgsmiCommandCompleteAsync = vboxVDMACrHgsmiCommandCompleteAsync;
pThis->IVBVACallbacks.pfnCrHgsmiControlCompleteAsync = vboxVDMACrHgsmiControlCompleteAsync;
# endif
#endif
/*
* Allocate the VRAM and map the first 512KB of it into GC so we can speed up VGA support.
*/
rc = PDMDevHlpMMIO2Register(pDevIns, 0 /* iRegion */, pThis->vram_size, 0, (void **)&pThis->vram_ptrR3, "VRam");
AssertLogRelMsgRCReturn(rc, ("PDMDevHlpMMIO2Register(%#x,) -> %Rrc\n", pThis->vram_size, rc), rc);
pThis->vram_ptrR0 = (RTR0PTR)pThis->vram_ptrR3; /** @todo #1865 Map parts into R0 or just use PGM access (Mac only). */
if (pThis->fGCEnabled)
{
RTRCPTR pRCMapping = 0;
rc = PDMDevHlpMMHyperMapMMIO2(pDevIns, 0 /* iRegion */, 0 /* off */, VGA_MAPPING_SIZE, "VGA VRam", &pRCMapping);
AssertLogRelMsgRCReturn(rc, ("PDMDevHlpMMHyperMapMMIO2(%#x,) -> %Rrc\n", VGA_MAPPING_SIZE, rc), rc);
pThis->vram_ptrRC = pRCMapping;
}
#if defined(VBOX_WITH_2X_4GB_ADDR_SPACE)
if (pThis->fR0Enabled)
{
RTR0PTR pR0Mapping = 0;
rc = PDMDevHlpMMIO2MapKernel(pDevIns, 0 /* iRegion */, 0 /* off */, VGA_MAPPING_SIZE, "VGA VRam", &pR0Mapping);
AssertLogRelMsgRCReturn(rc, ("PDMDevHlpMapMMIO2IntoR0(%#x,) -> %Rrc\n", VGA_MAPPING_SIZE, rc), rc);
pThis->vram_ptrR0 = pR0Mapping;
}
#endif
/*
* Register I/O ports, ROM and save state.
*/
rc = PDMDevHlpIOPortRegister(pDevIns, 0x3c0, 16, NULL, vgaIOPortWrite, vgaIOPortRead, NULL, NULL, "VGA - 3c0");
if (RT_FAILURE(rc))
return rc;
rc = PDMDevHlpIOPortRegister(pDevIns, 0x3b4, 2, NULL, vgaIOPortWrite, vgaIOPortRead, NULL, NULL, "VGA - 3b4");
if (RT_FAILURE(rc))
return rc;
rc = PDMDevHlpIOPortRegister(pDevIns, 0x3ba, 1, NULL, vgaIOPortWrite, vgaIOPortRead, NULL, NULL, "VGA - 3ba");
if (RT_FAILURE(rc))
return rc;
rc = PDMDevHlpIOPortRegister(pDevIns, 0x3d4, 2, NULL, vgaIOPortWrite, vgaIOPortRead, NULL, NULL, "VGA - 3d4");
if (RT_FAILURE(rc))
return rc;
rc = PDMDevHlpIOPortRegister(pDevIns, 0x3da, 1, NULL, vgaIOPortWrite, vgaIOPortRead, NULL, NULL, "VGA - 3da");
if (RT_FAILURE(rc))
return rc;
#ifdef VBOX_WITH_HGSMI
/* Use reserved VGA IO ports for HGSMI. */
rc = PDMDevHlpIOPortRegister(pDevIns, VGA_PORT_HGSMI_HOST, 4, NULL, vgaR3IOPortHGSMIWrite, vgaR3IOPortHGSMIRead, NULL, NULL, "VGA - 3b0 (HGSMI host)");
if (RT_FAILURE(rc))
return rc;
rc = PDMDevHlpIOPortRegister(pDevIns, VGA_PORT_HGSMI_GUEST, 4, NULL, vgaR3IOPortHGSMIWrite, vgaR3IOPortHGSMIRead, NULL, NULL, "VGA - 3d0 (HGSMI guest)");
if (RT_FAILURE(rc))
return rc;
#endif /* VBOX_WITH_HGSMI */
#ifdef CONFIG_BOCHS_VBE
rc = PDMDevHlpIOPortRegister(pDevIns, 0x1ce, 1, NULL, vgaIOPortWriteVBEIndex, vgaIOPortReadVBEIndex, NULL, NULL, "VGA/VBE - Index");
if (RT_FAILURE(rc))
return rc;
rc = PDMDevHlpIOPortRegister(pDevIns, 0x1cf, 1, NULL, vgaIOPortWriteVBEData, vgaIOPortReadVBEData, NULL, NULL, "VGA/VBE - Data");
if (RT_FAILURE(rc))
return rc;
#endif /* CONFIG_BOCHS_VBE */
/* guest context extension */
if (pThis->fGCEnabled)
{
rc = PDMDevHlpIOPortRegisterRC(pDevIns, 0x3c0, 16, 0, "vgaIOPortWrite", "vgaIOPortRead", NULL, NULL, "VGA - 3c0 (GC)");
if (RT_FAILURE(rc))
return rc;
rc = PDMDevHlpIOPortRegisterRC(pDevIns, 0x3b4, 2, 0, "vgaIOPortWrite", "vgaIOPortRead", NULL, NULL, "VGA - 3b4 (GC)");
if (RT_FAILURE(rc))
return rc;
rc = PDMDevHlpIOPortRegisterRC(pDevIns, 0x3ba, 1, 0, "vgaIOPortWrite", "vgaIOPortRead", NULL, NULL, "VGA - 3ba (GC)");
if (RT_FAILURE(rc))
return rc;
rc = PDMDevHlpIOPortRegisterRC(pDevIns, 0x3d4, 2, 0, "vgaIOPortWrite", "vgaIOPortRead", NULL, NULL, "VGA - 3d4 (GC)");
if (RT_FAILURE(rc))
return rc;
rc = PDMDevHlpIOPortRegisterRC(pDevIns, 0x3da, 1, 0, "vgaIOPortWrite", "vgaIOPortRead", NULL, NULL, "VGA - 3da (GC)");
if (RT_FAILURE(rc))
return rc;
#ifdef CONFIG_BOCHS_VBE
rc = PDMDevHlpIOPortRegisterRC(pDevIns, 0x1ce, 1, 0, "vgaIOPortWriteVBEIndex", "vgaIOPortReadVBEIndex", NULL, NULL, "VGA/VBE - Index (GC)");
if (RT_FAILURE(rc))
return rc;
rc = PDMDevHlpIOPortRegisterRC(pDevIns, 0x1cf, 1, 0, "vgaIOPortWriteVBEData", "vgaIOPortReadVBEData", NULL, NULL, "VGA/VBE - Data (GC)");
if (RT_FAILURE(rc))
return rc;
#endif /* CONFIG_BOCHS_VBE */
}
/* R0 context extension */
if (pThis->fR0Enabled)
{
rc = PDMDevHlpIOPortRegisterR0(pDevIns, 0x3c0, 16, 0, "vgaIOPortWrite", "vgaIOPortRead", NULL, NULL, "VGA - 3c0 (GC)");
if (RT_FAILURE(rc))
return rc;
rc = PDMDevHlpIOPortRegisterR0(pDevIns, 0x3b4, 2, 0, "vgaIOPortWrite", "vgaIOPortRead", NULL, NULL, "VGA - 3b4 (GC)");
if (RT_FAILURE(rc))
return rc;
rc = PDMDevHlpIOPortRegisterR0(pDevIns, 0x3ba, 1, 0, "vgaIOPortWrite", "vgaIOPortRead", NULL, NULL, "VGA - 3ba (GC)");
if (RT_FAILURE(rc))
return rc;
rc = PDMDevHlpIOPortRegisterR0(pDevIns, 0x3d4, 2, 0, "vgaIOPortWrite", "vgaIOPortRead", NULL, NULL, "VGA - 3d4 (GC)");
if (RT_FAILURE(rc))
return rc;
rc = PDMDevHlpIOPortRegisterR0(pDevIns, 0x3da, 1, 0, "vgaIOPortWrite", "vgaIOPortRead", NULL, NULL, "VGA - 3da (GC)");
if (RT_FAILURE(rc))
return rc;
#ifdef CONFIG_BOCHS_VBE
rc = PDMDevHlpIOPortRegisterR0(pDevIns, 0x1ce, 1, 0, "vgaIOPortWriteVBEIndex", "vgaIOPortReadVBEIndex", NULL, NULL, "VGA/VBE - Index (GC)");
if (RT_FAILURE(rc))
return rc;
rc = PDMDevHlpIOPortRegisterR0(pDevIns, 0x1cf, 1, 0, "vgaIOPortWriteVBEData", "vgaIOPortReadVBEData", NULL, NULL, "VGA/VBE - Data (GC)");
if (RT_FAILURE(rc))
return rc;
#endif /* CONFIG_BOCHS_VBE */
}
/* vga mmio */
rc = PDMDevHlpMMIORegister(pDevIns, 0x000a0000, 0x00020000, 0, vgaMMIOWrite, vgaMMIORead, vgaMMIOFill, "VGA - VGA Video Buffer");
if (RT_FAILURE(rc))
return rc;
if (pThis->fGCEnabled)
{
rc = PDMDevHlpMMIORegisterRC(pDevIns, 0x000a0000, 0x00020000, 0, "vgaMMIOWrite", "vgaMMIORead", "vgaMMIOFill");
if (RT_FAILURE(rc))
return rc;
}
if (pThis->fR0Enabled)
{
rc = PDMDevHlpMMIORegisterR0(pDevIns, 0x000a0000, 0x00020000, 0, "vgaMMIOWrite", "vgaMMIORead", "vgaMMIOFill");
if (RT_FAILURE(rc))
return rc;
}
/* vga bios */
rc = PDMDevHlpIOPortRegister(pDevIns, VBE_PRINTF_PORT, 1, NULL, vgaIOPortWriteBIOS, vgaIOPortReadBIOS, NULL, NULL, "VGA BIOS debug/panic");
if (RT_FAILURE(rc))
return rc;
if (pThis->fR0Enabled)
{
rc = PDMDevHlpIOPortRegisterR0(pDevIns, VBE_PRINTF_PORT, 1, 0, "vgaIOPortWriteBIOS", "vgaIOPortReadBIOS", NULL, NULL, "VGA BIOS debug/panic");
if (RT_FAILURE(rc))
return rc;
}
/*
* Get the VGA BIOS ROM file name.
*/
rc = CFGMR3QueryStringAlloc(pCfg, "BiosRom", &pThis->pszVgaBiosFile);
if (rc == VERR_CFGM_VALUE_NOT_FOUND)
{
pThis->pszVgaBiosFile = NULL;
rc = VINF_SUCCESS;
}
else if (RT_FAILURE(rc))
return PDMDEV_SET_ERROR(pDevIns, rc,
N_("Configuration error: Querying \"BiosRom\" as a string failed"));
else if (!*pThis->pszVgaBiosFile)
{
MMR3HeapFree(pThis->pszVgaBiosFile);
pThis->pszVgaBiosFile = NULL;
}
const uint8_t *pu8VgaBiosBinary = NULL;
uint64_t cbVgaBiosBinary;
/*
* Determine the VGA BIOS ROM size, open specified ROM file in the process.
*/
RTFILE FileVgaBios = NIL_RTFILE;
if (pThis->pszVgaBiosFile)
{
rc = RTFileOpen(&FileVgaBios, pThis->pszVgaBiosFile,
RTFILE_O_READ | RTFILE_O_OPEN | RTFILE_O_DENY_WRITE);
if (RT_SUCCESS(rc))
{
rc = RTFileGetSize(FileVgaBios, &pThis->cbVgaBios);
if (RT_SUCCESS(rc))
{
if ( RT_ALIGN(pThis->cbVgaBios, _4K) != pThis->cbVgaBios
|| pThis->cbVgaBios > _64K
|| pThis->cbVgaBios < 16 * _1K)
rc = VERR_TOO_MUCH_DATA;
}
}
if (RT_FAILURE(rc))
{
/*
* In case of failure simply fall back to the built-in VGA BIOS ROM.
*/
Log(("vgaConstruct: Failed to open VGA BIOS ROM file '%s', rc=%Rrc!\n", pThis->pszVgaBiosFile, rc));
RTFileClose(FileVgaBios);
FileVgaBios = NIL_RTFILE;
MMR3HeapFree(pThis->pszVgaBiosFile);
pThis->pszVgaBiosFile = NULL;
}
}
/*
* Attempt to get the VGA BIOS ROM data from file.
*/
if (pThis->pszVgaBiosFile)
{
/*
* Allocate buffer for the VGA BIOS ROM data.
*/
pThis->pu8VgaBios = (uint8_t *)PDMDevHlpMMHeapAlloc(pDevIns, pThis->cbVgaBios);
if (pThis->pu8VgaBios)
{
rc = RTFileRead(FileVgaBios, pThis->pu8VgaBios, pThis->cbVgaBios, NULL);
if (RT_FAILURE(rc))
{
AssertMsgFailed(("RTFileRead(,,%d,NULL) -> %Rrc\n", pThis->cbVgaBios, rc));
MMR3HeapFree(pThis->pu8VgaBios);
pThis->pu8VgaBios = NULL;
}
rc = VINF_SUCCESS;
}
else
rc = VERR_NO_MEMORY;
}
else
pThis->pu8VgaBios = NULL;
/* cleanup */
if (FileVgaBios != NIL_RTFILE)
RTFileClose(FileVgaBios);
/* If we were unable to get the data from file for whatever reason, fall
back to the built-in ROM image. */
uint32_t fFlags = 0;
if (pThis->pu8VgaBios == NULL)
{
pu8VgaBiosBinary = g_abVgaBiosBinary;
cbVgaBiosBinary = g_cbVgaBiosBinary;
fFlags = PGMPHYS_ROM_FLAGS_PERMANENT_BINARY;
}
else
{
pu8VgaBiosBinary = pThis->pu8VgaBios;
cbVgaBiosBinary = pThis->cbVgaBios;
}
AssertReleaseMsg(g_cbVgaBiosBinary <= _64K && g_cbVgaBiosBinary >= 32*_1K, ("g_cbVgaBiosBinary=%#x\n", g_cbVgaBiosBinary));
AssertReleaseMsg(RT_ALIGN_Z(g_cbVgaBiosBinary, PAGE_SIZE) == g_cbVgaBiosBinary, ("g_cbVgaBiosBinary=%#x\n", g_cbVgaBiosBinary));
rc = PDMDevHlpROMRegister(pDevIns, 0x000c0000, cbVgaBiosBinary, pu8VgaBiosBinary, cbVgaBiosBinary,
fFlags, "VGA BIOS");
if (RT_FAILURE(rc))
return rc;
/* save */
rc = PDMDevHlpSSMRegisterEx(pDevIns, VGA_SAVEDSTATE_VERSION, sizeof(*pThis), NULL,
NULL, vgaR3LiveExec, NULL,
vgaR3SavePrep, vgaR3SaveExec, vgaR3SaveDone,
NULL, vgaR3LoadExec, vgaR3LoadDone);
if (RT_FAILURE(rc))
return rc;
/* PCI */
rc = PDMDevHlpPCIRegister(pDevIns, &pThis->Dev);
if (RT_FAILURE(rc))
return rc;
/*AssertMsg(pThis->Dev.devfn == 16 || iInstance != 0, ("pThis->Dev.devfn=%d\n", pThis->Dev.devfn));*/
if (pThis->Dev.devfn != 16 && iInstance == 0)
Log(("!!WARNING!!: pThis->dev.devfn=%d (ignore if testcase or not started by Main)\n", pThis->Dev.devfn));
rc = PDMDevHlpPCIIORegionRegister(pDevIns, 0 /* iRegion */, pThis->vram_size, PCI_ADDRESS_SPACE_MEM_PREFETCH, vgaR3IORegionMap);
if (RT_FAILURE(rc))
return rc;
/* Initialize the PDM lock. */
rc = PDMDevHlpCritSectInit(pDevIns, &pThis->lock, RT_SRC_POS, "VGA");
if (RT_FAILURE(rc))
{
Log(("%s: Failed to create critical section.\n", __FUNCTION__));
return rc;
}
/*
* Create the refresh timer.
*/
rc = PDMDevHlpTMTimerCreate(pDevIns, TMCLOCK_REAL, vgaTimerRefresh,
pThis, TMTIMER_FLAGS_NO_CRIT_SECT,
"VGA Refresh Timer", &pThis->RefreshTimer);
if (RT_FAILURE(rc))
return rc;
/*
* Attach to the display.
*/
rc = vgaAttach(pDevIns, 0 /* display LUN # */, PDM_TACH_FLAGS_NOT_HOT_PLUG);
if (RT_FAILURE(rc))
return rc;
/*
* Initialize the retrace flag.
*/
rc = CFGMR3QueryBoolDef(pCfg, "RealRetrace", &pThis->fRealRetrace, false);
AssertLogRelRCReturn(rc, rc);
#ifdef VBE_NEW_DYN_LIST
uint16_t maxBiosXRes;
rc = CFGMR3QueryU16Def(pCfg, "MaxBiosXRes", &maxBiosXRes, UINT16_MAX);
AssertLogRelRCReturn(rc, rc);
uint16_t maxBiosYRes;
rc = CFGMR3QueryU16Def(pCfg, "MaxBiosYRes", &maxBiosYRes, UINT16_MAX);
AssertLogRelRCReturn(rc, rc);
/*
* Compute buffer size for the VBE BIOS Extra Data.
*/
cb = sizeof(mode_info_list) + sizeof(ModeInfoListItem);
rc = CFGMR3QueryU32(pCfg, "HeightReduction", &cyReduction);
if (RT_SUCCESS(rc) && cyReduction)
cb *= 2; /* Default mode list will be twice long */
else
cyReduction = 0;
rc = CFGMR3QueryU32(pCfg, "CustomVideoModes", &cCustomModes);
if (RT_SUCCESS(rc) && cCustomModes)
cb += sizeof(ModeInfoListItem) * cCustomModes;
else
cCustomModes = 0;
/*
* Allocate and initialize buffer for the VBE BIOS Extra Data.
*/
AssertRelease(sizeof(VBEHEADER) + cb < 65536);
pThis->cbVBEExtraData = (uint16_t)(sizeof(VBEHEADER) + cb);
pThis->pu8VBEExtraData = (uint8_t *)PDMDevHlpMMHeapAllocZ(pDevIns, pThis->cbVBEExtraData);
if (!pThis->pu8VBEExtraData)
return VERR_NO_MEMORY;
pVBEDataHdr = (PVBEHEADER)pThis->pu8VBEExtraData;
pVBEDataHdr->u16Signature = VBEHEADER_MAGIC;
pVBEDataHdr->cbData = cb;
# ifndef VRAM_SIZE_FIX
pCurMode = memcpy(pVBEDataHdr + 1, &mode_info_list, sizeof(mode_info_list));
pCurMode = (ModeInfoListItem *)((uintptr_t)pCurMode + sizeof(mode_info_list));
# else /* VRAM_SIZE_FIX defined */
pCurMode = (ModeInfoListItem *)(pVBEDataHdr + 1);
for (i = 0; i < MODE_INFO_SIZE; i++)
{
uint32_t pixelWidth, reqSize;
if (mode_info_list[i].info.MemoryModel == VBE_MEMORYMODEL_TEXT_MODE)
pixelWidth = 2;
else
pixelWidth = (mode_info_list[i].info.BitsPerPixel +7) / 8;
reqSize = mode_info_list[i].info.XResolution
* mode_info_list[i].info.YResolution
* pixelWidth;
if (reqSize >= pThis->vram_size)
continue;
if ( mode_info_list[i].info.XResolution > maxBiosXRes
|| mode_info_list[i].info.YResolution > maxBiosYRes)
continue;
*pCurMode = mode_info_list[i];
vgaAdjustModeInfo(pThis, pCurMode);
pCurMode++;
}
# endif /* VRAM_SIZE_FIX defined */
/*
* Copy default modes with subtracted YResolution.
*/
if (cyReduction)
{
ModeInfoListItem *pDefMode = mode_info_list;
Log(("vgaR3Construct: cyReduction=%u\n", cyReduction));
# ifndef VRAM_SIZE_FIX
for (i = 0; i < MODE_INFO_SIZE; i++, pCurMode++, pDefMode++)
{
*pCurMode = *pDefMode;
pCurMode->mode += 0x30;
pCurMode->info.YResolution -= cyReduction;
}
# else /* VRAM_SIZE_FIX defined */
for (i = 0; i < MODE_INFO_SIZE; i++, pDefMode++)
{
uint32_t pixelWidth, reqSize;
if (pDefMode->info.MemoryModel == VBE_MEMORYMODEL_TEXT_MODE)
pixelWidth = 2;
else
pixelWidth = (pDefMode->info.BitsPerPixel + 7) / 8;
reqSize = pDefMode->info.XResolution * pDefMode->info.YResolution * pixelWidth;
if (reqSize >= pThis->vram_size)
continue;
if ( pDefMode->info.XResolution > maxBiosXRes
|| pDefMode->info.YResolution - cyReduction > maxBiosYRes)
continue;
*pCurMode = *pDefMode;
pCurMode->mode += 0x30;
pCurMode->info.YResolution -= cyReduction;
pCurMode++;
}
# endif /* VRAM_SIZE_FIX defined */
}
/*
* Add custom modes.
*/
if (cCustomModes)
{
uint16_t u16CurMode = 0x160;
for (i = 1; i <= cCustomModes; i++)
{
char szExtraDataKey[sizeof("CustomVideoModeXX")];
char *pszExtraData = NULL;
/* query and decode the custom mode string. */
RTStrPrintf(szExtraDataKey, sizeof(szExtraDataKey), "CustomVideoMode%d", i);
rc = CFGMR3QueryStringAlloc(pCfg, szExtraDataKey, &pszExtraData);
if (RT_SUCCESS(rc))
{
ModeInfoListItem *pDefMode = mode_info_list;
unsigned int cx, cy, cBits, cParams, j;
uint16_t u16DefMode;
cParams = sscanf(pszExtraData, "%ux%ux%u", &cx, &cy, &cBits);
if ( cParams != 3
|| (cBits != 16 && cBits != 24 && cBits != 32))
{
AssertMsgFailed(("Configuration error: Invalid mode data '%s' for '%s'! cBits=%d\n", pszExtraData, szExtraDataKey, cBits));
return VERR_VGA_INVALID_CUSTOM_MODE;
}
cbPitch = calc_line_pitch(cBits, cx);
# ifdef VRAM_SIZE_FIX
if (cy * cbPitch >= pThis->vram_size)
{
AssertMsgFailed(("Configuration error: custom video mode %dx%dx%dbits is too large for the virtual video memory of %dMb. Please increase the video memory size.\n",
cx, cy, cBits, pThis->vram_size / _1M));
return VERR_VGA_INVALID_CUSTOM_MODE;
}
# endif /* VRAM_SIZE_FIX defined */
MMR3HeapFree(pszExtraData);
/* Use defaults from max@bpp mode. */
switch (cBits)
{
case 16:
u16DefMode = VBE_VESA_MODE_1024X768X565;
break;
case 24:
u16DefMode = VBE_VESA_MODE_1024X768X888;
break;
case 32:
u16DefMode = VBE_OWN_MODE_1024X768X8888;
break;
default: /* gcc, shut up! */
AssertMsgFailed(("gone postal!\n"));
continue;
}
/* mode_info_list is not terminated */
for (j = 0; j < MODE_INFO_SIZE && pDefMode->mode != u16DefMode; j++)
pDefMode++;
Assert(j < MODE_INFO_SIZE);
*pCurMode = *pDefMode;
pCurMode->mode = u16CurMode++;
/* adjust defaults */
pCurMode->info.XResolution = cx;
pCurMode->info.YResolution = cy;
pCurMode->info.BytesPerScanLine = cbPitch;
pCurMode->info.LinBytesPerScanLine = cbPitch;
vgaAdjustModeInfo(pThis, pCurMode);
/* commit it */
pCurMode++;
}
else if (rc != VERR_CFGM_VALUE_NOT_FOUND)
{
AssertMsgFailed(("CFGMR3QueryStringAlloc(,'%s',) -> %Rrc\n", szExtraDataKey, rc));
return rc;
}
} /* foreach custom mode key */
}
/*
* Add the "End of list" mode.
*/
memset(pCurMode, 0, sizeof(*pCurMode));
pCurMode->mode = VBE_VESA_MODE_END_OF_LIST;
/*
* Register I/O Port for the VBE BIOS Extra Data.
*/
rc = PDMDevHlpIOPortRegister(pDevIns, VBE_EXTRA_PORT, 1, NULL, vbeIOPortWriteVBEExtra, vbeIOPortReadVBEExtra, NULL, NULL, "VBE BIOS Extra Data");
if (RT_FAILURE(rc))
return rc;
#endif /* VBE_NEW_DYN_LIST */
/*
* Register I/O Port for the BIOS Logo.
*/
rc = PDMDevHlpIOPortRegister(pDevIns, LOGO_IO_PORT, 1, NULL, vbeIOPortWriteCMDLogo, vbeIOPortReadCMDLogo, NULL, NULL, "BIOS Logo");
if (RT_FAILURE(rc))
return rc;
/*
* Register debugger info callbacks.
*/
PDMDevHlpDBGFInfoRegister(pDevIns, "vga", "Display basic VGA state.", vgaInfoState);
PDMDevHlpDBGFInfoRegister(pDevIns, "vgatext", "Display VGA memory formatted as text.", vgaInfoText);
PDMDevHlpDBGFInfoRegister(pDevIns, "vgacr", "Dump VGA CRTC registers.", vgaInfoCR);
PDMDevHlpDBGFInfoRegister(pDevIns, "vgagr", "Dump VGA Graphics Controller registers.", vgaInfoGR);
PDMDevHlpDBGFInfoRegister(pDevIns, "vgasr", "Dump VGA Sequencer registers.", vgaInfoSR);
PDMDevHlpDBGFInfoRegister(pDevIns, "vgaar", "Dump VGA Attribute Controller registers.", vgaInfoAR);
PDMDevHlpDBGFInfoRegister(pDevIns, "vgadac", "Dump VGA DAC registers.", vgaInfoDAC);
PDMDevHlpDBGFInfoRegister(pDevIns, "vbe", "Dump VGA VBE registers.", vgaInfoVBE);
/*
* Construct the logo header.
*/
LOGOHDR LogoHdr = { LOGO_HDR_MAGIC, 0, 0, 0, 0, 0, 0 };
rc = CFGMR3QueryU8(pCfg, "FadeIn", &LogoHdr.fu8FadeIn);
if (rc == VERR_CFGM_VALUE_NOT_FOUND)
LogoHdr.fu8FadeIn = 1;
else if (RT_FAILURE(rc))
return PDMDEV_SET_ERROR(pDevIns, rc,
N_("Configuration error: Querying \"FadeIn\" as integer failed"));
rc = CFGMR3QueryU8(pCfg, "FadeOut", &LogoHdr.fu8FadeOut);
if (rc == VERR_CFGM_VALUE_NOT_FOUND)
LogoHdr.fu8FadeOut = 1;
else if (RT_FAILURE(rc))
return PDMDEV_SET_ERROR(pDevIns, rc,
N_("Configuration error: Querying \"FadeOut\" as integer failed"));
rc = CFGMR3QueryU16(pCfg, "LogoTime", &LogoHdr.u16LogoMillies);
if (rc == VERR_CFGM_VALUE_NOT_FOUND)
LogoHdr.u16LogoMillies = 0;
else if (RT_FAILURE(rc))
return PDMDEV_SET_ERROR(pDevIns, rc,
N_("Configuration error: Querying \"LogoTime\" as integer failed"));
rc = CFGMR3QueryU8(pCfg, "ShowBootMenu", &LogoHdr.fu8ShowBootMenu);
if (rc == VERR_CFGM_VALUE_NOT_FOUND)
LogoHdr.fu8ShowBootMenu = 0;
else if (RT_FAILURE(rc))
return PDMDEV_SET_ERROR(pDevIns, rc,
N_("Configuration error: Querying \"ShowBootMenu\" as integer failed"));
#if defined(DEBUG) && !defined(DEBUG_sunlover)
/* Disable the logo abd menu if all default settings. */
if ( LogoHdr.fu8FadeIn
&& LogoHdr.fu8FadeOut
&& LogoHdr.u16LogoMillies == 0
&& LogoHdr.fu8ShowBootMenu == 2)
LogoHdr.fu8FadeIn = LogoHdr.fu8FadeOut = LogoHdr.fu8ShowBootMenu = 0;
#endif
/* Delay the logo a little bit */
if (LogoHdr.fu8FadeIn && LogoHdr.fu8FadeOut && !LogoHdr.u16LogoMillies)
LogoHdr.u16LogoMillies = RT_MAX(LogoHdr.u16LogoMillies, LOGO_DELAY_TIME);
/*
* Get the Logo file name.
*/
rc = CFGMR3QueryStringAlloc(pCfg, "LogoFile", &pThis->pszLogoFile);
if (rc == VERR_CFGM_VALUE_NOT_FOUND)
pThis->pszLogoFile = NULL;
else if (RT_FAILURE(rc))
return PDMDEV_SET_ERROR(pDevIns, rc,
N_("Configuration error: Querying \"LogoFile\" as a string failed"));
else if (!*pThis->pszLogoFile)
{
MMR3HeapFree(pThis->pszLogoFile);
pThis->pszLogoFile = NULL;
}
/*
* Determine the logo size, open any specified logo file in the process.
*/
LogoHdr.cbLogo = g_cbVgaDefBiosLogo;
RTFILE FileLogo = NIL_RTFILE;
if (pThis->pszLogoFile)
{
rc = RTFileOpen(&FileLogo, pThis->pszLogoFile,
RTFILE_O_READ | RTFILE_O_OPEN | RTFILE_O_DENY_WRITE);
if (RT_SUCCESS(rc))
{
uint64_t cbFile;
rc = RTFileGetSize(FileLogo, &cbFile);
if (RT_SUCCESS(rc))
{
if (cbFile > 0 && cbFile < 32*_1M)
LogoHdr.cbLogo = (uint32_t)cbFile;
else
rc = VERR_TOO_MUCH_DATA;
}
}
if (RT_FAILURE(rc))
{
/*
* Ignore failure and fall back to the default logo.
*/
LogRel(("vgaR3Construct: Failed to open logo file '%s', rc=%Rrc!\n", pThis->pszLogoFile, rc));
if (FileLogo != NIL_RTFILE)
RTFileClose(FileLogo);
FileLogo = NIL_RTFILE;
MMR3HeapFree(pThis->pszLogoFile);
pThis->pszLogoFile = NULL;
}
}
/*
* Disable graphic splash screen if it doesn't fit into VRAM.
*/
if (pThis->vram_size < LOGO_MAX_SIZE)
LogoHdr.fu8FadeIn = LogoHdr.fu8FadeOut = LogoHdr.u16LogoMillies = 0;
/*
* Allocate buffer for the logo data.
* RT_MAX() is applied to let us fall back to default logo on read failure.
*/
pThis->cbLogo = sizeof(LogoHdr) + LogoHdr.cbLogo;
pThis->pu8Logo = (uint8_t *)PDMDevHlpMMHeapAlloc(pDevIns, RT_MAX(pThis->cbLogo, g_cbVgaDefBiosLogo + sizeof(LogoHdr)));
if (pThis->pu8Logo)
{
/*
* Write the logo header.
*/
PLOGOHDR pLogoHdr = (PLOGOHDR)pThis->pu8Logo;
*pLogoHdr = LogoHdr;
/*
* Write the logo bitmap.
*/
if (pThis->pszLogoFile)
{
rc = RTFileRead(FileLogo, pLogoHdr + 1, LogoHdr.cbLogo, NULL);
if (RT_FAILURE(rc))
{
AssertMsgFailed(("RTFileRead(,,%d,NULL) -> %Rrc\n", LogoHdr.cbLogo, rc));
pLogoHdr->cbLogo = LogoHdr.cbLogo = g_cbVgaDefBiosLogo;
memcpy(pLogoHdr + 1, g_abVgaDefBiosLogo, LogoHdr.cbLogo);
}
}
else
memcpy(pLogoHdr + 1, g_abVgaDefBiosLogo, LogoHdr.cbLogo);
rc = vbeParseBitmap(pThis);
if (RT_FAILURE(rc))
{
AssertMsgFailed(("vbeParseBitmap() -> %Rrc\n", rc));
pLogoHdr->cbLogo = LogoHdr.cbLogo = g_cbVgaDefBiosLogo;
memcpy(pLogoHdr + 1, g_abVgaDefBiosLogo, LogoHdr.cbLogo);
}
rc = vbeParseBitmap(pThis);
if (RT_FAILURE(rc))
AssertReleaseMsgFailed(("Internal bitmap failed! vbeParseBitmap() -> %Rrc\n", rc));
rc = VINF_SUCCESS;
}
else
rc = VERR_NO_MEMORY;
/*
* Cleanup.
*/
if (FileLogo != NIL_RTFILE)
RTFileClose(FileLogo);
#ifdef VBOX_WITH_HGSMI
VBVAInit (pThis);
#endif /* VBOX_WITH_HGSMI */
#ifdef VBOX_WITH_VDMA
if(rc == VINF_SUCCESS)
{
rc = vboxVDMAConstruct(pThis, 1024);
AssertRC(rc);
}
#endif
/*
* Statistics.
*/
STAM_REG(pVM, &pThis->StatRZMemoryRead, STAMTYPE_PROFILE, "/Devices/VGA/RZ/MMIO-Read", STAMUNIT_TICKS_PER_CALL, "Profiling of the VGAGCMemoryRead() body.");
STAM_REG(pVM, &pThis->StatR3MemoryRead, STAMTYPE_PROFILE, "/Devices/VGA/R3/MMIO-Read", STAMUNIT_TICKS_PER_CALL, "Profiling of the VGAGCMemoryRead() body.");
STAM_REG(pVM, &pThis->StatRZMemoryWrite, STAMTYPE_PROFILE, "/Devices/VGA/RZ/MMIO-Write", STAMUNIT_TICKS_PER_CALL, "Profiling of the VGAGCMemoryWrite() body.");
STAM_REG(pVM, &pThis->StatR3MemoryWrite, STAMTYPE_PROFILE, "/Devices/VGA/R3/MMIO-Write", STAMUNIT_TICKS_PER_CALL, "Profiling of the VGAGCMemoryWrite() body.");
STAM_REG(pVM, &pThis->StatMapPage, STAMTYPE_COUNTER, "/Devices/VGA/MapPageCalls", STAMUNIT_OCCURENCES, "Calls to IOMMMIOMapMMIO2Page.");
STAM_REG(pVM, &pThis->StatUpdateDisp, STAMTYPE_COUNTER, "/Devices/VGA/UpdateDisplay", STAMUNIT_OCCURENCES, "Calls to vgaPortUpdateDisplay().");
/* Init latched access mask. */
pThis->uMaskLatchAccess = 0x3ff;
return rc;
}
/**
* The device registration structure.
*/
const PDMDEVREG g_DeviceVga =
{
/* u32Version */
PDM_DEVREG_VERSION,
/* szName */
"vga",
/* szRCMod */
"VBoxDDGC.gc",
/* szR0Mod */
"VBoxDDR0.r0",
/* pszDescription */
"VGA Adaptor with VESA extensions.",
/* fFlags */
PDM_DEVREG_FLAGS_DEFAULT_BITS | PDM_DEVREG_FLAGS_RC | PDM_DEVREG_FLAGS_R0,
/* fClass */
PDM_DEVREG_CLASS_GRAPHICS,
/* cMaxInstances */
1,
/* cbInstance */
sizeof(VGASTATE),
/* pfnConstruct */
vgaR3Construct,
/* pfnDestruct */
vgaR3Destruct,
/* pfnRelocate */
vgaR3Relocate,
/* pfnIOCtl */
NULL,
/* pfnPowerOn */
NULL,
/* pfnReset */
vgaR3Reset,
/* pfnSuspend */
NULL,
/* pfnResume */
NULL,
/* pfnAttach */
vgaAttach,
/* pfnDetach */
vgaDetach,
/* pfnQueryInterface */
NULL,
/* pfnInitComplete */
NULL,
/* pfnPowerOff */
NULL,
/* pfnSoftReset */
NULL,
/* u32VersionEnd */
PDM_DEVREG_VERSION
};
#endif /* !IN_RING3 */
#endif /* !VBOX_DEVICE_STRUCT_TESTCASE */
/*
* Local Variables:
* nuke-trailing-whitespace-p:nil
* End:
*/