VirtualBox

source: vbox/trunk/src/VBox/Devices/Graphics/DevVGA.cpp@ 21957

Last change on this file since 21957 was 21957, checked in by vboxsync, 16 years ago

Corrected assertion

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 208.1 KB
Line 
1#ifdef VBOX
2/* $Id: DevVGA.cpp 21957 2009-08-04 13:47:24Z vboxsync $ */
3/** @file
4 * DevVGA - VBox VGA/VESA device.
5 */
6
7/*
8 * Copyright (C) 2006-2007 Sun Microsystems, Inc.
9 *
10 * This file is part of VirtualBox Open Source Edition (OSE), as
11 * available from http://www.virtualbox.org. This file is free software;
12 * you can redistribute it and/or modify it under the terms of the GNU
13 * General Public License (GPL) as published by the Free Software
14 * Foundation, in version 2 as it comes in the "COPYING" file of the
15 * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
16 * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
17 *
18 * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa
19 * Clara, CA 95054 USA or visit http://www.sun.com if you need
20 * additional information or have any questions.
21 * --------------------------------------------------------------------
22 *
23 * This code is based on:
24 *
25 * QEMU VGA Emulator.
26 *
27 * Copyright (c) 2003 Fabrice Bellard
28 *
29 * Permission is hereby granted, free of charge, to any person obtaining a copy
30 * of this software and associated documentation files (the "Software"), to deal
31 * in the Software without restriction, including without limitation the rights
32 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
33 * copies of the Software, and to permit persons to whom the Software is
34 * furnished to do so, subject to the following conditions:
35 *
36 * The above copyright notice and this permission notice shall be included in
37 * all copies or substantial portions of the Software.
38 *
39 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
40 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
41 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
42 * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
43 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
44 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
45 * THE SOFTWARE.
46 */
47
48/*******************************************************************************
49* Defined Constants And Macros *
50*******************************************************************************/
51/** The default amount of VRAM. */
52#define VGA_VRAM_DEFAULT (_4M)
53/** The maximum amount of VRAM. */
54#define VGA_VRAM_MAX (128 * _1M)
55/** The minimum amount of VRAM. */
56#define VGA_VRAM_MIN (_1M)
57
58#define VGA_SAVEDSTATE_VERSION 2
59
60/** The size of the VGA GC mapping.
61 * This is supposed to be all the VGA memory accessible to the guest.
62 * The initial value was 256KB but NTAllInOne.iso appears to access more
63 * thus the limit was upped to 512KB.
64 *
65 * @todo Someone with some VGA knowhow should make a better guess at this value.
66 */
67#define VGA_MAPPING_SIZE _512K
68
69/** Converts a vga adaptor state pointer to a device instance pointer. */
70#define VGASTATE2DEVINS(pVgaState) ((pVgaState)->CTX_SUFF(pDevIns))
71
72/** Use VBE bytewise I/O */
73#define VBE_BYTEWISE_IO
74
75/** Use VBE new dynamic mode list.
76 * If this is not defined, no checks are carried out to see if the modes all
77 * fit into the framebuffer! See the VRAM_SIZE_FIX define. */
78#define VBE_NEW_DYN_LIST
79
80/** Check that the video modes fit into virtual video memory.
81 * Only works when VBE_NEW_DYN_LIST is defined! */
82#define VRAM_SIZE_FIX
83
84/** Some fixes to ensure that logical scan-line lengths are not overwritten. */
85#define KEEP_SCAN_LINE_LENGTH
86
87/** Check buffer if an VRAM offset is within the right range or not. */
88#if defined(IN_RC) || defined(VBOX_WITH_2X_4GB_ADDR_SPACE_IN_R0)
89# define VERIFY_VRAM_WRITE_OFF_RETURN(pThis, off) \
90 do { \
91 if ((off) >= VGA_MAPPING_SIZE) \
92 { \
93 AssertMsgReturn((off) < (pThis)->vram_size, ("%RX32 !< %RX32\n", (uint32_t)(off), (pThis)->vram_size), VINF_SUCCESS); \
94 Log2(("%Rfn[%d]: %RX32 -> R3\n", __PRETTY_FUNCTION__, __LINE__, (off))); \
95 return VINF_IOM_HC_MMIO_WRITE; \
96 } \
97 } while (0)
98#else
99# define VERIFY_VRAM_WRITE_OFF_RETURN(pThis, off) \
100 AssertMsgReturn((off) < (pThis)->vram_size, ("%RX32 !< %RX32\n", (uint32_t)(off), (pThis)->vram_size), VINF_SUCCESS)
101#endif
102
103/** Check buffer if an VRAM offset is within the right range or not. */
104#if defined(IN_RC) || defined(VBOX_WITH_2X_4GB_ADDR_SPACE_IN_R0)
105# define VERIFY_VRAM_READ_OFF_RETURN(pThis, off, rcVar) \
106 do { \
107 if ((off) >= VGA_MAPPING_SIZE) \
108 { \
109 AssertMsgReturn((off) < (pThis)->vram_size, ("%RX32 !< %RX32\n", (uint32_t)(off), (pThis)->vram_size), 0xff); \
110 Log2(("%Rfn[%d]: %RX32 -> R3\n", __PRETTY_FUNCTION__, __LINE__, (off))); \
111 (rcVar) = VINF_IOM_HC_MMIO_READ; \
112 return 0; \
113 } \
114 } while (0)
115#else
116# define VERIFY_VRAM_READ_OFF_RETURN(pThis, off, rcVar) \
117 AssertMsgReturn((off) < (pThis)->vram_size, ("%RX32 !< %RX32\n", (uint32_t)(off), (pThis)->vram_size), 0xff)
118#endif
119
120
121/*******************************************************************************
122* Header Files *
123*******************************************************************************/
124#define LOG_GROUP LOG_GROUP_DEV_VGA
125#include <VBox/pdmdev.h>
126#include <VBox/pgm.h>
127#include <iprt/assert.h>
128#include <iprt/asm.h>
129#include <iprt/file.h>
130#include <iprt/time.h>
131#include <iprt/string.h>
132
133#include <VBox/VMMDev.h>
134#include <VBox/VBoxVideo.h>
135#include <VBox/bioslogo.h>
136
137#if defined(VBE_NEW_DYN_LIST) && defined(IN_RING3) && !defined(VBOX_DEVICE_STRUCT_TESTCASE)
138# include "DevVGAModes.h"
139# include <stdio.h> /* sscan */
140#endif
141
142#include "vl_vbox.h"
143#include "DevVGA.h"
144#include "Builtins.h"
145#include "Builtins2.h"
146
147
148/*******************************************************************************
149* Structures and Typedefs *
150*******************************************************************************/
151#pragma pack(1)
152
153/** BMP File Format Bitmap Header. */
154typedef struct
155{
156 uint16_t Type; /* File Type Identifier */
157 uint32_t FileSize; /* Size of File */
158 uint16_t Reserved1; /* Reserved (should be 0) */
159 uint16_t Reserved2; /* Reserved (should be 0) */
160 uint32_t Offset; /* Offset to bitmap data */
161} BMPINFO;
162
163/** Pointer to a bitmap header*/
164typedef BMPINFO *PBMPINFO;
165
166/** OS/2 1.x Information Header Format. */
167typedef struct
168{
169 uint32_t Size; /* Size of Remianing Header */
170 uint16_t Width; /* Width of Bitmap in Pixels */
171 uint16_t Height; /* Height of Bitmap in Pixels */
172 uint16_t Planes; /* Number of Planes */
173 uint16_t BitCount; /* Color Bits Per Pixel */
174} OS2HDR;
175
176/** Pointer to a OS/2 1.x header format */
177typedef OS2HDR *POS2HDR;
178
179/** OS/2 2.0 Information Header Format. */
180typedef struct
181{
182 uint32_t Size; /* Size of Remianing Header */
183 uint32_t Width; /* Width of Bitmap in Pixels */
184 uint32_t Height; /* Height of Bitmap in Pixels */
185 uint16_t Planes; /* Number of Planes */
186 uint16_t BitCount; /* Color Bits Per Pixel */
187 uint32_t Compression; /* Compression Scheme (0=none) */
188 uint32_t SizeImage; /* Size of bitmap in bytes */
189 uint32_t XPelsPerMeter; /* Horz. Resolution in Pixels/Meter */
190 uint32_t YPelsPerMeter; /* Vert. Resolution in Pixels/Meter */
191 uint32_t ClrUsed; /* Number of Colors in Color Table */
192 uint32_t ClrImportant; /* Number of Important Colors */
193 uint16_t Units; /* Resolution Mesaurement Used */
194 uint16_t Reserved; /* Reserved FIelds (always 0) */
195 uint16_t Recording; /* Orientation of Bitmap */
196 uint16_t Rendering; /* Halftone Algorithm Used on Image */
197 uint32_t Size1; /* Halftone Algorithm Data */
198 uint32_t Size2; /* Halftone Algorithm Data */
199 uint32_t ColorEncoding; /* Color Table Format (always 0) */
200 uint32_t Identifier; /* Misc. Field for Application Use */
201} OS22HDR;
202
203/** Pointer to a OS/2 2.0 header format */
204typedef OS22HDR *POS22HDR;
205
206/** Windows 3.x Information Header Format. */
207typedef struct
208{
209 uint32_t Size; /* Size of Remianing Header */
210 uint32_t Width; /* Width of Bitmap in Pixels */
211 uint32_t Height; /* Height of Bitmap in Pixels */
212 uint16_t Planes; /* Number of Planes */
213 uint16_t BitCount; /* Bits Per Pixel */
214 uint32_t Compression; /* Compression Scheme (0=none) */
215 uint32_t SizeImage; /* Size of bitmap in bytes */
216 uint32_t XPelsPerMeter; /* Horz. Resolution in Pixels/Meter */
217 uint32_t YPelsPerMeter; /* Vert. Resolution in Pixels/Meter */
218 uint32_t ClrUsed; /* Number of Colors in Color Table */
219 uint32_t ClrImportant; /* Number of Important Colors */
220} WINHDR;
221
222/** Pointer to a Windows 3.x header format */
223typedef WINHDR *PWINHDR;
224
225#pragma pack()
226
227#define BMP_ID 0x4D42
228
229/** @name BMP compressions.
230 * @{ */
231#define BMP_COMPRESS_NONE 0
232#define BMP_COMPRESS_RLE8 1
233#define BMP_COMPRESS_RLE4 2
234/** @} */
235
236/** @name BMP header sizes.
237 * @{ */
238#define BMP_HEADER_OS21 12
239#define BMP_HEADER_OS22 64
240#define BMP_HEADER_WIN3 40
241/** @} */
242
243/** The BIOS boot menu text position, X. */
244#define LOGO_F12TEXT_X 304
245/** The BIOS boot menu text position, Y. */
246#define LOGO_F12TEXT_Y 464
247
248/** Width of the "Press F12 to select boot device." bitmap.
249 Anything that exceeds the limit of F12BootText below is filled with
250 background. */
251#define LOGO_F12TEXT_WIDTH 286
252/** Height of the boot device selection bitmap, see LOGO_F12TEXT_WIDTH. */
253#define LOGO_F12TEXT_HEIGHT 12
254
255/** The BIOS logo delay time (msec). */
256#define LOGO_DELAY_TIME 2000
257
258#define LOGO_MAX_WIDTH 640
259#define LOGO_MAX_HEIGHT 480
260#define LOGO_MAX_SIZE LOGO_MAX_WIDTH * LOGO_MAX_HEIGHT * 4
261
262
263/*******************************************************************************
264* Global Variables *
265*******************************************************************************/
266/* "Press F12 to select boot device." bitmap. */
267static const uint8_t g_abLogoF12BootText[] =
268{
269 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
270 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
271 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x07, 0x0F, 0x7C,
272 0xF8, 0xF0, 0x01, 0xE0, 0x81, 0x9F, 0x3F, 0x00, 0x70, 0xF8, 0x00, 0xE0, 0xC3,
273 0x07, 0x0F, 0x1F, 0x3E, 0x70, 0x00, 0xF0, 0xE1, 0xC3, 0x07, 0x0E, 0x00, 0x6E,
274 0x7C, 0x60, 0xE0, 0xE1, 0xC3, 0x07, 0xC6, 0x80, 0x81, 0x31, 0x63, 0xC6, 0x00,
275 0x30, 0x80, 0x61, 0x0C, 0x00, 0x36, 0x63, 0x00, 0x8C, 0x19, 0x83, 0x61, 0xCC,
276 0x18, 0x36, 0x00, 0xCC, 0x8C, 0x19, 0xC3, 0x06, 0xC0, 0x8C, 0x31, 0x3C, 0x30,
277 0x8C, 0x19, 0x83, 0x31, 0x60, 0x60, 0x00, 0x0C, 0x18, 0x00, 0x0C, 0x60, 0x18,
278 0x00, 0x80, 0xC1, 0x18, 0x00, 0x30, 0x06, 0x60, 0x18, 0x30, 0x80, 0x01, 0x00,
279 0x33, 0x63, 0xC6, 0x30, 0x00, 0x30, 0x63, 0x80, 0x19, 0x0C, 0x03, 0x06, 0x00,
280 0x0C, 0x18, 0x18, 0xC0, 0x81, 0x03, 0x00, 0x03, 0x18, 0x0C, 0x00, 0x60, 0x30,
281 0x06, 0x00, 0x87, 0x01, 0x18, 0x06, 0x0C, 0x60, 0x00, 0xC0, 0xCC, 0x98, 0x31,
282 0x0C, 0x00, 0xCC, 0x18, 0x30, 0x0C, 0xC3, 0x80, 0x01, 0x00, 0x03, 0x66, 0xFE,
283 0x18, 0x30, 0x00, 0xC0, 0x02, 0x06, 0x06, 0x00, 0x18, 0x8C, 0x01, 0x60, 0xE0,
284 0x0F, 0x86, 0x3F, 0x03, 0x18, 0x00, 0x30, 0x33, 0x66, 0x0C, 0x03, 0x00, 0x33,
285 0xFE, 0x0C, 0xC3, 0x30, 0xE0, 0x0F, 0xC0, 0x87, 0x9B, 0x31, 0x63, 0xC6, 0x00,
286 0xF0, 0x80, 0x01, 0x03, 0x00, 0x06, 0x63, 0x00, 0x8C, 0x19, 0x83, 0x61, 0xCC,
287 0x18, 0x06, 0x00, 0x6C, 0x8C, 0x19, 0xC3, 0x00, 0x80, 0x8D, 0x31, 0xC3, 0x30,
288 0x8C, 0x19, 0x03, 0x30, 0xB3, 0xC3, 0x87, 0x0F, 0x1F, 0x00, 0x2C, 0x60, 0x80,
289 0x01, 0xE0, 0x87, 0x0F, 0x00, 0x3E, 0x7C, 0x60, 0xF0, 0xE1, 0xE3, 0x07, 0x00,
290 0x0F, 0x3E, 0x7C, 0xFC, 0x00, 0xC0, 0xC3, 0xC7, 0x30, 0x0E, 0x3E, 0x7C, 0x00,
291 0xCC, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x23, 0x1E, 0xC0, 0x00, 0x60, 0x00,
292 0x00, 0x00, 0x00, 0x00, 0x18, 0x00, 0x00, 0x60, 0x00, 0xC0, 0x00, 0x00, 0x00,
293 0x0C, 0x00, 0xC0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x33, 0x00, 0x00,
294 0x00, 0x00, 0x00, 0xC0, 0x0C, 0x87, 0x31, 0x00, 0x18, 0x00, 0x00, 0x00, 0x00,
295 0x00, 0x06, 0x00, 0x00, 0x18, 0x00, 0x30, 0x00, 0x00, 0x00, 0x03, 0x00, 0x30,
296 0x00, 0x00, 0xC0, 0x00, 0x00, 0x00, 0xE0, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00,
297 0xF8, 0x83, 0xC1, 0x07, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0xC0, 0x01, 0x00,
298 0x00, 0x04, 0x00, 0x0E, 0x00, 0x00, 0x80, 0x00, 0x00, 0x0E, 0x00, 0x00, 0x30,
299 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
300 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
301 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
302};
303
304
305#ifndef VBOX_DEVICE_STRUCT_TESTCASE
306/*******************************************************************************
307* Internal Functions *
308*******************************************************************************/
309RT_C_DECLS_BEGIN
310
311PDMBOTHCBDECL(int) vgaIOPortWrite(PPDMDEVINS pDevIns, void *pvUser, RTIOPORT Port, uint32_t u32, unsigned cb);
312PDMBOTHCBDECL(int) vgaIOPortRead(PPDMDEVINS pDevIns, void *pvUser, RTIOPORT Port, uint32_t *pu32, unsigned cb);
313PDMBOTHCBDECL(int) vgaIOPortWriteVBEIndex(PPDMDEVINS pDevIns, void *pvUser, RTIOPORT Port, uint32_t u32, unsigned cb);
314PDMBOTHCBDECL(int) vgaIOPortWriteVBEData(PPDMDEVINS pDevIns, void *pvUser, RTIOPORT Port, uint32_t u32, unsigned cb);
315PDMBOTHCBDECL(int) vgaIOPortReadVBEIndex(PPDMDEVINS pDevIns, void *pvUser, RTIOPORT Port, uint32_t *pu32, unsigned cb);
316PDMBOTHCBDECL(int) vgaIOPortReadVBEData(PPDMDEVINS pDevIns, void *pvUser, RTIOPORT Port, uint32_t *pu32, unsigned cb);
317PDMBOTHCBDECL(int) vgaMMIOFill(PPDMDEVINS pDevIns, void *pvUser, RTGCPHYS GCPhysAddr, uint32_t u32Item, unsigned cbItem, unsigned cItems);
318PDMBOTHCBDECL(int) vgaMMIORead(PPDMDEVINS pDevIns, void *pvUser, RTGCPHYS GCPhysAddr, void *pv, unsigned cb);
319PDMBOTHCBDECL(int) vgaMMIOWrite(PPDMDEVINS pDevIns, void *pvUser, RTGCPHYS GCPhysAddr, void *pv, unsigned cb);
320PDMBOTHCBDECL(int) vgaIOPortReadBIOS(PPDMDEVINS pDevIns, void *pvUser, RTIOPORT Port, uint32_t *pu32, unsigned cb);
321PDMBOTHCBDECL(int) vgaIOPortWriteBIOS(PPDMDEVINS pDevIns, void *pvUser, RTIOPORT Port, uint32_t u32, unsigned cb);
322#ifdef IN_RC
323PDMBOTHCBDECL(int) vgaGCLFBAccessHandler(PVM pVM, RTGCUINT uErrorCode, PCPUMCTXCORE pRegFrame, RTGCPTR pvFault, RTGCPHYS GCPhysFault, void *pvUser);
324#endif
325#ifdef IN_RING0
326PDMBOTHCBDECL(int) vgaR0LFBAccessHandler(PVM pVM, RTGCUINT uErrorCode, PCPUMCTXCORE pRegFrame, RTGCPTR pvFault, RTGCPHYS GCPhysFault, void *pvUser);
327#endif
328#ifdef IN_RING3
329# ifdef VBE_NEW_DYN_LIST
330PDMBOTHCBDECL(int) vbeIOPortReadVBEExtra(PPDMDEVINS pDevIns, void *pvUser, RTIOPORT Port, uint32_t *pu32, unsigned cb);
331PDMBOTHCBDECL(int) vbeIOPortWriteVBEExtra(PPDMDEVINS pDevIns, void *pvUser, RTIOPORT Port, uint32_t u32, unsigned cb);
332# endif
333PDMBOTHCBDECL(int) vbeIOPortReadCMDLogo(PPDMDEVINS pDevIns, void *pvUser, RTIOPORT Port, uint32_t *pu32, unsigned cb);
334PDMBOTHCBDECL(int) vbeIOPortWriteCMDLogo(PPDMDEVINS pDevIns, void *pvUser, RTIOPORT Port, uint32_t u32, unsigned cb);
335#endif /* IN_RING3 */
336
337
338RT_C_DECLS_END
339
340
341/**
342 * Set a VRAM page dirty.
343 *
344 * @param pThis VGA instance data.
345 * @param offVRAM The VRAM offset of the page to set.
346 */
347DECLINLINE(void) vga_set_dirty(VGAState *pThis, RTGCPHYS offVRAM)
348{
349 AssertMsg(offVRAM < pThis->vram_size, ("offVRAM = %p, pThis->vram_size = %p\n", offVRAM, pThis->vram_size));
350 ASMBitSet(&pThis->au32DirtyBitmap[0], offVRAM >> PAGE_SHIFT);
351 pThis->fHasDirtyBits = true;
352}
353
354/**
355 * Tests if a VRAM page is dirty.
356 *
357 * @returns true if dirty.
358 * @returns false if clean.
359 * @param pThis VGA instance data.
360 * @param offVRAM The VRAM offset of the page to check.
361 */
362DECLINLINE(bool) vga_is_dirty(VGAState *pThis, RTGCPHYS offVRAM)
363{
364 AssertMsg(offVRAM < pThis->vram_size, ("offVRAM = %p, pThis->vram_size = %p\n", offVRAM, pThis->vram_size));
365 return ASMBitTest(&pThis->au32DirtyBitmap[0], offVRAM >> PAGE_SHIFT);
366}
367
368/**
369 * Reset dirty flags in a give range.
370 *
371 * @param pThis VGA instance data.
372 * @param offVRAMStart Offset into the VRAM buffer of the first page.
373 * @param offVRAMEnd Offset into the VRAM buffer of the last page - exclusive.
374 */
375DECLINLINE(void) vga_reset_dirty(VGAState *pThis, RTGCPHYS offVRAMStart, RTGCPHYS offVRAMEnd)
376{
377 Assert(offVRAMStart < pThis->vram_size);
378 Assert(offVRAMEnd <= pThis->vram_size);
379 Assert(offVRAMStart < offVRAMEnd);
380 ASMBitClearRange(&pThis->au32DirtyBitmap[0], offVRAMStart >> PAGE_SHIFT, offVRAMEnd >> PAGE_SHIFT);
381}
382
383#endif /* !VBOX_DEVICE_STRUCT_TESTCASE */
384#endif /* VBOX */
385#ifndef VBOX_DEVICE_STRUCT_TESTCASE
386
387#ifndef VBOX
388#include "vl.h"
389#include "vga_int.h"
390#endif /* !VBOX */
391
392#ifdef LOG_ENABLED
393//#define DEBUG_VGA
394//#define DEBUG_VGA_MEM
395//#define DEBUG_VGA_REG
396
397#define DEBUG_BOCHS_VBE
398
399#endif
400
401/* force some bits to zero */
402#ifdef VBOX
403static
404#endif /* VBOX */
405const uint8_t sr_mask[8] = {
406 (uint8_t)~0xfc,
407 (uint8_t)~0xc2,
408 (uint8_t)~0xf0,
409 (uint8_t)~0xc0,
410 (uint8_t)~0xf1,
411 (uint8_t)~0xff,
412 (uint8_t)~0xff,
413 (uint8_t)~0x00,
414};
415
416#ifdef VBOX
417static
418#endif /* VBOX */
419const uint8_t gr_mask[16] = {
420 (uint8_t)~0xf0, /* 0x00 */
421 (uint8_t)~0xf0, /* 0x01 */
422 (uint8_t)~0xf0, /* 0x02 */
423 (uint8_t)~0xe0, /* 0x03 */
424 (uint8_t)~0xfc, /* 0x04 */
425 (uint8_t)~0x84, /* 0x05 */
426 (uint8_t)~0xf0, /* 0x06 */
427 (uint8_t)~0xf0, /* 0x07 */
428 (uint8_t)~0x00, /* 0x08 */
429 (uint8_t)~0xff, /* 0x09 */
430 (uint8_t)~0xff, /* 0x0a */
431 (uint8_t)~0xff, /* 0x0b */
432 (uint8_t)~0xff, /* 0x0c */
433 (uint8_t)~0xff, /* 0x0d */
434 (uint8_t)~0xff, /* 0x0e */
435 (uint8_t)~0xff, /* 0x0f */
436};
437
438#define cbswap_32(__x) \
439((uint32_t)( \
440 (((uint32_t)(__x) & (uint32_t)0x000000ffUL) << 24) | \
441 (((uint32_t)(__x) & (uint32_t)0x0000ff00UL) << 8) | \
442 (((uint32_t)(__x) & (uint32_t)0x00ff0000UL) >> 8) | \
443 (((uint32_t)(__x) & (uint32_t)0xff000000UL) >> 24) ))
444
445#ifdef WORDS_BIGENDIAN
446#define PAT(x) cbswap_32(x)
447#else
448#define PAT(x) (x)
449#endif
450
451#ifdef WORDS_BIGENDIAN
452#define BIG 1
453#else
454#define BIG 0
455#endif
456
457#ifdef WORDS_BIGENDIAN
458#define GET_PLANE(data, p) (((data) >> (24 - (p) * 8)) & 0xff)
459#else
460#define GET_PLANE(data, p) (((data) >> ((p) * 8)) & 0xff)
461#endif
462
463static const uint32_t mask16[16] = {
464 PAT(0x00000000),
465 PAT(0x000000ff),
466 PAT(0x0000ff00),
467 PAT(0x0000ffff),
468 PAT(0x00ff0000),
469 PAT(0x00ff00ff),
470 PAT(0x00ffff00),
471 PAT(0x00ffffff),
472 PAT(0xff000000),
473 PAT(0xff0000ff),
474 PAT(0xff00ff00),
475 PAT(0xff00ffff),
476 PAT(0xffff0000),
477 PAT(0xffff00ff),
478 PAT(0xffffff00),
479 PAT(0xffffffff),
480};
481
482#undef PAT
483
484#ifdef WORDS_BIGENDIAN
485#define PAT(x) (x)
486#else
487#define PAT(x) cbswap_32(x)
488#endif
489
490static const uint32_t dmask16[16] = {
491 PAT(0x00000000),
492 PAT(0x000000ff),
493 PAT(0x0000ff00),
494 PAT(0x0000ffff),
495 PAT(0x00ff0000),
496 PAT(0x00ff00ff),
497 PAT(0x00ffff00),
498 PAT(0x00ffffff),
499 PAT(0xff000000),
500 PAT(0xff0000ff),
501 PAT(0xff00ff00),
502 PAT(0xff00ffff),
503 PAT(0xffff0000),
504 PAT(0xffff00ff),
505 PAT(0xffffff00),
506 PAT(0xffffffff),
507};
508
509static const uint32_t dmask4[4] = {
510 PAT(0x00000000),
511 PAT(0x0000ffff),
512 PAT(0xffff0000),
513 PAT(0xffffffff),
514};
515
516#if defined(VBOX) && defined(IN_RING3)
517static uint32_t expand4[256];
518static uint16_t expand2[256];
519static uint8_t expand4to8[16];
520#endif /* VBOX && IN_RING3 */
521
522#ifndef VBOX
523VGAState *vga_state;
524int vga_io_memory;
525#endif /* !VBOX */
526
527static uint32_t vga_ioport_read(void *opaque, uint32_t addr)
528{
529 VGAState *s = (VGAState*)opaque;
530 int val, index;
531
532 /* check port range access depending on color/monochrome mode */
533 if ((addr >= 0x3b0 && addr <= 0x3bf && (s->msr & MSR_COLOR_EMULATION)) ||
534 (addr >= 0x3d0 && addr <= 0x3df && !(s->msr & MSR_COLOR_EMULATION))) {
535 val = 0xff;
536 Log(("VGA: following read ignored\n"));
537 } else {
538 switch(addr) {
539 case 0x3c0:
540 if (s->ar_flip_flop == 0) {
541 val = s->ar_index;
542 } else {
543 val = 0;
544 }
545 break;
546 case 0x3c1:
547 index = s->ar_index & 0x1f;
548 if (index < 21)
549 val = s->ar[index];
550 else
551 val = 0;
552 break;
553 case 0x3c2:
554 val = s->st00;
555 break;
556 case 0x3c4:
557 val = s->sr_index;
558 break;
559 case 0x3c5:
560 val = s->sr[s->sr_index];
561#ifdef DEBUG_VGA_REG
562 Log(("vga: read SR%x = 0x%02x\n", s->sr_index, val));
563#endif
564 break;
565 case 0x3c7:
566 val = s->dac_state;
567 break;
568 case 0x3c8:
569 val = s->dac_write_index;
570 break;
571 case 0x3c9:
572 val = s->palette[s->dac_read_index * 3 + s->dac_sub_index];
573 if (++s->dac_sub_index == 3) {
574 s->dac_sub_index = 0;
575 s->dac_read_index++;
576 }
577 break;
578 case 0x3ca:
579 val = s->fcr;
580 break;
581 case 0x3cc:
582 val = s->msr;
583 break;
584 case 0x3ce:
585 val = s->gr_index;
586 break;
587 case 0x3cf:
588 val = s->gr[s->gr_index];
589#ifdef DEBUG_VGA_REG
590 Log(("vga: read GR%x = 0x%02x\n", s->gr_index, val));
591#endif
592 break;
593 case 0x3b4:
594 case 0x3d4:
595 val = s->cr_index;
596 break;
597 case 0x3b5:
598 case 0x3d5:
599 val = s->cr[s->cr_index];
600#ifdef DEBUG_VGA_REG
601 Log(("vga: read CR%x = 0x%02x\n", s->cr_index, val));
602#endif
603 break;
604 case 0x3ba:
605 case 0x3da:
606 /* just toggle to fool polling */
607 s->st01 ^= ST01_V_RETRACE | ST01_DISP_ENABLE;
608 val = s->st01;
609 s->ar_flip_flop = 0;
610 break;
611 default:
612 val = 0x00;
613 break;
614 }
615 }
616#if defined(DEBUG_VGA)
617 Log(("VGA: read addr=0x%04x data=0x%02x\n", addr, val));
618#endif
619 return val;
620}
621
622static void vga_ioport_write(void *opaque, uint32_t addr, uint32_t val)
623{
624 VGAState *s = (VGAState*)opaque;
625 int index;
626
627#ifdef DEBUG_VGA
628 Log(("VGA: write addr=0x%04x data=0x%02x\n", addr, val));
629#endif
630
631 /* check port range access depending on color/monochrome mode */
632 if ((addr >= 0x3b0 && addr <= 0x3bf && (s->msr & MSR_COLOR_EMULATION)) ||
633 (addr >= 0x3d0 && addr <= 0x3df && !(s->msr & MSR_COLOR_EMULATION))) {
634 Log(("VGA: previous write ignored\n"));
635 return;
636 }
637
638 switch(addr) {
639 case 0x3c0:
640 if (s->ar_flip_flop == 0) {
641 val &= 0x3f;
642 s->ar_index = val;
643 } else {
644 index = s->ar_index & 0x1f;
645 switch(index) {
646#ifndef VBOX
647 case 0x00 ... 0x0f:
648#else /* VBOX */
649 case 0x00: case 0x01: case 0x02: case 0x03: case 0x04: case 0x05: case 0x06: case 0x07:
650 case 0x08: case 0x09: case 0x0a: case 0x0b: case 0x0c: case 0x0d: case 0x0e: case 0x0f:
651#endif /* VBOX */
652 s->ar[index] = val & 0x3f;
653 break;
654 case 0x10:
655 s->ar[index] = val & ~0x10;
656 break;
657 case 0x11:
658 s->ar[index] = val;
659 break;
660 case 0x12:
661 s->ar[index] = val & ~0xc0;
662 break;
663 case 0x13:
664 s->ar[index] = val & ~0xf0;
665 break;
666 case 0x14:
667 s->ar[index] = val & ~0xf0;
668 break;
669 default:
670 break;
671 }
672 }
673 s->ar_flip_flop ^= 1;
674 break;
675 case 0x3c2:
676 s->msr = val & ~0x10;
677 break;
678 case 0x3c4:
679 s->sr_index = val & 7;
680 break;
681 case 0x3c5:
682#ifdef DEBUG_VGA_REG
683 Log(("vga: write SR%x = 0x%02x\n", s->sr_index, val));
684#endif
685 s->sr[s->sr_index] = val & sr_mask[s->sr_index];
686
687#ifndef IN_RC
688 /* The VGA region is (could be) affected by this change; reset all aliases we've created. */
689 if ( s->sr_index == 4 /* mode */
690 || s->sr_index == 2 /* plane mask */)
691 {
692 if (s->fRemappedVGA)
693 {
694 IOMMMIOResetRegion(PDMDevHlpGetVM(s->CTX_SUFF(pDevIns)), 0x000a0000);
695 s->fRemappedVGA = false;
696 }
697 }
698#endif
699 break;
700 case 0x3c7:
701 s->dac_read_index = val;
702 s->dac_sub_index = 0;
703 s->dac_state = 3;
704 break;
705 case 0x3c8:
706 s->dac_write_index = val;
707 s->dac_sub_index = 0;
708 s->dac_state = 0;
709 break;
710 case 0x3c9:
711 s->dac_cache[s->dac_sub_index] = val;
712 if (++s->dac_sub_index == 3) {
713 memcpy(&s->palette[s->dac_write_index * 3], s->dac_cache, 3);
714 s->dac_sub_index = 0;
715 s->dac_write_index++;
716 }
717 break;
718 case 0x3ce:
719 s->gr_index = val & 0x0f;
720 break;
721 case 0x3cf:
722#ifdef DEBUG_VGA_REG
723 Log(("vga: write GR%x = 0x%02x\n", s->gr_index, val));
724#endif
725 s->gr[s->gr_index] = val & gr_mask[s->gr_index];
726
727#ifndef IN_RC
728 /* The VGA region is (could be) affected by this change; reset all aliases we've created. */
729 if (s->gr_index == 6 /* memory map mode */)
730 {
731 if (s->fRemappedVGA)
732 {
733 IOMMMIOResetRegion(PDMDevHlpGetVM(s->CTX_SUFF(pDevIns)), 0x000a0000);
734 s->fRemappedVGA = false;
735 }
736 }
737#endif
738 break;
739
740 case 0x3b4:
741 case 0x3d4:
742 s->cr_index = val;
743 break;
744 case 0x3b5:
745 case 0x3d5:
746#ifdef DEBUG_VGA_REG
747 Log(("vga: write CR%x = 0x%02x\n", s->cr_index, val));
748#endif
749 /* handle CR0-7 protection */
750 if ((s->cr[0x11] & 0x80) && s->cr_index <= 7) {
751 /* can always write bit 4 of CR7 */
752 if (s->cr_index == 7)
753 s->cr[7] = (s->cr[7] & ~0x10) | (val & 0x10);
754 return;
755 }
756 switch(s->cr_index) {
757 case 0x01: /* horizontal display end */
758 case 0x07:
759 case 0x09:
760 case 0x0c:
761 case 0x0d:
762 case 0x12: /* veritcal display end */
763 s->cr[s->cr_index] = val;
764 break;
765
766 default:
767 s->cr[s->cr_index] = val;
768 break;
769 }
770 break;
771 case 0x3ba:
772 case 0x3da:
773 s->fcr = val & 0x10;
774 break;
775 }
776}
777
778#ifdef CONFIG_BOCHS_VBE
779static uint32_t vbe_ioport_read_index(void *opaque, uint32_t addr)
780{
781 VGAState *s = (VGAState*)opaque;
782 uint32_t val;
783 val = s->vbe_index;
784 return val;
785}
786
787static uint32_t vbe_ioport_read_data(void *opaque, uint32_t addr)
788{
789 VGAState *s = (VGAState*)opaque;
790 uint32_t val;
791
792 if (s->vbe_index <= VBE_DISPI_INDEX_NB) {
793 if (s->vbe_regs[VBE_DISPI_INDEX_ENABLE] & VBE_DISPI_GETCAPS) {
794 switch(s->vbe_index) {
795 /* XXX: do not hardcode ? */
796 case VBE_DISPI_INDEX_XRES:
797 val = VBE_DISPI_MAX_XRES;
798 break;
799 case VBE_DISPI_INDEX_YRES:
800 val = VBE_DISPI_MAX_YRES;
801 break;
802 case VBE_DISPI_INDEX_BPP:
803 val = VBE_DISPI_MAX_BPP;
804 break;
805 default:
806 val = s->vbe_regs[s->vbe_index];
807 break;
808 }
809 } else if (s->vbe_index == VBE_DISPI_INDEX_VBOX_VIDEO) {
810 /* Reading from the port means that the old additions are requesting the number of monitors. */
811 val = 1;
812 } else {
813 val = s->vbe_regs[s->vbe_index];
814 }
815 } else {
816 val = 0;
817 }
818#ifdef DEBUG_BOCHS_VBE
819 Log(("VBE: read index=0x%x val=0x%x\n", s->vbe_index, val));
820#endif
821 return val;
822}
823
824static void vbe_ioport_write_index(void *opaque, uint32_t addr, uint32_t val)
825{
826 VGAState *s = (VGAState*)opaque;
827 s->vbe_index = val;
828}
829
830static int vbe_ioport_write_data(void *opaque, uint32_t addr, uint32_t val)
831{
832 VGAState *s = (VGAState*)opaque;
833
834 if (s->vbe_index <= VBE_DISPI_INDEX_NB) {
835#ifdef DEBUG_BOCHS_VBE
836 Log(("VBE: write index=0x%x val=0x%x\n", s->vbe_index, val));
837#endif
838 switch(s->vbe_index) {
839 case VBE_DISPI_INDEX_ID:
840 if (val == VBE_DISPI_ID0 ||
841 val == VBE_DISPI_ID1 ||
842 val == VBE_DISPI_ID2 ||
843 val == VBE_DISPI_ID3 ||
844 val == VBE_DISPI_ID4) {
845 s->vbe_regs[s->vbe_index] = val;
846 }
847#ifdef VBOX
848 if (val == VBE_DISPI_ID_VBOX_VIDEO) {
849 s->vbe_regs[s->vbe_index] = val;
850 }
851#ifdef VBOX_WITH_HGSMI
852 else if (val == VBE_DISPI_ID_HGSMI) {
853 s->vbe_regs[s->vbe_index] = val;
854 }
855#endif /* VBOX_WITH_HGSMI */
856#endif /* VBOX */
857 break;
858 case VBE_DISPI_INDEX_XRES:
859 if ((val <= VBE_DISPI_MAX_XRES) && ((val & 7) == 0)) {
860 s->vbe_regs[s->vbe_index] = val;
861#ifdef KEEP_SCAN_LINE_LENGTH
862 if (s->vbe_regs[VBE_DISPI_INDEX_BPP] == 4)
863 s->vbe_line_offset = val >> 1;
864 else
865 s->vbe_line_offset = val * ((s->vbe_regs[VBE_DISPI_INDEX_BPP] + 7) >> 3);
866 /* XXX: support weird bochs semantics ? */
867 s->vbe_regs[VBE_DISPI_INDEX_VIRT_WIDTH] = s->vbe_line_offset;
868 s->vbe_regs[VBE_DISPI_INDEX_X_OFFSET] = 0;
869 s->vbe_regs[VBE_DISPI_INDEX_Y_OFFSET] = 0;
870 s->vbe_start_addr = 0;
871#endif /* KEEP_SCAN_LINE_LENGTH defined */
872 }
873 break;
874 case VBE_DISPI_INDEX_YRES:
875 if (val <= VBE_DISPI_MAX_YRES) {
876 s->vbe_regs[s->vbe_index] = val;
877#ifdef KEEP_SCAN_LINE_LENGTH
878 s->vbe_regs[VBE_DISPI_INDEX_VIRT_HEIGHT] = val;
879 s->vbe_regs[VBE_DISPI_INDEX_X_OFFSET] = 0;
880 s->vbe_regs[VBE_DISPI_INDEX_Y_OFFSET] = 0;
881 s->vbe_start_addr = 0;
882#endif /* KEEP_SCAN_LINE_LENGTH defined */
883 }
884 break;
885 case VBE_DISPI_INDEX_BPP:
886 if (val == 0)
887 val = 8;
888 if (val == 4 || val == 8 || val == 15 ||
889 val == 16 || val == 24 || val == 32) {
890 s->vbe_regs[s->vbe_index] = val;
891#ifdef KEEP_SCAN_LINE_LENGTH
892 if (val == 4)
893 s->vbe_line_offset = s->vbe_regs[VBE_DISPI_INDEX_XRES] >> 1;
894 else
895 s->vbe_line_offset = s->vbe_regs[VBE_DISPI_INDEX_XRES] * ((val + 7) >> 3);
896 /* XXX: support weird bochs semantics ? */
897 s->vbe_regs[VBE_DISPI_INDEX_VIRT_WIDTH] = s->vbe_line_offset;
898 s->vbe_regs[VBE_DISPI_INDEX_X_OFFSET] = 0;
899 s->vbe_regs[VBE_DISPI_INDEX_Y_OFFSET] = 0;
900 s->vbe_start_addr = 0;
901#endif /* KEEP_SCAN_LINE_LENGTH defined */
902 }
903 break;
904 case VBE_DISPI_INDEX_BANK:
905 if (val > s->vbe_bank_max)
906 val = s->vbe_bank_max;
907 s->vbe_regs[s->vbe_index] = val;
908 s->bank_offset = (val << 16);
909
910#ifndef IN_RC
911 /* The VGA region is (could be) affected by this change; reset all aliases we've created. */
912 if (s->fRemappedVGA)
913 {
914 IOMMMIOResetRegion(PDMDevHlpGetVM(s->CTX_SUFF(pDevIns)), 0x000a0000);
915 s->fRemappedVGA = false;
916 }
917#endif
918 break;
919
920 case VBE_DISPI_INDEX_ENABLE:
921#ifndef IN_RING3
922 return VINF_IOM_HC_IOPORT_WRITE;
923#else
924 if ((val & VBE_DISPI_ENABLED) &&
925 !(s->vbe_regs[VBE_DISPI_INDEX_ENABLE] & VBE_DISPI_ENABLED)) {
926 int h, shift_control;
927#ifdef VBOX
928 /* Check the values before we screw up with a resolution which is too big or small. */
929 size_t cb = s->vbe_regs[VBE_DISPI_INDEX_XRES];
930 if (s->vbe_regs[VBE_DISPI_INDEX_BPP] == 4)
931 cb = s->vbe_regs[VBE_DISPI_INDEX_XRES] >> 1;
932 else
933 cb = s->vbe_regs[VBE_DISPI_INDEX_XRES] * ((s->vbe_regs[VBE_DISPI_INDEX_BPP] + 7) >> 3);
934 cb *= s->vbe_regs[VBE_DISPI_INDEX_YRES];
935#ifndef KEEP_SCAN_LINE_LENGTH
936 if ( !s->vbe_regs[VBE_DISPI_INDEX_XRES]
937 || !s->vbe_regs[VBE_DISPI_INDEX_YRES]
938 || cb > s->vram_size)
939 {
940 AssertMsgFailed(("XRES=%d YRES=%d cb=%d vram_size=%d\n",
941 s->vbe_regs[VBE_DISPI_INDEX_XRES], s->vbe_regs[VBE_DISPI_INDEX_YRES], cb, s->vram_size));
942 return VINF_SUCCESS; /* Note: silent failure like before */
943 }
944#else /* KEEP_SCAN_LINE_LENGTH defined */
945 if ( !s->vbe_regs[VBE_DISPI_INDEX_VIRT_WIDTH]
946 || !s->vbe_regs[VBE_DISPI_INDEX_YRES]
947 || cb > s->vram_size)
948 {
949 AssertMsgFailed(("VIRT WIDTH=%d YRES=%d cb=%d vram_size=%d\n",
950 s->vbe_regs[VBE_DISPI_INDEX_VIRT_WIDTH], s->vbe_regs[VBE_DISPI_INDEX_YRES], cb, s->vram_size));
951 return VINF_SUCCESS; /* Note: silent failure like before */
952 }
953#endif /* KEEP_SCAN_LINE_LENGTH defined */
954#endif /* VBOX */
955
956#ifndef KEEP_SCAN_LINE_LENGTH
957 s->vbe_regs[VBE_DISPI_INDEX_VIRT_WIDTH] =
958 s->vbe_regs[VBE_DISPI_INDEX_XRES];
959 s->vbe_regs[VBE_DISPI_INDEX_VIRT_HEIGHT] =
960 s->vbe_regs[VBE_DISPI_INDEX_YRES];
961 s->vbe_regs[VBE_DISPI_INDEX_X_OFFSET] = 0;
962 s->vbe_regs[VBE_DISPI_INDEX_Y_OFFSET] = 0;
963
964 if (s->vbe_regs[VBE_DISPI_INDEX_BPP] == 4)
965 s->vbe_line_offset = s->vbe_regs[VBE_DISPI_INDEX_XRES] >> 1;
966 else
967 s->vbe_line_offset = s->vbe_regs[VBE_DISPI_INDEX_XRES] *
968 ((s->vbe_regs[VBE_DISPI_INDEX_BPP] + 7) >> 3);
969 s->vbe_start_addr = 0;
970#endif /* KEEP_SCAN_LINE_LENGTH not defined */
971
972 /* clear the screen (should be done in BIOS) */
973 if (!(val & VBE_DISPI_NOCLEARMEM)) {
974#ifndef VBOX
975 memset(s->vram_ptr, 0,
976 s->vbe_regs[VBE_DISPI_INDEX_YRES] * s->vbe_line_offset);
977#else /* VBOX */
978 memset(s->CTX_SUFF(vram_ptr), 0,
979 s->vbe_regs[VBE_DISPI_INDEX_YRES] * s->vbe_line_offset);
980#endif /* VBOX */
981 }
982
983 /* we initialize the VGA graphic mode (should be done
984 in BIOS) */
985 s->gr[0x06] = (s->gr[0x06] & ~0x0c) | 0x05; /* graphic mode + memory map 1 */
986 s->cr[0x17] |= 3; /* no CGA modes */
987 s->cr[0x13] = s->vbe_line_offset >> 3;
988 /* width */
989 s->cr[0x01] = (s->vbe_regs[VBE_DISPI_INDEX_XRES] >> 3) - 1;
990 /* height (only meaningful if < 1024) */
991 h = s->vbe_regs[VBE_DISPI_INDEX_YRES] - 1;
992 s->cr[0x12] = h;
993 s->cr[0x07] = (s->cr[0x07] & ~0x42) |
994 ((h >> 7) & 0x02) | ((h >> 3) & 0x40);
995 /* line compare to 1023 */
996 s->cr[0x18] = 0xff;
997 s->cr[0x07] |= 0x10;
998 s->cr[0x09] |= 0x40;
999
1000 if (s->vbe_regs[VBE_DISPI_INDEX_BPP] == 4) {
1001 shift_control = 0;
1002 s->sr[0x01] &= ~8; /* no double line */
1003 } else {
1004 shift_control = 2;
1005 s->sr[4] |= 0x08; /* set chain 4 mode */
1006 s->sr[2] |= 0x0f; /* activate all planes */
1007 }
1008 s->gr[0x05] = (s->gr[0x05] & ~0x60) | (shift_control << 5);
1009 s->cr[0x09] &= ~0x9f; /* no double scan */
1010#ifdef VBOX
1011 /* sunlover 30.05.2007
1012 * The ar_index remains with bit 0x20 cleared after a switch from fullscreen
1013 * DOS mode on Windows XP guest. That leads to GMODE_BLANK in vga_update_display.
1014 * But the VBE mode is graphics, so not a blank anymore.
1015 */
1016 s->ar_index |= 0x20;
1017#endif /* VBOX */
1018 } else {
1019 /* XXX: the bios should do that */
1020#ifdef VBOX
1021 /* sunlover 21.12.2006
1022 * Here is probably more to reset. When this was executed in GC
1023 * then the *update* functions could not detect a mode change.
1024 * Or may be these update function should take the s->vbe_regs[s->vbe_index]
1025 * into account when detecting a mode change.
1026 *
1027 * The 'mode reset not detected' problem is now fixed by executing the
1028 * VBE_DISPI_INDEX_ENABLE case always in RING3 in order to call the
1029 * LFBChange callback.
1030 */
1031#endif /* VBOX */
1032 s->bank_offset = 0;
1033 }
1034 s->vbe_regs[s->vbe_index] = val;
1035 /*
1036 * LFB video mode is either disabled or changed. This notification
1037 * is used by the display to disable VBVA.
1038 */
1039 s->pDrv->pfnLFBModeChange(s->pDrv, (val & VBE_DISPI_ENABLED) != 0);
1040
1041 /* The VGA region is (could be) affected by this change; reset all aliases we've created. */
1042 if (s->fRemappedVGA)
1043 {
1044 IOMMMIOResetRegion(PDMDevHlpGetVM(s->CTX_SUFF(pDevIns)), 0x000a0000);
1045 s->fRemappedVGA = false;
1046 }
1047 break;
1048#endif /* IN_RING3 */
1049 case VBE_DISPI_INDEX_VIRT_WIDTH:
1050 {
1051 int w, h, line_offset;
1052
1053 if (val < s->vbe_regs[VBE_DISPI_INDEX_XRES])
1054 return VINF_SUCCESS;
1055 w = val;
1056 if (s->vbe_regs[VBE_DISPI_INDEX_BPP] == 4)
1057 line_offset = w >> 1;
1058 else
1059 line_offset = w * ((s->vbe_regs[VBE_DISPI_INDEX_BPP] + 7) >> 3);
1060 h = s->vram_size / line_offset;
1061 /* XXX: support weird bochs semantics ? */
1062 if (h < s->vbe_regs[VBE_DISPI_INDEX_YRES])
1063 return VINF_SUCCESS;
1064 s->vbe_regs[VBE_DISPI_INDEX_VIRT_WIDTH] = w;
1065 s->vbe_regs[VBE_DISPI_INDEX_VIRT_HEIGHT] = h;
1066 s->vbe_line_offset = line_offset;
1067 }
1068 break;
1069 case VBE_DISPI_INDEX_X_OFFSET:
1070 case VBE_DISPI_INDEX_Y_OFFSET:
1071 {
1072 int x;
1073 s->vbe_regs[s->vbe_index] = val;
1074 s->vbe_start_addr = s->vbe_line_offset * s->vbe_regs[VBE_DISPI_INDEX_Y_OFFSET];
1075 x = s->vbe_regs[VBE_DISPI_INDEX_X_OFFSET];
1076 if (s->vbe_regs[VBE_DISPI_INDEX_BPP] == 4)
1077 s->vbe_start_addr += x >> 1;
1078 else
1079 s->vbe_start_addr += x * ((s->vbe_regs[VBE_DISPI_INDEX_BPP] + 7) >> 3);
1080 s->vbe_start_addr >>= 2;
1081 }
1082 break;
1083 case VBE_DISPI_INDEX_VBOX_VIDEO:
1084#ifdef VBOX
1085#ifndef IN_RING3
1086 return VINF_IOM_HC_IOPORT_WRITE;
1087#else
1088 /* Changes in the VGA device are minimal. The device is bypassed. The driver does all work. */
1089 if (val == VBOX_VIDEO_DISABLE_ADAPTER_MEMORY)
1090 {
1091 s->pDrv->pfnProcessAdapterData(s->pDrv, NULL, 0);
1092 }
1093 else if (val == VBOX_VIDEO_INTERPRET_ADAPTER_MEMORY)
1094 {
1095 s->pDrv->pfnProcessAdapterData(s->pDrv, s->CTX_SUFF(vram_ptr), s->vram_size);
1096 }
1097 else if ((val & 0xFFFF0000) == VBOX_VIDEO_INTERPRET_DISPLAY_MEMORY_BASE)
1098 {
1099 s->pDrv->pfnProcessDisplayData(s->pDrv, s->CTX_SUFF(vram_ptr), val & 0xFFFF);
1100 }
1101#endif /* IN_RING3 */
1102#endif /* VBOX */
1103 break;
1104 default:
1105 break;
1106 }
1107 }
1108 return VINF_SUCCESS;
1109}
1110#endif
1111
1112/* called for accesses between 0xa0000 and 0xc0000 */
1113#ifdef VBOX
1114static uint32_t vga_mem_readb(void *opaque, target_phys_addr_t addr, int *prc)
1115#else
1116uint32_t vga_mem_readb(void *opaque, target_phys_addr_t addr)
1117#endif /* VBOX */
1118{
1119 VGAState *s = (VGAState*)opaque;
1120 int memory_map_mode, plane;
1121 uint32_t ret;
1122
1123#ifdef DEBUG_VGA_MEM
1124 Log(("vga: read [0x%x] -> ", addr));
1125#endif
1126 /* convert to VGA memory offset */
1127 memory_map_mode = (s->gr[6] >> 2) & 3;
1128#ifdef VBOX
1129 RTGCPHYS GCPhys = addr; /* save original address */
1130#endif
1131 addr &= 0x1ffff;
1132 switch(memory_map_mode) {
1133 case 0:
1134 break;
1135 case 1:
1136 if (addr >= 0x10000)
1137 return 0xff;
1138 addr += s->bank_offset;
1139 break;
1140 case 2:
1141 addr -= 0x10000;
1142 if (addr >= 0x8000)
1143 return 0xff;
1144 break;
1145 default:
1146 case 3:
1147 addr -= 0x18000;
1148 if (addr >= 0x8000)
1149 return 0xff;
1150 break;
1151 }
1152
1153 if (s->sr[4] & 0x08) {
1154 /* chain 4 mode : simplest access */
1155#ifndef VBOX
1156 ret = s->vram_ptr[addr];
1157#else /* VBOX */
1158# ifndef IN_RC
1159 /* If all planes are accessible, then map the page to the frame buffer and make it writable. */
1160 if ( (s->sr[2] & 3) == 3
1161 && !vga_is_dirty(s, addr))
1162 {
1163 /** @todo only allow read access (doesn't work now) */
1164 STAM_COUNTER_INC(&s->StatMapPage);
1165 IOMMMIOMapMMIO2Page(PDMDevHlpGetVM(s->CTX_SUFF(pDevIns)), GCPhys, s->GCPhysVRAM + addr, X86_PTE_RW|X86_PTE_P);
1166 /* Set as dirty as write accesses won't be noticed now. */
1167 vga_set_dirty(s, addr);
1168 s->fRemappedVGA = true;
1169 }
1170# endif /* IN_RC */
1171 VERIFY_VRAM_READ_OFF_RETURN(s, addr, *prc);
1172 ret = s->CTX_SUFF(vram_ptr)[addr];
1173#endif /* VBOX */
1174 } else if (!(s->sr[4] & 0x04)) { /* Host access is controlled by SR4, not GR5! */
1175 /* odd/even mode (aka text mode mapping) */
1176 plane = (s->gr[4] & 2) | (addr & 1);
1177#ifndef VBOX
1178 ret = s->vram_ptr[((addr & ~1) << 1) | plane];
1179#else /* VBOX */
1180 /* See the comment for a similar line in vga_mem_writeb. */
1181 RTGCPHYS off = ((addr & ~1) << 2) | plane;
1182 VERIFY_VRAM_READ_OFF_RETURN(s, off, *prc);
1183 ret = s->CTX_SUFF(vram_ptr)[off];
1184#endif /* VBOX */
1185 } else {
1186 /* standard VGA latched access */
1187#ifndef VBOX
1188 s->latch = ((uint32_t *)s->vram_ptr)[addr];
1189#else /* VBOX */
1190 VERIFY_VRAM_READ_OFF_RETURN(s, addr, *prc);
1191 s->latch = ((uint32_t *)s->CTX_SUFF(vram_ptr))[addr];
1192#endif /* VBOX */
1193
1194 if (!(s->gr[5] & 0x08)) {
1195 /* read mode 0 */
1196 plane = s->gr[4];
1197 ret = GET_PLANE(s->latch, plane);
1198 } else {
1199 /* read mode 1 */
1200 ret = (s->latch ^ mask16[s->gr[2]]) & mask16[s->gr[7]];
1201 ret |= ret >> 16;
1202 ret |= ret >> 8;
1203 ret = (~ret) & 0xff;
1204 }
1205 }
1206#ifdef DEBUG_VGA_MEM
1207 Log((" 0x%02x\n", ret));
1208#endif
1209 return ret;
1210}
1211
1212#ifndef VBOX
1213static uint32_t vga_mem_readw(void *opaque, target_phys_addr_t addr)
1214{
1215 uint32_t v;
1216#ifdef TARGET_WORDS_BIGENDIAN
1217 v = vga_mem_readb(opaque, addr) << 8;
1218 v |= vga_mem_readb(opaque, addr + 1);
1219#else
1220 v = vga_mem_readb(opaque, addr);
1221 v |= vga_mem_readb(opaque, addr + 1) << 8;
1222#endif
1223 return v;
1224}
1225
1226static uint32_t vga_mem_readl(void *opaque, target_phys_addr_t addr)
1227{
1228 uint32_t v;
1229#ifdef TARGET_WORDS_BIGENDIAN
1230 v = vga_mem_readb(opaque, addr) << 24;
1231 v |= vga_mem_readb(opaque, addr + 1) << 16;
1232 v |= vga_mem_readb(opaque, addr + 2) << 8;
1233 v |= vga_mem_readb(opaque, addr + 3);
1234#else
1235 v = vga_mem_readb(opaque, addr);
1236 v |= vga_mem_readb(opaque, addr + 1) << 8;
1237 v |= vga_mem_readb(opaque, addr + 2) << 16;
1238 v |= vga_mem_readb(opaque, addr + 3) << 24;
1239#endif
1240 return v;
1241}
1242#endif /* !VBOX */
1243
1244/* called for accesses between 0xa0000 and 0xc0000 */
1245#ifdef VBOX
1246static
1247#endif /* VBOX */
1248int vga_mem_writeb(void *opaque, target_phys_addr_t addr, uint32_t val)
1249{
1250 VGAState *s = (VGAState*)opaque;
1251 int memory_map_mode, plane, write_mode, b, func_select, mask;
1252 uint32_t write_mask, bit_mask, set_mask;
1253
1254#ifdef DEBUG_VGA_MEM
1255 Log(("vga: [0x%x] = 0x%02x\n", addr, val));
1256#endif
1257 /* convert to VGA memory offset */
1258 memory_map_mode = (s->gr[6] >> 2) & 3;
1259#ifdef VBOX
1260 RTGCPHYS GCPhys = addr; /* save original address */
1261#endif
1262 addr &= 0x1ffff;
1263 switch(memory_map_mode) {
1264 case 0:
1265 break;
1266 case 1:
1267 if (addr >= 0x10000)
1268 return VINF_SUCCESS;
1269 addr += s->bank_offset;
1270 break;
1271 case 2:
1272 addr -= 0x10000;
1273 if (addr >= 0x8000)
1274 return VINF_SUCCESS;
1275 break;
1276 default:
1277 case 3:
1278 addr -= 0x18000;
1279 if (addr >= 0x8000)
1280 return VINF_SUCCESS;
1281 break;
1282 }
1283
1284 if (s->sr[4] & 0x08) {
1285 /* chain 4 mode : simplest access */
1286 plane = addr & 3;
1287 mask = (1 << plane);
1288 if (s->sr[2] & mask) {
1289#ifndef VBOX
1290 s->vram_ptr[addr] = val;
1291#else /* VBOX */
1292# ifndef IN_RC
1293 /* If all planes are accessible, then map the page to the frame buffer and make it writable. */
1294 if ( (s->sr[2] & 3) == 3
1295 && !vga_is_dirty(s, addr))
1296 {
1297 STAM_COUNTER_INC(&s->StatMapPage);
1298 IOMMMIOMapMMIO2Page(PDMDevHlpGetVM(s->CTX_SUFF(pDevIns)), GCPhys, s->GCPhysVRAM + addr, X86_PTE_RW | X86_PTE_P);
1299 s->fRemappedVGA = true;
1300 }
1301# endif /* IN_RC */
1302
1303 VERIFY_VRAM_WRITE_OFF_RETURN(s, addr);
1304 s->CTX_SUFF(vram_ptr)[addr] = val;
1305#endif /* VBOX */
1306#ifdef DEBUG_VGA_MEM
1307 Log(("vga: chain4: [0x%x]\n", addr));
1308#endif
1309 s->plane_updated |= mask; /* only used to detect font change */
1310#ifndef VBOX
1311 cpu_physical_memory_set_dirty(s->vram_offset + addr);
1312#else /* VBOX */
1313 vga_set_dirty(s, addr);
1314#endif /* VBOX */
1315 }
1316 } else if (!(s->sr[4] & 0x04)) { /* Host access is controlled by SR4, not GR5! */
1317 /* odd/even mode (aka text mode mapping) */
1318 plane = (s->gr[4] & 2) | (addr & 1);
1319 mask = (1 << plane);
1320 if (s->sr[2] & mask) {
1321#ifndef VBOX
1322 addr = ((addr & ~1) << 1) | plane;
1323#else
1324 /* 'addr' is offset in a plane, bit 0 selects the plane.
1325 * Mask the bit 0, convert plane index to vram offset,
1326 * that is multiply by the number of planes,
1327 * and select the plane byte in the vram offset.
1328 */
1329 addr = ((addr & ~1) << 2) | plane;
1330#endif /* VBOX */
1331#ifndef VBOX
1332 s->vram_ptr[addr] = val;
1333#else /* VBOX */
1334 VERIFY_VRAM_WRITE_OFF_RETURN(s, addr);
1335 s->CTX_SUFF(vram_ptr)[addr] = val;
1336#endif /* VBOX */
1337#ifdef DEBUG_VGA_MEM
1338 Log(("vga: odd/even: [0x%x]\n", addr));
1339#endif
1340 s->plane_updated |= mask; /* only used to detect font change */
1341#ifndef VBOX
1342 cpu_physical_memory_set_dirty(s->vram_offset + addr);
1343#else /* VBOX */
1344 vga_set_dirty(s, addr);
1345#endif /* VBOX */
1346 }
1347 } else {
1348 /* standard VGA latched access */
1349 VERIFY_VRAM_WRITE_OFF_RETURN(s, addr * 4 + 3);
1350
1351#ifdef IN_RING0
1352 if (((++s->cLatchAccesses) & s->uMaskLatchAccess) == s->uMaskLatchAccess)
1353 {
1354 static uint32_t const s_aMask[5] = { 0x3ff, 0x1ff, 0x7f, 0x3f, 0x1f};
1355 static uint64_t const s_aDelta[5] = {10000000, 5000000, 2500000, 1250000, 625000};
1356 if (PDMDevHlpCanEmulateIoBlock(s->CTX_SUFF(pDevIns)))
1357 {
1358 uint64_t u64CurTime = RTTimeSystemNanoTS();
1359
1360 /* About 1000 (or more) accesses per 10 ms will trigger a reschedule
1361 * to the recompiler
1362 */
1363 if (u64CurTime - s->u64LastLatchedAccess < s_aDelta[s->iMask])
1364 {
1365 s->u64LastLatchedAccess = 0;
1366 s->iMask = RT_MIN(s->iMask + 1U, RT_ELEMENTS(s_aMask) - 1U);
1367 s->uMaskLatchAccess = s_aMask[s->iMask];
1368 s->cLatchAccesses = s->uMaskLatchAccess - 1;
1369 return VINF_EM_RAW_EMULATE_IO_BLOCK;
1370 }
1371 if (s->u64LastLatchedAccess)
1372 {
1373 Log2(("Reset mask (was %d) delta %RX64 (limit %x)\n", s->iMask, u64CurTime - s->u64LastLatchedAccess, s_aDelta[s->iMask]));
1374 if (s->iMask)
1375 s->iMask--;
1376 s->uMaskLatchAccess = s_aMask[s->iMask];
1377 }
1378 s->u64LastLatchedAccess = u64CurTime;
1379 }
1380 else
1381 {
1382 s->u64LastLatchedAccess = 0;
1383 s->iMask = 0;
1384 s->uMaskLatchAccess = s_aMask[s->iMask];
1385 s->cLatchAccesses = 0;
1386 }
1387 }
1388#endif
1389
1390 write_mode = s->gr[5] & 3;
1391 switch(write_mode) {
1392 default:
1393 case 0:
1394 /* rotate */
1395 b = s->gr[3] & 7;
1396 val = ((val >> b) | (val << (8 - b))) & 0xff;
1397 val |= val << 8;
1398 val |= val << 16;
1399
1400 /* apply set/reset mask */
1401 set_mask = mask16[s->gr[1]];
1402 val = (val & ~set_mask) | (mask16[s->gr[0]] & set_mask);
1403 bit_mask = s->gr[8];
1404 break;
1405 case 1:
1406 val = s->latch;
1407 goto do_write;
1408 case 2:
1409 val = mask16[val & 0x0f];
1410 bit_mask = s->gr[8];
1411 break;
1412 case 3:
1413 /* rotate */
1414 b = s->gr[3] & 7;
1415 val = (val >> b) | (val << (8 - b));
1416
1417 bit_mask = s->gr[8] & val;
1418 val = mask16[s->gr[0]];
1419 break;
1420 }
1421
1422 /* apply logical operation */
1423 func_select = s->gr[3] >> 3;
1424 switch(func_select) {
1425 case 0:
1426 default:
1427 /* nothing to do */
1428 break;
1429 case 1:
1430 /* and */
1431 val &= s->latch;
1432 break;
1433 case 2:
1434 /* or */
1435 val |= s->latch;
1436 break;
1437 case 3:
1438 /* xor */
1439 val ^= s->latch;
1440 break;
1441 }
1442
1443 /* apply bit mask */
1444 bit_mask |= bit_mask << 8;
1445 bit_mask |= bit_mask << 16;
1446 val = (val & bit_mask) | (s->latch & ~bit_mask);
1447
1448 do_write:
1449 /* mask data according to sr[2] */
1450 mask = s->sr[2];
1451 s->plane_updated |= mask; /* only used to detect font change */
1452 write_mask = mask16[mask];
1453#ifndef VBOX
1454 ((uint32_t *)s->vram_ptr)[addr] =
1455 (((uint32_t *)s->vram_ptr)[addr] & ~write_mask) |
1456 (val & write_mask);
1457#else /* VBOX */
1458 ((uint32_t *)s->CTX_SUFF(vram_ptr))[addr] =
1459 (((uint32_t *)s->CTX_SUFF(vram_ptr))[addr] & ~write_mask) |
1460 (val & write_mask);
1461#endif /* VBOX */
1462#ifdef DEBUG_VGA_MEM
1463 Log(("vga: latch: [0x%x] mask=0x%08x val=0x%08x\n",
1464 addr * 4, write_mask, val));
1465#endif
1466#ifndef VBOX
1467 cpu_physical_memory_set_dirty(s->vram_offset + (addr << 2));
1468#else /* VBOX */
1469 vga_set_dirty(s, (addr << 2));
1470#endif /* VBOX */
1471 }
1472
1473 return VINF_SUCCESS;
1474}
1475
1476#ifndef VBOX
1477static void vga_mem_writew(void *opaque, target_phys_addr_t addr, uint32_t val)
1478{
1479#ifdef TARGET_WORDS_BIGENDIAN
1480 vga_mem_writeb(opaque, addr, (val >> 8) & 0xff);
1481 vga_mem_writeb(opaque, addr + 1, val & 0xff);
1482#else
1483 vga_mem_writeb(opaque, addr, val & 0xff);
1484 vga_mem_writeb(opaque, addr + 1, (val >> 8) & 0xff);
1485#endif
1486}
1487
1488static void vga_mem_writel(void *opaque, target_phys_addr_t addr, uint32_t val)
1489{
1490#ifdef TARGET_WORDS_BIGENDIAN
1491 vga_mem_writeb(opaque, addr, (val >> 24) & 0xff);
1492 vga_mem_writeb(opaque, addr + 1, (val >> 16) & 0xff);
1493 vga_mem_writeb(opaque, addr + 2, (val >> 8) & 0xff);
1494 vga_mem_writeb(opaque, addr + 3, val & 0xff);
1495#else
1496 vga_mem_writeb(opaque, addr, val & 0xff);
1497 vga_mem_writeb(opaque, addr + 1, (val >> 8) & 0xff);
1498 vga_mem_writeb(opaque, addr + 2, (val >> 16) & 0xff);
1499 vga_mem_writeb(opaque, addr + 3, (val >> 24) & 0xff);
1500#endif
1501}
1502#endif /* !VBOX */
1503
1504#if !defined(VBOX) || defined(IN_RING3)
1505typedef void vga_draw_glyph8_func(uint8_t *d, int linesize,
1506 const uint8_t *font_ptr, int h,
1507 uint32_t fgcol, uint32_t bgcol);
1508typedef void vga_draw_glyph9_func(uint8_t *d, int linesize,
1509 const uint8_t *font_ptr, int h,
1510 uint32_t fgcol, uint32_t bgcol, int dup9);
1511typedef void vga_draw_line_func(VGAState *s1, uint8_t *d,
1512 const uint8_t *s, int width);
1513
1514static inline unsigned int rgb_to_pixel8(unsigned int r, unsigned int g, unsigned b)
1515{
1516 return ((r >> 5) << 5) | ((g >> 5) << 2) | (b >> 6);
1517}
1518
1519static inline unsigned int rgb_to_pixel15(unsigned int r, unsigned int g, unsigned b)
1520{
1521 return ((r >> 3) << 10) | ((g >> 3) << 5) | (b >> 3);
1522}
1523
1524static inline unsigned int rgb_to_pixel16(unsigned int r, unsigned int g, unsigned b)
1525{
1526 return ((r >> 3) << 11) | ((g >> 2) << 5) | (b >> 3);
1527}
1528
1529static inline unsigned int rgb_to_pixel32(unsigned int r, unsigned int g, unsigned b)
1530{
1531 return (r << 16) | (g << 8) | b;
1532}
1533
1534#define DEPTH 8
1535#include "DevVGATmpl.h"
1536
1537#define DEPTH 15
1538#include "DevVGATmpl.h"
1539
1540#define DEPTH 16
1541#include "DevVGATmpl.h"
1542
1543#define DEPTH 32
1544#include "DevVGATmpl.h"
1545
1546static unsigned int rgb_to_pixel8_dup(unsigned int r, unsigned int g, unsigned b)
1547{
1548 unsigned int col;
1549 col = rgb_to_pixel8(r, g, b);
1550 col |= col << 8;
1551 col |= col << 16;
1552 return col;
1553}
1554
1555static unsigned int rgb_to_pixel15_dup(unsigned int r, unsigned int g, unsigned b)
1556{
1557 unsigned int col;
1558 col = rgb_to_pixel15(r, g, b);
1559 col |= col << 16;
1560 return col;
1561}
1562
1563static unsigned int rgb_to_pixel16_dup(unsigned int r, unsigned int g, unsigned b)
1564{
1565 unsigned int col;
1566 col = rgb_to_pixel16(r, g, b);
1567 col |= col << 16;
1568 return col;
1569}
1570
1571static unsigned int rgb_to_pixel32_dup(unsigned int r, unsigned int g, unsigned b)
1572{
1573 unsigned int col;
1574 col = rgb_to_pixel32(r, g, b);
1575 return col;
1576}
1577
1578/* return true if the palette was modified */
1579static int update_palette16(VGAState *s)
1580{
1581 int full_update, i;
1582 uint32_t v, col, *palette;
1583
1584 full_update = 0;
1585 palette = s->last_palette;
1586 for(i = 0; i < 16; i++) {
1587 v = s->ar[i];
1588 if (s->ar[0x10] & 0x80)
1589 v = ((s->ar[0x14] & 0xf) << 4) | (v & 0xf);
1590 else
1591 v = ((s->ar[0x14] & 0xc) << 4) | (v & 0x3f);
1592 v = v * 3;
1593 col = s->rgb_to_pixel(c6_to_8(s->palette[v]),
1594 c6_to_8(s->palette[v + 1]),
1595 c6_to_8(s->palette[v + 2]));
1596 if (col != palette[i]) {
1597 full_update = 1;
1598 palette[i] = col;
1599 }
1600 }
1601 return full_update;
1602}
1603
1604/* return true if the palette was modified */
1605static int update_palette256(VGAState *s)
1606{
1607 int full_update, i;
1608 uint32_t v, col, *palette;
1609 int wide_dac;
1610
1611 full_update = 0;
1612 palette = s->last_palette;
1613 v = 0;
1614 wide_dac = (s->vbe_regs[VBE_DISPI_INDEX_ENABLE] & (VBE_DISPI_ENABLED | VBE_DISPI_8BIT_DAC))
1615 == (VBE_DISPI_ENABLED | VBE_DISPI_8BIT_DAC);
1616 for(i = 0; i < 256; i++) {
1617 if (wide_dac)
1618 col = s->rgb_to_pixel(s->palette[v],
1619 s->palette[v + 1],
1620 s->palette[v + 2]);
1621 else
1622 col = s->rgb_to_pixel(c6_to_8(s->palette[v]),
1623 c6_to_8(s->palette[v + 1]),
1624 c6_to_8(s->palette[v + 2]));
1625 if (col != palette[i]) {
1626 full_update = 1;
1627 palette[i] = col;
1628 }
1629 v += 3;
1630 }
1631 return full_update;
1632}
1633
1634static void vga_get_offsets(VGAState *s,
1635 uint32_t *pline_offset,
1636 uint32_t *pstart_addr,
1637 uint32_t *pline_compare)
1638{
1639 uint32_t start_addr, line_offset, line_compare;
1640#ifdef CONFIG_BOCHS_VBE
1641 if (s->vbe_regs[VBE_DISPI_INDEX_ENABLE] & VBE_DISPI_ENABLED) {
1642 line_offset = s->vbe_line_offset;
1643 start_addr = s->vbe_start_addr;
1644 line_compare = 65535;
1645 } else
1646#endif
1647 {
1648 /* compute line_offset in bytes */
1649 line_offset = s->cr[0x13];
1650 line_offset <<= 3;
1651#ifdef VBOX
1652 if (!(s->cr[0x14] & 0x40) && !(s->cr[0x17] & 0x40))
1653 {
1654 /* Word mode. Used for odd/even modes. */
1655 line_offset *= 2;
1656 }
1657#endif /* VBOX */
1658
1659 /* starting address */
1660 start_addr = s->cr[0x0d] | (s->cr[0x0c] << 8);
1661
1662 /* line compare */
1663 line_compare = s->cr[0x18] |
1664 ((s->cr[0x07] & 0x10) << 4) |
1665 ((s->cr[0x09] & 0x40) << 3);
1666 }
1667 *pline_offset = line_offset;
1668 *pstart_addr = start_addr;
1669 *pline_compare = line_compare;
1670}
1671
1672/* update start_addr and line_offset. Return TRUE if modified */
1673static int update_basic_params(VGAState *s)
1674{
1675 int full_update;
1676 uint32_t start_addr, line_offset, line_compare;
1677
1678 full_update = 0;
1679
1680 s->get_offsets(s, &line_offset, &start_addr, &line_compare);
1681
1682 if (line_offset != s->line_offset ||
1683 start_addr != s->start_addr ||
1684 line_compare != s->line_compare) {
1685 s->line_offset = line_offset;
1686 s->start_addr = start_addr;
1687 s->line_compare = line_compare;
1688 full_update = 1;
1689 }
1690 return full_update;
1691}
1692
1693static inline int get_depth_index(int depth)
1694{
1695 switch(depth) {
1696 default:
1697 case 8:
1698 return 0;
1699 case 15:
1700 return 1;
1701 case 16:
1702 return 2;
1703 case 32:
1704 return 3;
1705 }
1706}
1707
1708static vga_draw_glyph8_func *vga_draw_glyph8_table[4] = {
1709 vga_draw_glyph8_8,
1710 vga_draw_glyph8_16,
1711 vga_draw_glyph8_16,
1712 vga_draw_glyph8_32,
1713};
1714
1715static vga_draw_glyph8_func *vga_draw_glyph16_table[4] = {
1716 vga_draw_glyph16_8,
1717 vga_draw_glyph16_16,
1718 vga_draw_glyph16_16,
1719 vga_draw_glyph16_32,
1720};
1721
1722static vga_draw_glyph9_func *vga_draw_glyph9_table[4] = {
1723 vga_draw_glyph9_8,
1724 vga_draw_glyph9_16,
1725 vga_draw_glyph9_16,
1726 vga_draw_glyph9_32,
1727};
1728
1729static const uint8_t cursor_glyph[32 * 4] = {
1730 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
1731 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
1732 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
1733 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
1734 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
1735 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
1736 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
1737 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
1738 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
1739 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
1740 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
1741 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
1742 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
1743 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
1744 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
1745 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
1746};
1747
1748/*
1749 * Text mode update
1750 * Missing:
1751 * - double scan
1752 * - double width
1753 * - underline
1754 * - flashing
1755 */
1756#ifndef VBOX
1757static void vga_draw_text(VGAState *s, int full_update)
1758#else
1759static int vga_draw_text(VGAState *s, int full_update)
1760#endif /* !VBOX */
1761{
1762 int cx, cy, cheight, cw, ch, cattr, height, width, ch_attr;
1763 int cx_min, cx_max, linesize, x_incr;
1764 uint32_t offset, fgcol, bgcol, v, cursor_offset;
1765 uint8_t *d1, *d, *src, *s1, *dest, *cursor_ptr;
1766 const uint8_t *font_ptr, *font_base[2];
1767 int dup9, line_offset, depth_index;
1768 uint32_t *palette;
1769 uint32_t *ch_attr_ptr;
1770 vga_draw_glyph8_func *vga_draw_glyph8;
1771 vga_draw_glyph9_func *vga_draw_glyph9;
1772
1773 full_update |= update_palette16(s);
1774 palette = s->last_palette;
1775
1776 /* compute font data address (in plane 2) */
1777 v = s->sr[3];
1778 offset = (((v >> 4) & 1) | ((v << 1) & 6)) * 8192 * 4 + 2;
1779 if (offset != s->font_offsets[0]) {
1780 s->font_offsets[0] = offset;
1781 full_update = 1;
1782 }
1783#ifndef VBOX
1784 font_base[0] = s->vram_ptr + offset;
1785#else /* VBOX */
1786 font_base[0] = s->CTX_SUFF(vram_ptr) + offset;
1787#endif /* VBOX */
1788
1789 offset = (((v >> 5) & 1) | ((v >> 1) & 6)) * 8192 * 4 + 2;
1790#ifndef VBOX
1791 font_base[1] = s->vram_ptr + offset;
1792#else /* VBOX */
1793 font_base[1] = s->CTX_SUFF(vram_ptr) + offset;
1794#endif /* VBOX */
1795 if (offset != s->font_offsets[1]) {
1796 s->font_offsets[1] = offset;
1797 full_update = 1;
1798 }
1799 if (s->plane_updated & (1 << 2)) {
1800 /* if the plane 2 was modified since the last display, it
1801 indicates the font may have been modified */
1802 s->plane_updated = 0;
1803 full_update = 1;
1804 }
1805 full_update |= update_basic_params(s);
1806
1807 line_offset = s->line_offset;
1808#ifndef VBOX
1809 s1 = s->vram_ptr + (s->start_addr * 4);
1810#else /* VBOX */
1811 s1 = s->CTX_SUFF(vram_ptr) + (s->start_addr * 8);
1812#endif /* VBOX */
1813
1814 /* total width & height */
1815 cheight = (s->cr[9] & 0x1f) + 1;
1816 cw = 8;
1817 if (!(s->sr[1] & 0x01))
1818 cw = 9;
1819 if (s->sr[1] & 0x08)
1820 cw = 16; /* NOTE: no 18 pixel wide */
1821#ifndef VBOX
1822 x_incr = cw * ((s->ds->depth + 7) >> 3);
1823#else /* VBOX */
1824 x_incr = cw * ((s->pDrv->cBits + 7) >> 3);
1825#endif /* VBOX */
1826 width = (s->cr[0x01] + 1);
1827 if (s->cr[0x06] == 100) {
1828 /* ugly hack for CGA 160x100x16 - explain me the logic */
1829 height = 100;
1830 } else {
1831 height = s->cr[0x12] |
1832 ((s->cr[0x07] & 0x02) << 7) |
1833 ((s->cr[0x07] & 0x40) << 3);
1834 height = (height + 1) / cheight;
1835 }
1836 if ((height * width) > CH_ATTR_SIZE) {
1837 /* better than nothing: exit if transient size is too big */
1838#ifndef VBOX
1839 return;
1840#else
1841 return VINF_SUCCESS;
1842#endif /* VBOX */
1843 }
1844
1845 if (width != (int)s->last_width || height != (int)s->last_height ||
1846 cw != s->last_cw || cheight != s->last_ch) {
1847 s->last_scr_width = width * cw;
1848 s->last_scr_height = height * cheight;
1849#ifndef VBOX
1850 dpy_resize(s->ds, s->last_scr_width, s->last_scr_height);
1851 s->last_width = width;
1852 s->last_height = height;
1853 s->last_ch = cheight;
1854 s->last_cw = cw;
1855 full_update = 1;
1856#else /* VBOX */
1857 /* For text modes the direct use of guest VRAM is not implemented, so bpp and cbLine are 0 here. */
1858 int rc = s->pDrv->pfnResize(s->pDrv, 0, NULL, 0, s->last_scr_width, s->last_scr_height);
1859 s->last_width = width;
1860 s->last_height = height;
1861 s->last_ch = cheight;
1862 s->last_cw = cw;
1863 full_update = 1;
1864 if (rc == VINF_VGA_RESIZE_IN_PROGRESS)
1865 return rc;
1866 AssertRC(rc);
1867#endif /* VBOX */
1868 }
1869 cursor_offset = ((s->cr[0x0e] << 8) | s->cr[0x0f]) - s->start_addr;
1870 if (cursor_offset != s->cursor_offset ||
1871 s->cr[0xa] != s->cursor_start ||
1872 s->cr[0xb] != s->cursor_end) {
1873 /* if the cursor position changed, we update the old and new
1874 chars */
1875 if (s->cursor_offset < CH_ATTR_SIZE)
1876 s->last_ch_attr[s->cursor_offset] = ~0;
1877 if (cursor_offset < CH_ATTR_SIZE)
1878 s->last_ch_attr[cursor_offset] = ~0;
1879 s->cursor_offset = cursor_offset;
1880 s->cursor_start = s->cr[0xa];
1881 s->cursor_end = s->cr[0xb];
1882 }
1883#ifndef VBOX
1884 cursor_ptr = s->vram_ptr + (s->start_addr + cursor_offset) * 4;
1885
1886 depth_index = get_depth_index(s->ds->depth);
1887#else /* VBOX */
1888 cursor_ptr = s->CTX_SUFF(vram_ptr) + (s->start_addr + cursor_offset) * 8;
1889 depth_index = get_depth_index(s->pDrv->cBits);
1890#endif /* VBOX */
1891 if (cw == 16)
1892 vga_draw_glyph8 = vga_draw_glyph16_table[depth_index];
1893 else
1894 vga_draw_glyph8 = vga_draw_glyph8_table[depth_index];
1895 vga_draw_glyph9 = vga_draw_glyph9_table[depth_index];
1896
1897#ifndef VBOX
1898 dest = s->ds->data;
1899 linesize = s->ds->linesize;
1900#else /* VBOX */
1901 dest = s->pDrv->pu8Data;
1902 linesize = s->pDrv->cbScanline;
1903#endif /* VBOX */
1904 ch_attr_ptr = s->last_ch_attr;
1905
1906 for(cy = 0; cy < height; cy++) {
1907 d1 = dest;
1908 src = s1;
1909 cx_min = width;
1910 cx_max = -1;
1911 for(cx = 0; cx < width; cx++) {
1912 ch_attr = *(uint16_t *)src;
1913 if (full_update || ch_attr != (int)*ch_attr_ptr) {
1914 if (cx < cx_min)
1915 cx_min = cx;
1916 if (cx > cx_max)
1917 cx_max = cx;
1918 *ch_attr_ptr = ch_attr;
1919#ifdef WORDS_BIGENDIAN
1920 ch = ch_attr >> 8;
1921 cattr = ch_attr & 0xff;
1922#else
1923 ch = ch_attr & 0xff;
1924 cattr = ch_attr >> 8;
1925#endif
1926 font_ptr = font_base[(cattr >> 3) & 1];
1927 font_ptr += 32 * 4 * ch;
1928 bgcol = palette[cattr >> 4];
1929 fgcol = palette[cattr & 0x0f];
1930 if (cw != 9) {
1931 vga_draw_glyph8(d1, linesize,
1932 font_ptr, cheight, fgcol, bgcol);
1933 } else {
1934 dup9 = 0;
1935 if (ch >= 0xb0 && ch <= 0xdf && (s->ar[0x10] & 0x04))
1936 dup9 = 1;
1937 vga_draw_glyph9(d1, linesize,
1938 font_ptr, cheight, fgcol, bgcol, dup9);
1939 }
1940 if (src == cursor_ptr &&
1941 !(s->cr[0x0a] & 0x20)) {
1942 int line_start, line_last, h;
1943 /* draw the cursor */
1944 line_start = s->cr[0x0a] & 0x1f;
1945 line_last = s->cr[0x0b] & 0x1f;
1946 /* XXX: check that */
1947 if (line_last > cheight - 1)
1948 line_last = cheight - 1;
1949 if (line_last >= line_start && line_start < cheight) {
1950 h = line_last - line_start + 1;
1951 d = d1 + linesize * line_start;
1952 if (cw != 9) {
1953 vga_draw_glyph8(d, linesize,
1954 cursor_glyph, h, fgcol, bgcol);
1955 } else {
1956 vga_draw_glyph9(d, linesize,
1957 cursor_glyph, h, fgcol, bgcol, 1);
1958 }
1959 }
1960 }
1961 }
1962 d1 += x_incr;
1963#ifndef VBOX
1964 src += 4;
1965#else
1966 src += 8; /* Every second byte of a plane is used in text mode. */
1967#endif
1968
1969 ch_attr_ptr++;
1970 }
1971#ifndef VBOX
1972 if (cx_max != -1) {
1973 dpy_update(s->ds, cx_min * cw, cy * cheight,
1974 (cx_max - cx_min + 1) * cw, cheight);
1975 }
1976#else
1977 if (cx_max != -1)
1978 s->pDrv->pfnUpdateRect(s->pDrv, cx_min * cw, cy * cheight, (cx_max - cx_min + 1) * cw, cheight);
1979#endif
1980 dest += linesize * cheight;
1981 s1 += line_offset;
1982 }
1983#ifdef VBOX
1984 return VINF_SUCCESS;
1985#endif /* VBOX */
1986}
1987
1988enum {
1989 VGA_DRAW_LINE2,
1990 VGA_DRAW_LINE2D2,
1991 VGA_DRAW_LINE4,
1992 VGA_DRAW_LINE4D2,
1993 VGA_DRAW_LINE8D2,
1994 VGA_DRAW_LINE8,
1995 VGA_DRAW_LINE15,
1996 VGA_DRAW_LINE16,
1997 VGA_DRAW_LINE24,
1998 VGA_DRAW_LINE32,
1999 VGA_DRAW_LINE_NB
2000};
2001
2002static vga_draw_line_func *vga_draw_line_table[4 * VGA_DRAW_LINE_NB] = {
2003 vga_draw_line2_8,
2004 vga_draw_line2_16,
2005 vga_draw_line2_16,
2006 vga_draw_line2_32,
2007
2008 vga_draw_line2d2_8,
2009 vga_draw_line2d2_16,
2010 vga_draw_line2d2_16,
2011 vga_draw_line2d2_32,
2012
2013 vga_draw_line4_8,
2014 vga_draw_line4_16,
2015 vga_draw_line4_16,
2016 vga_draw_line4_32,
2017
2018 vga_draw_line4d2_8,
2019 vga_draw_line4d2_16,
2020 vga_draw_line4d2_16,
2021 vga_draw_line4d2_32,
2022
2023 vga_draw_line8d2_8,
2024 vga_draw_line8d2_16,
2025 vga_draw_line8d2_16,
2026 vga_draw_line8d2_32,
2027
2028 vga_draw_line8_8,
2029 vga_draw_line8_16,
2030 vga_draw_line8_16,
2031 vga_draw_line8_32,
2032
2033 vga_draw_line15_8,
2034 vga_draw_line15_15,
2035 vga_draw_line15_16,
2036 vga_draw_line15_32,
2037
2038 vga_draw_line16_8,
2039 vga_draw_line16_15,
2040 vga_draw_line16_16,
2041 vga_draw_line16_32,
2042
2043 vga_draw_line24_8,
2044 vga_draw_line24_15,
2045 vga_draw_line24_16,
2046 vga_draw_line24_32,
2047
2048 vga_draw_line32_8,
2049 vga_draw_line32_15,
2050 vga_draw_line32_16,
2051 vga_draw_line32_32,
2052};
2053
2054static int vga_get_bpp(VGAState *s)
2055{
2056 int ret;
2057#ifdef CONFIG_BOCHS_VBE
2058 if (s->vbe_regs[VBE_DISPI_INDEX_ENABLE] & VBE_DISPI_ENABLED) {
2059 ret = s->vbe_regs[VBE_DISPI_INDEX_BPP];
2060 } else
2061#endif
2062 {
2063 ret = 0;
2064 }
2065 return ret;
2066}
2067
2068static void vga_get_resolution(VGAState *s, int *pwidth, int *pheight)
2069{
2070 int width, height;
2071#ifdef CONFIG_BOCHS_VBE
2072 if (s->vbe_regs[VBE_DISPI_INDEX_ENABLE] & VBE_DISPI_ENABLED) {
2073 width = s->vbe_regs[VBE_DISPI_INDEX_XRES];
2074 height = s->vbe_regs[VBE_DISPI_INDEX_YRES];
2075 } else
2076#endif
2077 {
2078 width = (s->cr[0x01] + 1) * 8;
2079 height = s->cr[0x12] |
2080 ((s->cr[0x07] & 0x02) << 7) |
2081 ((s->cr[0x07] & 0x40) << 3);
2082 height = (height + 1);
2083 }
2084 *pwidth = width;
2085 *pheight = height;
2086}
2087
2088#ifndef VBOX
2089void vga_invalidate_scanlines(VGAState *s, int y1, int y2)
2090{
2091 int y;
2092 if (y1 >= VGA_MAX_HEIGHT)
2093 return;
2094 if (y2 >= VGA_MAX_HEIGHT)
2095 y2 = VGA_MAX_HEIGHT;
2096 for(y = y1; y < y2; y++) {
2097 s->invalidated_y_table[y >> 5] |= 1 << (y & 0x1f);
2098 }
2099}
2100#endif /* !VBOX*/
2101
2102#ifdef VBOX
2103/**
2104 * Performs the display driver resizing when in graphics mode.
2105 *
2106 * This will recalc / update any status data depending on the driver
2107 * properties (bit depth mostly).
2108 *
2109 * @returns VINF_SUCCESS on success.
2110 * @returns VINF_VGA_RESIZE_IN_PROGRESS if the operation wasn't complete.
2111 * @param s Pointer to the vga status.
2112 * @param cx The width.
2113 * @param cy The height.
2114 */
2115static int vga_resize_graphic(VGAState *s, int cx, int cy, int v)
2116{
2117 const unsigned cBits = s->get_bpp(s);
2118
2119 /* Take into account the programmed start address (in DWORDs) of the visible screen. */
2120 int rc = s->pDrv->pfnResize(s->pDrv, cBits, s->CTX_SUFF(vram_ptr) + s->start_addr * 4, s->line_offset, cx, cy);
2121
2122 /* last stuff */
2123 s->last_bpp = cBits;
2124 s->last_scr_width = cx;
2125 s->last_scr_height = cy;
2126 s->last_width = cx;
2127 s->last_height = cy;
2128
2129 if (rc == VINF_VGA_RESIZE_IN_PROGRESS)
2130 return rc;
2131 AssertRC(rc);
2132
2133 /* update palette */
2134 switch (s->pDrv->cBits)
2135 {
2136 case 32: s->rgb_to_pixel = rgb_to_pixel32_dup; break;
2137 case 16:
2138 default: s->rgb_to_pixel = rgb_to_pixel16_dup; break;
2139 case 15: s->rgb_to_pixel = rgb_to_pixel15_dup; break;
2140 case 8: s->rgb_to_pixel = rgb_to_pixel8_dup; break;
2141 }
2142 if (s->shift_control == 0)
2143 update_palette16(s);
2144 else if (s->shift_control == 1)
2145 update_palette16(s);
2146 return VINF_SUCCESS;
2147}
2148#endif /* VBOX */
2149
2150/*
2151 * graphic modes
2152 */
2153#ifndef VBOX
2154static void vga_draw_graphic(VGAState *s, int full_update)
2155#else
2156static int vga_draw_graphic(VGAState *s, int full_update)
2157#endif /* !VBOX */
2158{
2159 int y1, y2, y, update, page_min, page_max, linesize, y_start, double_scan;
2160 int width, height, shift_control, line_offset, page0, page1, bwidth;
2161 int disp_width, multi_run;
2162 uint8_t *d;
2163 uint32_t v, addr1, addr;
2164 vga_draw_line_func *vga_draw_line;
2165 int offsets_changed;
2166
2167 offsets_changed = update_basic_params(s);
2168
2169 full_update |= offsets_changed;
2170
2171 s->get_resolution(s, &width, &height);
2172 disp_width = width;
2173
2174 shift_control = (s->gr[0x05] >> 5) & 3;
2175 double_scan = (s->cr[0x09] >> 7);
2176 multi_run = double_scan;
2177 if (shift_control != s->shift_control ||
2178 double_scan != s->double_scan) {
2179 full_update = 1;
2180 s->shift_control = shift_control;
2181 s->double_scan = double_scan;
2182 }
2183
2184 if (shift_control == 0) {
2185 full_update |= update_palette16(s);
2186 if (s->sr[0x01] & 8) {
2187 v = VGA_DRAW_LINE4D2;
2188 disp_width <<= 1;
2189 } else {
2190 v = VGA_DRAW_LINE4;
2191 }
2192 } else if (shift_control == 1) {
2193 full_update |= update_palette16(s);
2194 if (s->sr[0x01] & 8) {
2195 v = VGA_DRAW_LINE2D2;
2196 disp_width <<= 1;
2197 } else {
2198 v = VGA_DRAW_LINE2;
2199 }
2200 } else {
2201 switch(s->get_bpp(s)) {
2202 default:
2203 case 0:
2204 full_update |= update_palette256(s);
2205 v = VGA_DRAW_LINE8D2;
2206 break;
2207 case 8:
2208 full_update |= update_palette256(s);
2209 v = VGA_DRAW_LINE8;
2210 break;
2211 case 15:
2212 v = VGA_DRAW_LINE15;
2213 break;
2214 case 16:
2215 v = VGA_DRAW_LINE16;
2216 break;
2217 case 24:
2218 v = VGA_DRAW_LINE24;
2219 break;
2220 case 32:
2221 v = VGA_DRAW_LINE32;
2222 break;
2223 }
2224 }
2225#ifndef VBOX
2226 vga_draw_line = vga_draw_line_table[v * 4 + get_depth_index(s->ds->depth)];
2227
2228 if (disp_width != s->last_width ||
2229 height != s->last_height) {
2230 dpy_resize(s->ds, disp_width, height);
2231 s->last_scr_width = disp_width;
2232 s->last_scr_height = height;
2233 s->last_width = disp_width;
2234 s->last_height = height;
2235 full_update = 1;
2236 }
2237#else /* VBOX */
2238 if ( disp_width != (int)s->last_width
2239 || height != (int)s->last_height
2240 || s->get_bpp(s) != (int)s->last_bpp
2241 || offsets_changed)
2242 {
2243 int rc = vga_resize_graphic(s, disp_width, height, v);
2244 if (rc != VINF_SUCCESS) /* Return any rc, particularly VINF_VGA_RESIZE_IN_PROGRESS, to the caller. */
2245 return rc;
2246 full_update = 1;
2247 }
2248 vga_draw_line = vga_draw_line_table[v * 4 + get_depth_index(s->pDrv->cBits)];
2249
2250#endif /* VBOX */
2251 if (s->cursor_invalidate)
2252 s->cursor_invalidate(s);
2253
2254 line_offset = s->line_offset;
2255#if 0
2256 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",
2257 width, height, v, line_offset, s->cr[9], s->cr[0x17], s->line_compare, s->sr[0x01]));
2258#endif
2259 addr1 = (s->start_addr * 4);
2260#ifndef VBOX
2261 bwidth = width * 4;
2262#else /* VBOX */
2263 /* The width of VRAM scanline. */
2264 bwidth = s->line_offset;
2265 /* In some cases the variable is not yet set, probably due to incomplete
2266 * programming of the virtual hardware ports. Just return.
2267 */
2268 if (bwidth == 0) return VINF_SUCCESS;
2269#endif /* VBOX */
2270 y_start = -1;
2271 page_min = 0x7fffffff;
2272 page_max = -1;
2273#ifndef VBOX
2274 d = s->ds->data;
2275 linesize = s->ds->linesize;
2276#else /* VBOX */
2277 d = s->pDrv->pu8Data;
2278 linesize = s->pDrv->cbScanline;
2279#endif /* VBOX */
2280
2281 y1 = 0;
2282 y2 = s->cr[0x09] & 0x1F; /* starting row scan count */
2283 for(y = 0; y < height; y++) {
2284 addr = addr1;
2285 /* CGA/MDA compatibility. Note that these addresses are all
2286 * shifted left by two compared to VGA specs.
2287 */
2288 if (!(s->cr[0x17] & 1)) {
2289 addr = (addr & ~(1 << 15)) | ((y1 & 1) << 15);
2290 }
2291 if (!(s->cr[0x17] & 2)) {
2292 addr = (addr & ~(1 << 16)) | ((y1 & 2) << 15);
2293 }
2294#ifndef VBOX
2295 page0 = s->vram_offset + (addr & TARGET_PAGE_MASK);
2296 page1 = s->vram_offset + ((addr + bwidth - 1) & TARGET_PAGE_MASK);
2297 update = full_update |
2298 cpu_physical_memory_get_dirty(page0, VGA_DIRTY_FLAG) |
2299 cpu_physical_memory_get_dirty(page1, VGA_DIRTY_FLAG);
2300 if ((page1 - page0) > TARGET_PAGE_SIZE) {
2301 /* if wide line, can use another page */
2302 update |= cpu_physical_memory_get_dirty(page0 + TARGET_PAGE_SIZE,
2303 VGA_DIRTY_FLAG);
2304 }
2305#else /* VBOX */
2306 page0 = addr & TARGET_PAGE_MASK;
2307 page1 = (addr + bwidth - 1) & TARGET_PAGE_MASK;
2308 update = full_update | vga_is_dirty(s, page0) | vga_is_dirty(s, page1);
2309 if (page1 - page0 > TARGET_PAGE_SIZE) {
2310 /* if wide line, can use another page */
2311 update |= vga_is_dirty(s, page0 + TARGET_PAGE_SIZE);
2312 }
2313#endif /* VBOX */
2314 /* explicit invalidation for the hardware cursor */
2315 update |= (s->invalidated_y_table[y >> 5] >> (y & 0x1f)) & 1;
2316 if (update) {
2317 if (y_start < 0)
2318 y_start = y;
2319 if (page0 < page_min)
2320 page_min = page0;
2321 if (page1 > page_max)
2322 page_max = page1;
2323#ifndef VBOX
2324 vga_draw_line(s, d, s->vram_ptr + addr, width);
2325#else /* VBOX */
2326 if (s->fRenderVRAM)
2327 vga_draw_line(s, d, s->CTX_SUFF(vram_ptr) + addr, width);
2328#endif /* VBOX */
2329 if (s->cursor_draw_line)
2330 s->cursor_draw_line(s, d, y);
2331 } else {
2332 if (y_start >= 0) {
2333 /* flush to display */
2334#ifndef VBOX
2335 dpy_update(s->ds, 0, y_start,
2336 disp_width, y - y_start);
2337#else /* VBOX */
2338 s->pDrv->pfnUpdateRect(s->pDrv, 0, y_start, disp_width, y - y_start);
2339#endif /* VBOX */
2340 y_start = -1;
2341 }
2342 }
2343 if (!multi_run) {
2344 y1++;
2345 multi_run = double_scan;
2346
2347 if (y2 == 0) {
2348 y2 = s->cr[0x09] & 0x1F;
2349 addr1 += line_offset;
2350 } else {
2351 --y2;
2352 }
2353 } else {
2354 multi_run--;
2355 }
2356 /* line compare acts on the displayed lines */
2357 if ((uint32_t)y == s->line_compare)
2358 addr1 = 0;
2359 d += linesize;
2360 }
2361 if (y_start >= 0) {
2362 /* flush to display */
2363#ifndef VBOX
2364 dpy_update(s->ds, 0, y_start,
2365 disp_width, y - y_start);
2366#else /* VBOX */
2367 s->pDrv->pfnUpdateRect(s->pDrv, 0, y_start, disp_width, y - y_start);
2368#endif /* VBOX */
2369 }
2370 /* reset modified pages */
2371 if (page_max != -1) {
2372#ifndef VBOX
2373 cpu_physical_memory_reset_dirty(page_min, page_max + TARGET_PAGE_SIZE,
2374 VGA_DIRTY_FLAG);
2375#else /* VBOX */
2376 vga_reset_dirty(s, page_min, page_max + TARGET_PAGE_SIZE);
2377#endif /* VBOX */
2378 }
2379 memset(s->invalidated_y_table, 0, ((height + 31) >> 5) * 4);
2380#ifdef VBOX
2381 return VINF_SUCCESS;
2382#endif /* VBOX */
2383}
2384
2385static void vga_draw_blank(VGAState *s, int full_update)
2386{
2387#ifndef VBOX
2388 int i, w, val;
2389 uint8_t *d;
2390
2391 if (!full_update)
2392 return;
2393 if (s->last_scr_width <= 0 || s->last_scr_height <= 0)
2394 return;
2395 if (s->ds->depth == 8)
2396 val = s->rgb_to_pixel(0, 0, 0);
2397 else
2398 val = 0;
2399 w = s->last_scr_width * ((s->ds->depth + 7) >> 3);
2400 d = s->ds->data;
2401 for(i = 0; i < s->last_scr_height; i++) {
2402 memset(d, val, w);
2403 d += s->ds->linesize;
2404 }
2405 dpy_update(s->ds, 0, 0,
2406 s->last_scr_width, s->last_scr_height);
2407#else /* VBOX */
2408
2409 int i, w, val;
2410 uint8_t *d;
2411 uint32_t cbScanline = s->pDrv->cbScanline;
2412
2413 if (s->pDrv->pu8Data == s->vram_ptrR3) /* Do not clear the VRAM itself. */
2414 return;
2415 if (!full_update)
2416 return;
2417 if (s->last_scr_width <= 0 || s->last_scr_height <= 0)
2418 return;
2419 if (s->pDrv->cBits == 8)
2420 val = s->rgb_to_pixel(0, 0, 0);
2421 else
2422 val = 0;
2423 w = s->last_scr_width * ((s->pDrv->cBits + 7) >> 3);
2424 d = s->pDrv->pu8Data;
2425 for(i = 0; i < (int)s->last_scr_height; i++) {
2426 memset(d, val, w);
2427 d += cbScanline;
2428 }
2429 s->pDrv->pfnUpdateRect(s->pDrv, 0, 0, s->last_scr_width, s->last_scr_height);
2430#endif /* VBOX */
2431}
2432
2433#ifdef VBOX
2434static DECLCALLBACK(void) voidUpdateRect(PPDMIDISPLAYCONNECTOR pInterface, uint32_t x, uint32_t y, uint32_t cx, uint32_t cy)
2435{
2436}
2437#endif /* VBOX */
2438
2439
2440#define GMODE_TEXT 0
2441#define GMODE_GRAPH 1
2442#define GMODE_BLANK 2
2443
2444#ifndef VBOX
2445void vga_update_display(void)
2446{
2447 VGAState *s = vga_state;
2448#else /* VBOX */
2449static int vga_update_display(PVGASTATE s, bool fUpdateAll)
2450{
2451 int rc = VINF_SUCCESS;
2452#endif /* VBOX */
2453 int full_update, graphic_mode;
2454
2455#ifndef VBOX
2456 if (s->ds->depth == 0) {
2457#else /* VBOX */
2458 if (s->pDrv->cBits == 0) {
2459#endif /* VBOX */
2460 /* nothing to do */
2461 } else {
2462#ifndef VBOX
2463 switch(s->ds->depth) {
2464#else /* VBOX */
2465 switch(s->pDrv->cBits) {
2466#endif /* VBOX */
2467 case 8:
2468 s->rgb_to_pixel = rgb_to_pixel8_dup;
2469 break;
2470 case 15:
2471 s->rgb_to_pixel = rgb_to_pixel15_dup;
2472 break;
2473 default:
2474 case 16:
2475 s->rgb_to_pixel = rgb_to_pixel16_dup;
2476 break;
2477 case 32:
2478 s->rgb_to_pixel = rgb_to_pixel32_dup;
2479 break;
2480 }
2481
2482#ifdef VBOX
2483 if (fUpdateAll) {
2484 /* A full update is requested. Special processing for a "blank" mode is required. */
2485 typedef DECLCALLBACK(void) FNUPDATERECT(PPDMIDISPLAYCONNECTOR pInterface, uint32_t x, uint32_t y, uint32_t cx, uint32_t cy);
2486 typedef FNUPDATERECT *PFNUPDATERECT;
2487
2488 PFNUPDATERECT pfnUpdateRect = NULL;
2489
2490 /* Detect the "screen blank" conditions. */
2491 int fBlank = 0;
2492 if (!(s->ar_index & 0x20) || (s->sr[0x01] & 0x20)) {
2493 fBlank = 1;
2494 }
2495
2496 if (fBlank) {
2497 /* Provide a void pfnUpdateRect callback. */
2498 if (s->pDrv) {
2499 pfnUpdateRect = s->pDrv->pfnUpdateRect;
2500 s->pDrv->pfnUpdateRect = voidUpdateRect;
2501 }
2502 }
2503
2504 /* Do a complete redraw, which will pick up a new screen resolution. */
2505 if (s->gr[6] & 1) {
2506 s->graphic_mode = GMODE_GRAPH;
2507 rc = vga_draw_graphic(s, 1);
2508 } else {
2509 s->graphic_mode = GMODE_TEXT;
2510 rc = vga_draw_text(s, 1);
2511 }
2512
2513 if (fBlank) {
2514 /* Set the current mode and restore the callback. */
2515 s->graphic_mode = GMODE_BLANK;
2516 if (s->pDrv) {
2517 s->pDrv->pfnUpdateRect = pfnUpdateRect;
2518 }
2519 }
2520 return rc;
2521 }
2522#endif /* VBOX */
2523
2524 full_update = 0;
2525 if (!(s->ar_index & 0x20) || (s->sr[0x01] & 0x20)) {
2526 graphic_mode = GMODE_BLANK;
2527 } else {
2528 graphic_mode = s->gr[6] & 1;
2529 }
2530 if (graphic_mode != s->graphic_mode) {
2531 s->graphic_mode = graphic_mode;
2532 full_update = 1;
2533 }
2534 switch(graphic_mode) {
2535 case GMODE_TEXT:
2536#ifdef VBOX
2537 rc =
2538#endif /* VBOX */
2539 vga_draw_text(s, full_update);
2540 break;
2541 case GMODE_GRAPH:
2542#ifdef VBOX
2543 rc =
2544#endif /* VBOX */
2545 vga_draw_graphic(s, full_update);
2546 break;
2547 case GMODE_BLANK:
2548 default:
2549 vga_draw_blank(s, full_update);
2550 break;
2551 }
2552 }
2553#ifdef VBOX
2554 return rc;
2555#endif /* VBOX */
2556}
2557
2558/* force a full display refresh */
2559#ifndef VBOX
2560void vga_invalidate_display(void)
2561{
2562 VGAState *s = vga_state;
2563
2564 s->last_width = -1;
2565 s->last_height = -1;
2566}
2567#endif /* !VBOX */
2568
2569#ifndef VBOX /* see vgaR3Reset() */
2570static void vga_reset(VGAState *s)
2571{
2572 memset(s, 0, sizeof(VGAState));
2573 s->graphic_mode = -1; /* force full update */
2574}
2575#endif /* !VBOX */
2576
2577#ifndef VBOX
2578static CPUReadMemoryFunc *vga_mem_read[3] = {
2579 vga_mem_readb,
2580 vga_mem_readw,
2581 vga_mem_readl,
2582};
2583
2584static CPUWriteMemoryFunc *vga_mem_write[3] = {
2585 vga_mem_writeb,
2586 vga_mem_writew,
2587 vga_mem_writel,
2588};
2589#endif /* !VBOX */
2590
2591static void vga_save(QEMUFile *f, void *opaque)
2592{
2593 VGAState *s = (VGAState*)opaque;
2594 int i;
2595
2596 qemu_put_be32s(f, &s->latch);
2597 qemu_put_8s(f, &s->sr_index);
2598 qemu_put_buffer(f, s->sr, 8);
2599 qemu_put_8s(f, &s->gr_index);
2600 qemu_put_buffer(f, s->gr, 16);
2601 qemu_put_8s(f, &s->ar_index);
2602 qemu_put_buffer(f, s->ar, 21);
2603 qemu_put_be32s(f, &s->ar_flip_flop);
2604 qemu_put_8s(f, &s->cr_index);
2605 qemu_put_buffer(f, s->cr, 256);
2606 qemu_put_8s(f, &s->msr);
2607 qemu_put_8s(f, &s->fcr);
2608 qemu_put_8s(f, &s->st00);
2609 qemu_put_8s(f, &s->st01);
2610
2611 qemu_put_8s(f, &s->dac_state);
2612 qemu_put_8s(f, &s->dac_sub_index);
2613 qemu_put_8s(f, &s->dac_read_index);
2614 qemu_put_8s(f, &s->dac_write_index);
2615 qemu_put_buffer(f, s->dac_cache, 3);
2616 qemu_put_buffer(f, s->palette, 768);
2617
2618 qemu_put_be32s(f, &s->bank_offset);
2619#ifdef CONFIG_BOCHS_VBE
2620 qemu_put_byte(f, 1);
2621 qemu_put_be16s(f, &s->vbe_index);
2622 for(i = 0; i < VBE_DISPI_INDEX_NB; i++)
2623 qemu_put_be16s(f, &s->vbe_regs[i]);
2624 qemu_put_be32s(f, &s->vbe_start_addr);
2625 qemu_put_be32s(f, &s->vbe_line_offset);
2626#else
2627 qemu_put_byte(f, 0);
2628#endif
2629}
2630
2631static int vga_load(QEMUFile *f, void *opaque, int version_id)
2632{
2633 VGAState *s = (VGAState*)opaque;
2634 int is_vbe, i;
2635 uint32_t u32Dummy;
2636
2637 if (version_id > VGA_SAVEDSTATE_VERSION)
2638#ifndef VBOX
2639 return -EINVAL;
2640#else /* VBOX */
2641 {
2642 Log(("vga_load: version_id=%d - UNKNOWN\n", version_id));
2643 return VERR_SSM_UNSUPPORTED_DATA_UNIT_VERSION;
2644 }
2645#endif /* VBOX */
2646
2647 qemu_get_be32s(f, &s->latch);
2648 qemu_get_8s(f, &s->sr_index);
2649 qemu_get_buffer(f, s->sr, 8);
2650 qemu_get_8s(f, &s->gr_index);
2651 qemu_get_buffer(f, s->gr, 16);
2652 qemu_get_8s(f, &s->ar_index);
2653 qemu_get_buffer(f, s->ar, 21);
2654 qemu_get_be32s(f, (uint32_t *)&s->ar_flip_flop);
2655 qemu_get_8s(f, &s->cr_index);
2656 qemu_get_buffer(f, s->cr, 256);
2657 qemu_get_8s(f, &s->msr);
2658 qemu_get_8s(f, &s->fcr);
2659 qemu_get_8s(f, &s->st00);
2660 qemu_get_8s(f, &s->st01);
2661
2662 qemu_get_8s(f, &s->dac_state);
2663 qemu_get_8s(f, &s->dac_sub_index);
2664 qemu_get_8s(f, &s->dac_read_index);
2665 qemu_get_8s(f, &s->dac_write_index);
2666 qemu_get_buffer(f, s->dac_cache, 3);
2667 qemu_get_buffer(f, s->palette, 768);
2668
2669 qemu_get_be32s(f, (uint32_t *)&s->bank_offset);
2670 is_vbe = qemu_get_byte(f);
2671#ifdef CONFIG_BOCHS_VBE
2672 if (!is_vbe)
2673#ifndef VBOX
2674 return -EINVAL;
2675#else /* VBOX */
2676 {
2677 Log(("vga_load: !is_vbe !!\n"));
2678 return VERR_SSM_DATA_UNIT_FORMAT_CHANGED;
2679 }
2680#endif /* VBOX */
2681 qemu_get_be16s(f, &s->vbe_index);
2682 for(i = 0; i < VBE_DISPI_INDEX_NB; i++)
2683 qemu_get_be16s(f, &s->vbe_regs[i]);
2684 qemu_get_be32s(f, &s->vbe_start_addr);
2685 qemu_get_be32s(f, &s->vbe_line_offset);
2686 if (version_id < 2)
2687 qemu_get_be32s(f, &u32Dummy);
2688 s->vbe_bank_max = s->vram_size >> 16;
2689#else
2690 if (is_vbe)
2691#ifndef VBOX
2692 return -EINVAL;
2693#else /* VBOX */
2694 {
2695 Log(("vga_load: is_vbe !!\n"));
2696 return VERR_SSM_DATA_UNIT_FORMAT_CHANGED;
2697 }
2698#endif /* VBOX */
2699#endif
2700
2701 /* force refresh */
2702 s->graphic_mode = -1;
2703 return 0;
2704}
2705
2706#ifndef VBOX /* see vgaR3IORegionMap */
2707static void vga_map(PCIDevice *pci_dev, int region_num,
2708 uint32_t addr, uint32_t size, int type)
2709{
2710 VGAState *s = vga_state;
2711
2712 cpu_register_physical_memory(addr, s->vram_size, s->vram_offset);
2713}
2714#endif
2715
2716#ifndef VBOX /* see vgaR3Construct */
2717void vga_common_init(VGAState *s, DisplayState *ds, uint8_t *vga_ram_base,
2718 unsigned long vga_ram_offset, int vga_ram_size)
2719#else
2720static void vga_init_expand(void)
2721#endif
2722{
2723 int i, j, v, b;
2724
2725 for(i = 0;i < 256; i++) {
2726 v = 0;
2727 for(j = 0; j < 8; j++) {
2728 v |= ((i >> j) & 1) << (j * 4);
2729 }
2730 expand4[i] = v;
2731
2732 v = 0;
2733 for(j = 0; j < 4; j++) {
2734 v |= ((i >> (2 * j)) & 3) << (j * 4);
2735 }
2736 expand2[i] = v;
2737 }
2738 for(i = 0; i < 16; i++) {
2739 v = 0;
2740 for(j = 0; j < 4; j++) {
2741 b = ((i >> j) & 1);
2742 v |= b << (2 * j);
2743 v |= b << (2 * j + 1);
2744 }
2745 expand4to8[i] = v;
2746 }
2747#ifdef VBOX
2748}
2749#else /* !VBOX */
2750 vga_reset(s);
2751
2752 s->vram_ptr = vga_ram_base;
2753 s->vram_offset = vga_ram_offset;
2754 s->vram_size = vga_ram_size;
2755 s->ds = ds;
2756 s->get_bpp = vga_get_bpp;
2757 s->get_offsets = vga_get_offsets;
2758 s->get_resolution = vga_get_resolution;
2759 /* XXX: currently needed for display */
2760 vga_state = s;
2761}
2762
2763
2764int vga_initialize(PCIBus *bus, DisplayState *ds, uint8_t *vga_ram_base,
2765 unsigned long vga_ram_offset, int vga_ram_size)
2766{
2767 VGAState *s;
2768
2769 s = qemu_mallocz(sizeof(VGAState));
2770 if (!s)
2771 return -1;
2772
2773 vga_common_init(s, ds, vga_ram_base, vga_ram_offset, vga_ram_size);
2774
2775 register_savevm("vga", 0, 1, vga_save, vga_load, s);
2776
2777 register_ioport_write(0x3c0, 16, 1, vga_ioport_write, s);
2778
2779 register_ioport_write(0x3b4, 2, 1, vga_ioport_write, s);
2780 register_ioport_write(0x3d4, 2, 1, vga_ioport_write, s);
2781 register_ioport_write(0x3ba, 1, 1, vga_ioport_write, s);
2782 register_ioport_write(0x3da, 1, 1, vga_ioport_write, s);
2783
2784 register_ioport_read(0x3c0, 16, 1, vga_ioport_read, s);
2785
2786 register_ioport_read(0x3b4, 2, 1, vga_ioport_read, s);
2787 register_ioport_read(0x3d4, 2, 1, vga_ioport_read, s);
2788 register_ioport_read(0x3ba, 1, 1, vga_ioport_read, s);
2789 register_ioport_read(0x3da, 1, 1, vga_ioport_read, s);
2790 s->bank_offset = 0;
2791
2792#ifdef CONFIG_BOCHS_VBE
2793 s->vbe_regs[VBE_DISPI_INDEX_ID] = VBE_DISPI_ID0;
2794 s->vbe_bank_max = s->vram_size >> 16;
2795#if defined (TARGET_I386)
2796 register_ioport_read(0x1ce, 1, 2, vbe_ioport_read_index, s);
2797 register_ioport_read(0x1cf, 1, 2, vbe_ioport_read_data, s);
2798
2799 register_ioport_write(0x1ce, 1, 2, vbe_ioport_write_index, s);
2800 register_ioport_write(0x1cf, 1, 2, vbe_ioport_write_data, s);
2801
2802 /* old Bochs IO ports */
2803 register_ioport_read(0xff80, 1, 2, vbe_ioport_read_index, s);
2804 register_ioport_read(0xff81, 1, 2, vbe_ioport_read_data, s);
2805
2806 register_ioport_write(0xff80, 1, 2, vbe_ioport_write_index, s);
2807 register_ioport_write(0xff81, 1, 2, vbe_ioport_write_data, s);
2808#else
2809 register_ioport_read(0x1ce, 1, 2, vbe_ioport_read_index, s);
2810 register_ioport_read(0x1d0, 1, 2, vbe_ioport_read_data, s);
2811
2812 register_ioport_write(0x1ce, 1, 2, vbe_ioport_write_index, s);
2813 register_ioport_write(0x1d0, 1, 2, vbe_ioport_write_data, s);
2814#endif
2815#endif /* CONFIG_BOCHS_VBE */
2816
2817 vga_io_memory = cpu_register_io_memory(0, vga_mem_read, vga_mem_write, s);
2818 cpu_register_physical_memory(isa_mem_base + 0x000a0000, 0x20000,
2819 vga_io_memory);
2820
2821 if (bus) {
2822 PCIDevice *d;
2823 uint8_t *pci_conf;
2824
2825 d = pci_register_device(bus, "VGA",
2826 sizeof(PCIDevice),
2827 -1, NULL, NULL);
2828 pci_conf = d->config;
2829 pci_conf[0x00] = 0x34; // dummy VGA (same as Bochs ID)
2830 pci_conf[0x01] = 0x12;
2831 pci_conf[0x02] = 0x11;
2832 pci_conf[0x03] = 0x11;
2833 pci_conf[0x0a] = 0x00; // VGA controller
2834 pci_conf[0x0b] = 0x03;
2835 pci_conf[0x0e] = 0x00; // header_type
2836
2837 /* XXX: vga_ram_size must be a power of two */
2838 pci_register_io_region(d, 0, vga_ram_size,
2839 PCI_ADDRESS_SPACE_MEM_PREFETCH, vga_map);
2840 } else {
2841#ifdef CONFIG_BOCHS_VBE
2842 /* XXX: use optimized standard vga accesses */
2843 cpu_register_physical_memory(VBE_DISPI_LFB_PHYSICAL_ADDRESS,
2844 vga_ram_size, vga_ram_offset);
2845#endif
2846 }
2847 return 0;
2848}
2849#endif /* !VBOX */
2850
2851
2852#ifndef VBOX
2853/********************************************************/
2854/* vga screen dump */
2855
2856static int vga_save_w, vga_save_h;
2857
2858static void vga_save_dpy_update(DisplayState *s,
2859 int x, int y, int w, int h)
2860{
2861}
2862
2863static void vga_save_dpy_resize(DisplayState *s, int w, int h)
2864{
2865 s->linesize = w * 4;
2866#ifndef VBOX
2867 s->data = qemu_malloc(h * s->linesize);
2868#else /* VBOX */
2869 if (!s->data)
2870 {
2871 PPDMDEVINS pDevIns = VGASTATE2DEVINS((PVGASTATE)s->pvVgaState);
2872 s->data = PDMDevHlpMMHeapAlloc(pDevIns, h * s->linesize);
2873 }
2874 else // (32-bpp buffer is allocated by the caller)
2875 s->linesize = ((w * 32 + 31) / 32) * 4;
2876#endif /* VBOX */
2877 vga_save_w = w;
2878 vga_save_h = h;
2879}
2880
2881static void vga_save_dpy_refresh(DisplayState *s)
2882{
2883}
2884
2885static int ppm_save(const char *filename, uint8_t *data,
2886 int w, int h, int linesize)
2887{
2888 FILE *f;
2889 uint8_t *d, *d1;
2890 unsigned int v;
2891 int y, x;
2892
2893 f = fopen(filename, "wb");
2894 if (!f)
2895 return -1;
2896 fprintf(f, "P6\n%d %d\n%d\n",
2897 w, h, 255);
2898 d1 = data;
2899 for(y = 0; y < h; y++) {
2900 d = d1;
2901 for(x = 0; x < w; x++) {
2902 v = *(uint32_t *)d;
2903 fputc((v >> 16) & 0xff, f);
2904 fputc((v >> 8) & 0xff, f);
2905 fputc((v) & 0xff, f);
2906 d += 4;
2907 }
2908 d1 += linesize;
2909 }
2910 fclose(f);
2911 return 0;
2912}
2913
2914/* save the vga display in a PPM image even if no display is
2915 available */
2916void vga_screen_dump(const char *filename)
2917{
2918 VGAState *s = vga_state;
2919 DisplayState *saved_ds, ds1, *ds = &ds1;
2920
2921 /* XXX: this is a little hackish */
2922 vga_invalidate_display();
2923 saved_ds = s->ds;
2924
2925 memset(ds, 0, sizeof(DisplayState));
2926 ds->dpy_update = vga_save_dpy_update;
2927 ds->dpy_resize = vga_save_dpy_resize;
2928 ds->dpy_refresh = vga_save_dpy_refresh;
2929 ds->depth = 32;
2930
2931 s->ds = ds;
2932 s->graphic_mode = -1;
2933 vga_update_display();
2934
2935 if (ds->data) {
2936 ppm_save(filename, ds->data, vga_save_w, vga_save_h,
2937 s->ds->linesize);
2938 qemu_free(ds->data);
2939 }
2940 s->ds = saved_ds;
2941}
2942#endif /* !VBOX */
2943
2944
2945#if 0 //def VBOX
2946/* copy the vga display contents to the given buffer. the size of the buffer
2947 must be sufficient to store the screen copy (see below). the width and height
2948 parameters determine the required dimensions of the copy. If they differ
2949 from the actual screen dimensions, then the returned copy is shrinked or
2950 stretched accordingly. The copy is always a 32-bit image, so the size of
2951 the buffer supplied must be at least (((width * 32 + 31) / 32) * 4) * height,
2952 i.e. dword-aligned. returns zero if the operation was successfull and -1
2953 otherwise. */
2954
2955static int vga_copy_screen_to(PVGASTATE s, uint8_t *buf, int width, int height)
2956{
2957 DisplayState *saved_ds, ds1, *ds = &ds1;
2958 if (!buf || width <= 0 || height <= 0)
2959 return -1;
2960
2961 /* XXX: this is a little hackish */
2962 vga_invalidate_display(s);
2963 saved_ds = s->ds;
2964
2965 memset(ds, 0, sizeof(DisplayState));
2966 ds->dpy_update = vga_save_dpy_update;
2967 ds->dpy_resize = vga_save_dpy_resize;
2968 ds->dpy_refresh = vga_save_dpy_refresh;
2969 ds->depth = 32;
2970 ds->data = buf;
2971 ds->pvVgaState = s;
2972
2973 s->ds = ds;
2974 s->graphic_mode = -1;
2975 vga_update_display(s);
2976
2977//@@TODO (dmik): implement stretching/shrinking!
2978
2979 s->ds = saved_ds;
2980 return 0;
2981}
2982
2983/* copy the given buffer to the vga display. width and height define the
2984 dimensions of the image in the buffer. x and y define the point on the
2985 vga display to copy the image to. the buffer is assumed to contain a 32-bit
2986 image, so the size of one scanline must be ((width * 32 + 31) / 32) * 4),
2987 i.e. dword-aligned. returns zero if the operation was successfull and -1
2988 otherwise. */
2989static int vga_copy_screen_from(PVGASTATE s, uint8_t *buf, int x, int y, int width, int height)
2990{
2991 int bpl = ((width * 32 + 31) / 32) * 4;
2992 int linesize = s->ds->linesize;
2993 uint8_t *dst;
2994 uint8_t *src;
2995 int bpp;
2996 vga_draw_line_func *vga_draw_line;
2997
2998 if (!buf || x < 0 || y < 0 || width <= 0 || height <= 0
2999 || x + width > s->ds->width || y + height > s->ds->height)
3000 return -1;
3001
3002 vga_draw_line = vga_draw_line_table[VGA_DRAW_LINE32 * 4 + get_depth_index(s->ds->depth)];
3003 switch (s->ds->depth) {
3004 case 8: bpp = 1; break;
3005 case 15:
3006 case 16: bpp = 2; break;
3007 case 32: bpp = 4; break;
3008 default: return -1;
3009 }
3010
3011 dst = s->ds->data + y * linesize + x * bpp;
3012 src = buf;
3013 for (y = 0; y < height; y ++)
3014 {
3015 vga_draw_line(s, dst, src, width);
3016 dst += linesize;
3017 src += bpl;
3018 }
3019
3020 return 0;
3021}
3022#endif
3023
3024#endif /* !VBOX || !IN_RC || !IN_RING0 */
3025
3026
3027
3028#ifdef VBOX /* VirtualBox code start */
3029
3030
3031/* -=-=-=-=-=- all contexts -=-=-=-=-=- */
3032
3033/**
3034 * Port I/O Handler for VGA OUT operations.
3035 *
3036 * @returns VBox status code.
3037 *
3038 * @param pDevIns The device instance.
3039 * @param pvUser User argument - ignored.
3040 * @param Port Port number used for the IN operation.
3041 * @param u32 The value to output.
3042 * @param cb The value size in bytes.
3043 */
3044PDMBOTHCBDECL(int) vgaIOPortWrite(PPDMDEVINS pDevIns, void *pvUser, RTIOPORT Port, uint32_t u32, unsigned cb)
3045{
3046 VGAState *s = PDMINS_2_DATA(pDevIns, PVGASTATE);
3047
3048 int rc = PDMCritSectEnter(&s->lock, VINF_IOM_HC_IOPORT_WRITE);
3049 if (rc != VINF_SUCCESS)
3050 return rc;
3051
3052 NOREF(pvUser);
3053 if (cb == 1)
3054 vga_ioport_write(s, Port, u32);
3055 else if (cb == 2)
3056 {
3057 vga_ioport_write(s, Port, u32 & 0xff);
3058 vga_ioport_write(s, Port + 1, u32 >> 8);
3059 }
3060 PDMCritSectLeave(&s->lock);
3061 return VINF_SUCCESS;
3062}
3063
3064
3065/**
3066 * Port I/O Handler for VGA IN operations.
3067 *
3068 * @returns VBox status code.
3069 *
3070 * @param pDevIns The device instance.
3071 * @param pvUser User argument - ignored.
3072 * @param Port Port number used for the IN operation.
3073 * @param pu32 Where to store the result.
3074 * @param cb Number of bytes read.
3075 */
3076PDMBOTHCBDECL(int) vgaIOPortRead(PPDMDEVINS pDevIns, void *pvUser, RTIOPORT Port, uint32_t *pu32, unsigned cb)
3077{
3078 VGAState *s = PDMINS_2_DATA(pDevIns, PVGASTATE);
3079 NOREF(pvUser);
3080
3081 int rc = PDMCritSectEnter(&s->lock, VINF_IOM_HC_IOPORT_READ);
3082 if (rc != VINF_SUCCESS)
3083 return rc;
3084
3085 rc = VERR_IOM_IOPORT_UNUSED;
3086 if (cb == 1)
3087 {
3088 *pu32 = vga_ioport_read(s, Port);
3089 rc = VINF_SUCCESS;
3090 }
3091 else if (cb == 2)
3092 {
3093 *pu32 = vga_ioport_read(s, Port)
3094 | (vga_ioport_read(s, Port + 1) << 8);
3095 rc = VINF_SUCCESS;
3096 }
3097 PDMCritSectLeave(&s->lock);
3098 return rc;
3099}
3100
3101
3102/**
3103 * Port I/O Handler for VBE OUT operations.
3104 *
3105 * @returns VBox status code.
3106 *
3107 * @param pDevIns The device instance.
3108 * @param pvUser User argument - ignored.
3109 * @param Port Port number used for the IN operation.
3110 * @param u32 The value to output.
3111 * @param cb The value size in bytes.
3112 */
3113PDMBOTHCBDECL(int) vgaIOPortWriteVBEData(PPDMDEVINS pDevIns, void *pvUser, RTIOPORT Port, uint32_t u32, unsigned cb)
3114{
3115 VGAState *s = PDMINS_2_DATA(pDevIns, PVGASTATE);
3116
3117 int rc = PDMCritSectEnter(&s->lock, VINF_IOM_HC_IOPORT_WRITE);
3118 if (rc != VINF_SUCCESS)
3119 return rc;
3120
3121 NOREF(pvUser);
3122
3123#ifdef VBOX_WITH_HGSMI
3124#ifdef IN_RING3
3125 if (s->vbe_index == VBE_DISPI_INDEX_VBVA_GUEST)
3126 {
3127 HGSMIGuestWrite (s->pHGSMI, u32);
3128 PDMCritSectLeave(&s->lock);
3129 return VINF_SUCCESS;
3130 }
3131 if (s->vbe_index == VBE_DISPI_INDEX_VBVA_HOST)
3132 {
3133#if defined(VBOX_WITH_VIDEOHWACCEL)
3134 if(u32 == HGSMIOFFSET_VOID)
3135 {
3136 PDMDevHlpPCISetIrq(pDevIns, 0, PDM_IRQ_LEVEL_LOW);
3137 HGSMIClearHostGuestFlags(s->pHGSMI, HGSMIHOSTFLAGS_IRQ);
3138 }
3139 else
3140#endif
3141 {
3142 HGSMIHostWrite (s->pHGSMI, u32);
3143 }
3144 PDMCritSectLeave(&s->lock);
3145 return VINF_SUCCESS;
3146 }
3147#else
3148 if ( s->vbe_index == VBE_DISPI_INDEX_VBVA_HOST
3149 || s->vbe_index == VBE_DISPI_INDEX_VBVA_GUEST)
3150 {
3151 Log(("vgaIOPortWriteVBEData: %s - Switching to host...\n",
3152 s->vbe_index == VBE_DISPI_INDEX_VBVA_HOST? "VBE_DISPI_INDEX_VBVA_HOST": "VBE_DISPI_INDEX_VBVA_GUEST"));
3153 PDMCritSectLeave(&s->lock);
3154 return VINF_IOM_HC_IOPORT_WRITE;
3155 }
3156#endif /* !IN_RING3 */
3157#endif /* VBOX_WITH_HGSMI */
3158
3159#ifndef IN_RING3
3160 /*
3161 * This has to be done on the host in order to execute the connector callbacks.
3162 */
3163 if ( s->vbe_index == VBE_DISPI_INDEX_ENABLE
3164 || s->vbe_index == VBE_DISPI_INDEX_VBOX_VIDEO)
3165 {
3166 Log(("vgaIOPortWriteVBEData: VBE_DISPI_INDEX_ENABLE - Switching to host...\n"));
3167 PDMCritSectLeave(&s->lock);
3168 return VINF_IOM_HC_IOPORT_WRITE;
3169 }
3170#endif
3171#ifdef VBE_BYTEWISE_IO
3172 if (cb == 1)
3173 {
3174 if (!s->fWriteVBEData)
3175 {
3176 if ( (s->vbe_index == VBE_DISPI_INDEX_ENABLE)
3177 && (u32 & VBE_DISPI_ENABLED))
3178 {
3179 s->fWriteVBEData = false;
3180 rc = vbe_ioport_write_data(s, Port, u32 & 0xFF);
3181 PDMCritSectLeave(&s->lock);
3182 return rc;
3183 }
3184 else
3185 {
3186 s->cbWriteVBEData = u32 & 0xFF;
3187 s->fWriteVBEData = true;
3188 PDMCritSectLeave(&s->lock);
3189 return VINF_SUCCESS;
3190 }
3191 }
3192 else
3193 {
3194 u32 = (s->cbWriteVBEData << 8) | (u32 & 0xFF);
3195 s->fWriteVBEData = false;
3196 cb = 2;
3197 }
3198 }
3199#endif
3200 if (cb == 2 || cb == 4)
3201 {
3202//#ifdef IN_RC
3203// /*
3204// * The VBE_DISPI_INDEX_ENABLE memsets the entire frame buffer.
3205// * Since we're not mapping the entire framebuffer any longer that
3206// * has to be done on the host.
3207// */
3208// if ( (s->vbe_index == VBE_DISPI_INDEX_ENABLE)
3209// && (u32 & VBE_DISPI_ENABLED))
3210// {
3211// Log(("vgaIOPortWriteVBEData: VBE_DISPI_INDEX_ENABLE & VBE_DISPI_ENABLED - Switching to host...\n"));
3212// return VINF_IOM_HC_IOPORT_WRITE;
3213// }
3214//#endif
3215 rc = vbe_ioport_write_data(s, Port, u32);
3216 PDMCritSectLeave(&s->lock);
3217 return rc;
3218 }
3219 else
3220 AssertMsgFailed(("vgaIOPortWriteVBEData: Port=%#x cb=%d u32=%#x\n", Port, cb, u32));
3221
3222 PDMCritSectLeave(&s->lock);
3223 return VINF_SUCCESS;
3224}
3225
3226
3227/**
3228 * Port I/O Handler for VBE OUT operations.
3229 *
3230 * @returns VBox status code.
3231 *
3232 * @param pDevIns The device instance.
3233 * @param pvUser User argument - ignored.
3234 * @param Port Port number used for the IN operation.
3235 * @param u32 The value to output.
3236 * @param cb The value size in bytes.
3237 */
3238PDMBOTHCBDECL(int) vgaIOPortWriteVBEIndex(PPDMDEVINS pDevIns, void *pvUser, RTIOPORT Port, uint32_t u32, unsigned cb)
3239{
3240 NOREF(pvUser);
3241 VGAState *s = PDMINS_2_DATA(pDevIns, PVGASTATE);
3242
3243 int rc = PDMCritSectEnter(&s->lock, VINF_IOM_HC_IOPORT_WRITE);
3244 if (rc != VINF_SUCCESS)
3245 return rc;
3246
3247#ifdef VBE_BYTEWISE_IO
3248 if (cb == 1)
3249 {
3250 if (!s->fWriteVBEIndex)
3251 {
3252 s->cbWriteVBEIndex = u32 & 0x00FF;
3253 s->fWriteVBEIndex = true;
3254 PDMCritSectLeave(&s->lock);
3255 return VINF_SUCCESS;
3256 }
3257 else
3258 {
3259 s->fWriteVBEIndex = false;
3260 vbe_ioport_write_index(s, Port, (s->cbWriteVBEIndex << 8) | (u32 & 0x00FF));
3261 PDMCritSectLeave(&s->lock);
3262 return VINF_SUCCESS;
3263 }
3264 }
3265 else
3266#endif
3267 if (cb == 2)
3268 vbe_ioport_write_index(s, Port, u32);
3269 else
3270 AssertMsgFailed(("vgaIOPortWriteVBEIndex: Port=%#x cb=%d u32=%#x\n", Port, cb, u32));
3271 PDMCritSectLeave(&s->lock);
3272 return VINF_SUCCESS;
3273}
3274
3275
3276/**
3277 * Port I/O Handler for VBE IN operations.
3278 *
3279 * @returns VBox status code.
3280 *
3281 * @param pDevIns The device instance.
3282 * @param pvUser User argument - ignored.
3283 * @param Port Port number used for the IN operation.
3284 * @param pu32 Where to store the result.
3285 * @param cb Number of bytes to read.
3286 */
3287PDMBOTHCBDECL(int) vgaIOPortReadVBEData(PPDMDEVINS pDevIns, void *pvUser, RTIOPORT Port, uint32_t *pu32, unsigned cb)
3288{
3289 NOREF(pvUser);
3290 VGAState *s = PDMINS_2_DATA(pDevIns, PVGASTATE);
3291
3292 int rc = PDMCritSectEnter(&s->lock, VINF_IOM_HC_IOPORT_READ);
3293 if (rc != VINF_SUCCESS)
3294 return rc;
3295
3296#ifdef VBOX_WITH_HGSMI
3297#ifdef IN_RING3
3298 if (s->vbe_index == VBE_DISPI_INDEX_VBVA_GUEST)
3299 {
3300 *pu32 = HGSMIGuestRead (s->pHGSMI);
3301 PDMCritSectLeave(&s->lock);
3302 return VINF_SUCCESS;
3303 }
3304 if (s->vbe_index == VBE_DISPI_INDEX_VBVA_HOST)
3305 {
3306 *pu32 = HGSMIHostRead (s->pHGSMI);
3307 PDMCritSectLeave(&s->lock);
3308 return VINF_SUCCESS;
3309 }
3310#else
3311 if ( s->vbe_index == VBE_DISPI_INDEX_VBVA_HOST
3312 || s->vbe_index == VBE_DISPI_INDEX_VBVA_GUEST)
3313 {
3314 Log(("vgaIOPortWriteVBEData: %s - Switching to host...\n",
3315 s->vbe_index == VBE_DISPI_INDEX_VBVA_HOST? "VBE_DISPI_INDEX_VBVA_HOST": "VBE_DISPI_INDEX_VBVA_GUEST"));
3316 PDMCritSectLeave(&s->lock);
3317 return VINF_IOM_HC_IOPORT_READ;
3318 }
3319#endif /* !IN_RING3 */
3320#endif /* VBOX_WITH_HGSMI */
3321
3322#ifdef VBE_BYTEWISE_IO
3323 if (cb == 1)
3324 {
3325 if (!s->fReadVBEData)
3326 {
3327 *pu32 = (vbe_ioport_read_data(s, Port) >> 8) & 0xFF;
3328 s->fReadVBEData = true;
3329 PDMCritSectLeave(&s->lock);
3330 return VINF_SUCCESS;
3331 }
3332 else
3333 {
3334 *pu32 = vbe_ioport_read_data(s, Port) & 0xFF;
3335 s->fReadVBEData = false;
3336 PDMCritSectLeave(&s->lock);
3337 return VINF_SUCCESS;
3338 }
3339 }
3340 else
3341#endif
3342 if (cb == 2)
3343 {
3344 *pu32 = vbe_ioport_read_data(s, Port);
3345 PDMCritSectLeave(&s->lock);
3346 return VINF_SUCCESS;
3347 }
3348 else if (cb == 4)
3349 {
3350 /* Quick hack for getting the vram size. */
3351 *pu32 = s->vram_size;
3352 PDMCritSectLeave(&s->lock);
3353 return VINF_SUCCESS;
3354 }
3355 AssertMsgFailed(("vgaIOPortReadVBEData: Port=%#x cb=%d\n", Port, cb));
3356 PDMCritSectLeave(&s->lock);
3357 return VERR_IOM_IOPORT_UNUSED;
3358}
3359
3360
3361/**
3362 * Port I/O Handler for VBE IN operations.
3363 *
3364 * @returns VBox status code.
3365 *
3366 * @param pDevIns The device instance.
3367 * @param pvUser User argument - ignored.
3368 * @param Port Port number used for the IN operation.
3369 * @param pu32 Where to store the result.
3370 * @param cb Number of bytes to read.
3371 */
3372PDMBOTHCBDECL(int) vgaIOPortReadVBEIndex(PPDMDEVINS pDevIns, void *pvUser, RTIOPORT Port, uint32_t *pu32, unsigned cb)
3373{
3374 NOREF(pvUser);
3375 VGAState *s = PDMINS_2_DATA(pDevIns, PVGASTATE);
3376
3377 int rc = PDMCritSectEnter(&s->lock, VINF_IOM_HC_IOPORT_READ);
3378 if (rc != VINF_SUCCESS)
3379 return rc;
3380
3381#ifdef VBE_BYTEWISE_IO
3382 if (cb == 1)
3383 {
3384 if (!s->fReadVBEIndex)
3385 {
3386 *pu32 = (vbe_ioport_read_index(s, Port) >> 8) & 0xFF;
3387 s->fReadVBEIndex = true;
3388 PDMCritSectLeave(&s->lock);
3389 return VINF_SUCCESS;
3390 }
3391 else
3392 {
3393 *pu32 = vbe_ioport_read_index(s, Port) & 0xFF;
3394 s->fReadVBEIndex = false;
3395 PDMCritSectLeave(&s->lock);
3396 return VINF_SUCCESS;
3397 }
3398 }
3399 else
3400#endif
3401 if (cb == 2)
3402 {
3403 *pu32 = vbe_ioport_read_index(s, Port);
3404 PDMCritSectLeave(&s->lock);
3405 return VINF_SUCCESS;
3406 }
3407 PDMCritSectLeave(&s->lock);
3408 AssertMsgFailed(("vgaIOPortReadVBEIndex: Port=%#x cb=%d\n", Port, cb));
3409 return VERR_IOM_IOPORT_UNUSED;
3410}
3411
3412
3413
3414
3415
3416/* -=-=-=-=-=- Guest Context -=-=-=-=-=- */
3417
3418/*
3419 * Internal. For use inside VGAGCMemoryFillWrite only.
3420 * Macro for apply logical operation and bit mask.
3421 */
3422#define APPLY_LOGICAL_AND_MASK(s, val, bit_mask) \
3423 /* apply logical operation */ \
3424 switch(s->gr[3] >> 3) \
3425 { \
3426 case 0: \
3427 default: \
3428 /* nothing to do */ \
3429 break; \
3430 case 1: \
3431 /* and */ \
3432 val &= s->latch; \
3433 break; \
3434 case 2: \
3435 /* or */ \
3436 val |= s->latch; \
3437 break; \
3438 case 3: \
3439 /* xor */ \
3440 val ^= s->latch; \
3441 break; \
3442 } \
3443 /* apply bit mask */ \
3444 val = (val & bit_mask) | (s->latch & ~bit_mask)
3445
3446/**
3447 * Legacy VGA memory (0xa0000 - 0xbffff) write hook, to be called from IOM and from the inside of VGADeviceGC.cpp.
3448 * This is the advanced version of vga_mem_writeb function.
3449 *
3450 * @returns VBox status code.
3451 * @param pThis VGA device structure
3452 * @param pvUser User argument - ignored.
3453 * @param GCPhysAddr Physical address of memory to write.
3454 * @param u32Item Data to write, up to 4 bytes.
3455 * @param cbItem Size of data Item, only 1/2/4 bytes is allowed for now.
3456 * @param cItems Number of data items to write.
3457 */
3458static int vgaInternalMMIOFill(PVGASTATE pThis, void *pvUser, RTGCPHYS GCPhysAddr, uint32_t u32Item, unsigned cbItem, unsigned cItems)
3459{
3460 uint32_t b;
3461 uint32_t write_mask, bit_mask, set_mask;
3462 uint32_t aVal[4];
3463 unsigned i;
3464 NOREF(pvUser);
3465
3466 for (i = 0; i < cbItem; i++)
3467 {
3468 aVal[i] = u32Item & 0xff;
3469 u32Item >>= 8;
3470 }
3471
3472 /* convert to VGA memory offset */
3473 /// @todo add check for the end of region
3474 GCPhysAddr &= 0x1ffff;
3475 switch((pThis->gr[6] >> 2) & 3) {
3476 case 0:
3477 break;
3478 case 1:
3479 if (GCPhysAddr >= 0x10000)
3480 return VINF_SUCCESS;
3481 GCPhysAddr += pThis->bank_offset;
3482 break;
3483 case 2:
3484 GCPhysAddr -= 0x10000;
3485 if (GCPhysAddr >= 0x8000)
3486 return VINF_SUCCESS;
3487 break;
3488 default:
3489 case 3:
3490 GCPhysAddr -= 0x18000;
3491 if (GCPhysAddr >= 0x8000)
3492 return VINF_SUCCESS;
3493 break;
3494 }
3495
3496 if (pThis->sr[4] & 0x08) {
3497 /* chain 4 mode : simplest access */
3498 VERIFY_VRAM_WRITE_OFF_RETURN(pThis, GCPhysAddr + cItems * cbItem - 1);
3499
3500 while (cItems-- > 0)
3501 for (i = 0; i < cbItem; i++)
3502 {
3503 if (pThis->sr[2] & (1 << (GCPhysAddr & 3)))
3504 {
3505 pThis->CTX_SUFF(vram_ptr)[GCPhysAddr] = aVal[i];
3506 vga_set_dirty(pThis, GCPhysAddr);
3507 }
3508 GCPhysAddr++;
3509 }
3510 } else if (pThis->gr[5] & 0x10) {
3511 /* odd/even mode (aka text mode mapping) */
3512 VERIFY_VRAM_WRITE_OFF_RETURN(pThis, GCPhysAddr * 2 + cItems * cbItem - 1);
3513 while (cItems-- > 0)
3514 for (i = 0; i < cbItem; i++)
3515 {
3516 unsigned plane = (pThis->gr[4] & 2) | (GCPhysAddr & 1);
3517 if (pThis->sr[2] & (1 << plane)) {
3518 RTGCPHYS PhysAddr2 = ((GCPhysAddr & ~1) << 2) | plane;
3519 pThis->CTX_SUFF(vram_ptr)[PhysAddr2] = aVal[i];
3520 vga_set_dirty(pThis, PhysAddr2);
3521 }
3522 GCPhysAddr++;
3523 }
3524 } else {
3525 /* standard VGA latched access */
3526 VERIFY_VRAM_WRITE_OFF_RETURN(pThis, GCPhysAddr + cItems * cbItem - 1);
3527
3528 switch(pThis->gr[5] & 3) {
3529 default:
3530 case 0:
3531 /* rotate */
3532 b = pThis->gr[3] & 7;
3533 bit_mask = pThis->gr[8];
3534 bit_mask |= bit_mask << 8;
3535 bit_mask |= bit_mask << 16;
3536 set_mask = mask16[pThis->gr[1]];
3537
3538 for (i = 0; i < cbItem; i++)
3539 {
3540 aVal[i] = ((aVal[i] >> b) | (aVal[i] << (8 - b))) & 0xff;
3541 aVal[i] |= aVal[i] << 8;
3542 aVal[i] |= aVal[i] << 16;
3543
3544 /* apply set/reset mask */
3545 aVal[i] = (aVal[i] & ~set_mask) | (mask16[pThis->gr[0]] & set_mask);
3546
3547 APPLY_LOGICAL_AND_MASK(pThis, aVal[i], bit_mask);
3548 }
3549 break;
3550 case 1:
3551 for (i = 0; i < cbItem; i++)
3552 aVal[i] = pThis->latch;
3553 break;
3554 case 2:
3555 bit_mask = pThis->gr[8];
3556 bit_mask |= bit_mask << 8;
3557 bit_mask |= bit_mask << 16;
3558 for (i = 0; i < cbItem; i++)
3559 {
3560 aVal[i] = mask16[aVal[i] & 0x0f];
3561
3562 APPLY_LOGICAL_AND_MASK(pThis, aVal[i], bit_mask);
3563 }
3564 break;
3565 case 3:
3566 /* rotate */
3567 b = pThis->gr[3] & 7;
3568
3569 for (i = 0; i < cbItem; i++)
3570 {
3571 aVal[i] = (aVal[i] >> b) | (aVal[i] << (8 - b));
3572 bit_mask = pThis->gr[8] & aVal[i];
3573 bit_mask |= bit_mask << 8;
3574 bit_mask |= bit_mask << 16;
3575 aVal[i] = mask16[pThis->gr[0]];
3576
3577 APPLY_LOGICAL_AND_MASK(pThis, aVal[i], bit_mask);
3578 }
3579 break;
3580 }
3581
3582 /* mask data according to sr[2] */
3583 write_mask = mask16[pThis->sr[2]];
3584
3585 /* actually write data */
3586 if (cbItem == 1)
3587 {
3588 /* The most frequently case is 1 byte I/O. */
3589 while (cItems-- > 0)
3590 {
3591 ((uint32_t *)pThis->CTX_SUFF(vram_ptr))[GCPhysAddr] = (((uint32_t *)pThis->CTX_SUFF(vram_ptr))[GCPhysAddr] & ~write_mask) | (aVal[0] & write_mask);
3592 vga_set_dirty(pThis, GCPhysAddr << 2);
3593 GCPhysAddr++;
3594 }
3595 }
3596 else if (cbItem == 2)
3597 {
3598 /* The second case is 2 bytes I/O. */
3599 while (cItems-- > 0)
3600 {
3601 ((uint32_t *)pThis->CTX_SUFF(vram_ptr))[GCPhysAddr] = (((uint32_t *)pThis->CTX_SUFF(vram_ptr))[GCPhysAddr] & ~write_mask) | (aVal[0] & write_mask);
3602 vga_set_dirty(pThis, GCPhysAddr << 2);
3603 GCPhysAddr++;
3604
3605 ((uint32_t *)pThis->CTX_SUFF(vram_ptr))[GCPhysAddr] = (((uint32_t *)pThis->CTX_SUFF(vram_ptr))[GCPhysAddr] & ~write_mask) | (aVal[1] & write_mask);
3606 vga_set_dirty(pThis, GCPhysAddr << 2);
3607 GCPhysAddr++;
3608 }
3609 }
3610 else
3611 {
3612 /* And the rest is 4 bytes. */
3613 Assert(cbItem == 4);
3614 while (cItems-- > 0)
3615 for (i = 0; i < cbItem; i++)
3616 {
3617 ((uint32_t *)pThis->CTX_SUFF(vram_ptr))[GCPhysAddr] = (((uint32_t *)pThis->CTX_SUFF(vram_ptr))[GCPhysAddr] & ~write_mask) | (aVal[i] & write_mask);
3618 vga_set_dirty(pThis, GCPhysAddr << 2);
3619 GCPhysAddr++;
3620 }
3621 }
3622 }
3623 return VINF_SUCCESS;
3624}
3625
3626/**
3627 * Legacy VGA memory (0xa0000 - 0xbffff) write hook, to be called from IOM and from the inside of VGADeviceGC.cpp.
3628 * This is the advanced version of vga_mem_writeb function.
3629 *
3630 * @returns VBox status code.
3631 * @param pDevIns Pointer device instance.
3632 * @param pvUser User argument - ignored.
3633 * @param GCPhysAddr Physical address of memory to write.
3634 * @param u32Item Data to write, up to 4 bytes.
3635 * @param cbItem Size of data Item, only 1/2/4 bytes is allowed for now.
3636 * @param cItems Number of data items to write.
3637 */
3638PDMBOTHCBDECL(int) vgaMMIOFill(PPDMDEVINS pDevIns, void *pvUser, RTGCPHYS GCPhysAddr, uint32_t u32Item, unsigned cbItem, unsigned cItems)
3639{
3640 PVGASTATE pThis = PDMINS_2_DATA(pDevIns, PVGASTATE);
3641
3642 int rc = PDMCritSectEnter(&pThis->lock, VINF_IOM_HC_MMIO_WRITE);
3643 if (rc != VINF_SUCCESS)
3644 return rc;
3645
3646 rc = vgaInternalMMIOFill(pThis, pvUser, GCPhysAddr, u32Item, cbItem, cItems);
3647 PDMCritSectLeave(&pThis->lock);
3648 return rc;
3649}
3650#undef APPLY_LOGICAL_AND_MASK
3651
3652
3653/**
3654 * Legacy VGA memory (0xa0000 - 0xbffff) read hook, to be called from IOM.
3655 *
3656 * @returns VBox status code.
3657 * @param pDevIns Pointer device instance.
3658 * @param pvUser User argument - ignored.
3659 * @param GCPhysAddr Physical address of memory to read.
3660 * @param pv Where to store readed data.
3661 * @param cb Bytes to read.
3662 */
3663PDMBOTHCBDECL(int) vgaMMIORead(PPDMDEVINS pDevIns, void *pvUser, RTGCPHYS GCPhysAddr, void *pv, unsigned cb)
3664{
3665 PVGASTATE pThis = PDMINS_2_DATA(pDevIns, PVGASTATE);
3666 STAM_PROFILE_START(&pThis->CTX_MID_Z(Stat,MemoryRead), a);
3667 NOREF(pvUser);
3668
3669 int rc = PDMCritSectEnter(&pThis->lock, VINF_IOM_HC_MMIO_READ);
3670 if (rc != VINF_SUCCESS)
3671 return rc;
3672
3673 switch (cb)
3674 {
3675 case 1:
3676 *(uint8_t *)pv = vga_mem_readb(pThis, GCPhysAddr, &rc); break;
3677 case 2:
3678 *(uint16_t *)pv = vga_mem_readb(pThis, GCPhysAddr, &rc)
3679 | (vga_mem_readb(pThis, GCPhysAddr + 1, &rc) << 8);
3680 break;
3681 case 4:
3682 *(uint32_t *)pv = vga_mem_readb(pThis, GCPhysAddr, &rc)
3683 | (vga_mem_readb(pThis, GCPhysAddr + 1, &rc) << 8)
3684 | (vga_mem_readb(pThis, GCPhysAddr + 2, &rc) << 16)
3685 | (vga_mem_readb(pThis, GCPhysAddr + 3, &rc) << 24);
3686 break;
3687
3688 case 8:
3689 *(uint64_t *)pv = (uint64_t)vga_mem_readb(pThis, GCPhysAddr, &rc)
3690 | ((uint64_t)vga_mem_readb(pThis, GCPhysAddr + 1, &rc) << 8)
3691 | ((uint64_t)vga_mem_readb(pThis, GCPhysAddr + 2, &rc) << 16)
3692 | ((uint64_t)vga_mem_readb(pThis, GCPhysAddr + 3, &rc) << 24)
3693 | ((uint64_t)vga_mem_readb(pThis, GCPhysAddr + 4, &rc) << 32)
3694 | ((uint64_t)vga_mem_readb(pThis, GCPhysAddr + 5, &rc) << 40)
3695 | ((uint64_t)vga_mem_readb(pThis, GCPhysAddr + 6, &rc) << 48)
3696 | ((uint64_t)vga_mem_readb(pThis, GCPhysAddr + 7, &rc) << 56);
3697 break;
3698
3699 default:
3700 {
3701 uint8_t *pu8Data = (uint8_t *)pv;
3702 while (cb-- > 0)
3703 {
3704 *pu8Data++ = vga_mem_readb(pThis, GCPhysAddr++, &rc);
3705 if (RT_UNLIKELY(rc != VINF_SUCCESS))
3706 break;
3707 }
3708 }
3709 }
3710 STAM_PROFILE_STOP(&pThis->CTX_MID_Z(Stat,MemoryRead), a);
3711 PDMCritSectLeave(&pThis->lock);
3712 return rc;
3713}
3714
3715/**
3716 * Legacy VGA memory (0xa0000 - 0xbffff) write hook, to be called from IOM.
3717 *
3718 * @returns VBox status code.
3719 * @param pDevIns Pointer device instance.
3720 * @param pvUser User argument - ignored.
3721 * @param GCPhysAddr Physical address of memory to write.
3722 * @param pv Pointer to data.
3723 * @param cb Bytes to write.
3724 */
3725PDMBOTHCBDECL(int) vgaMMIOWrite(PPDMDEVINS pDevIns, void *pvUser, RTGCPHYS GCPhysAddr, void *pv, unsigned cb)
3726{
3727 PVGASTATE pThis = PDMINS_2_DATA(pDevIns, PVGASTATE);
3728 uint8_t *pu8 = (uint8_t *)pv;
3729 STAM_PROFILE_START(&pThis->CTX_MID_Z(Stat,MemoryWrite), a);
3730
3731 int rc = PDMCritSectEnter(&pThis->lock, VINF_IOM_HC_MMIO_WRITE);
3732 if (rc != VINF_SUCCESS)
3733 return rc;
3734
3735 switch (cb)
3736 {
3737 case 1:
3738 rc = vga_mem_writeb(pThis, GCPhysAddr, *pu8);
3739 break;
3740#if 1
3741 case 2:
3742 rc = vga_mem_writeb(pThis, GCPhysAddr + 0, pu8[0]);
3743 if (RT_LIKELY(rc == VINF_SUCCESS))
3744 rc = vga_mem_writeb(pThis, GCPhysAddr + 1, pu8[1]);
3745 break;
3746 case 4:
3747 rc = vga_mem_writeb(pThis, GCPhysAddr + 0, pu8[0]);
3748 if (RT_LIKELY(rc == VINF_SUCCESS))
3749 rc = vga_mem_writeb(pThis, GCPhysAddr + 1, pu8[1]);
3750 if (RT_LIKELY(rc == VINF_SUCCESS))
3751 rc = vga_mem_writeb(pThis, GCPhysAddr + 2, pu8[2]);
3752 if (RT_LIKELY(rc == VINF_SUCCESS))
3753 rc = vga_mem_writeb(pThis, GCPhysAddr + 3, pu8[3]);
3754 break;
3755 case 8:
3756 rc = vga_mem_writeb(pThis, GCPhysAddr + 0, pu8[0]);
3757 if (RT_LIKELY(rc == VINF_SUCCESS))
3758 rc = vga_mem_writeb(pThis, GCPhysAddr + 1, pu8[1]);
3759 if (RT_LIKELY(rc == VINF_SUCCESS))
3760 rc = vga_mem_writeb(pThis, GCPhysAddr + 2, pu8[2]);
3761 if (RT_LIKELY(rc == VINF_SUCCESS))
3762 rc = vga_mem_writeb(pThis, GCPhysAddr + 3, pu8[3]);
3763 if (RT_LIKELY(rc == VINF_SUCCESS))
3764 rc = vga_mem_writeb(pThis, GCPhysAddr + 4, pu8[4]);
3765 if (RT_LIKELY(rc == VINF_SUCCESS))
3766 rc = vga_mem_writeb(pThis, GCPhysAddr + 5, pu8[5]);
3767 if (RT_LIKELY(rc == VINF_SUCCESS))
3768 rc = vga_mem_writeb(pThis, GCPhysAddr + 6, pu8[6]);
3769 if (RT_LIKELY(rc == VINF_SUCCESS))
3770 rc = vga_mem_writeb(pThis, GCPhysAddr + 7, pu8[7]);
3771 break;
3772#else
3773 case 2:
3774 rc = vgaMMIOFill(pDevIns, GCPhysAddr, *(uint16_t *)pv, 2, 1);
3775 break;
3776 case 4:
3777 rc = vgaMMIOFill(pDevIns, GCPhysAddr, *(uint32_t *)pv, 4, 1);
3778 break;
3779 case 8:
3780 rc = vgaMMIOFill(pDevIns, GCPhysAddr, *(uint64_t *)pv, 8, 1);
3781 break;
3782#endif
3783 default:
3784 while (cb-- > 0 && rc == VINF_SUCCESS)
3785 rc = vga_mem_writeb(pThis, GCPhysAddr++, *pu8++);
3786 break;
3787
3788 }
3789 STAM_PROFILE_STOP(&pThis->CTX_MID_Z(Stat,MemoryWrite), a);
3790 PDMCritSectLeave(&pThis->lock);
3791 return rc;
3792}
3793
3794
3795/**
3796 * Handle LFB access.
3797 * @returns VBox status code.
3798 * @param pVM VM handle.
3799 * @param pThis VGA device instance data.
3800 * @param GCPhys The access physical address.
3801 * @param GCPtr The access virtual address (only GC).
3802 */
3803static int vgaLFBAccess(PVM pVM, PVGASTATE pThis, RTGCPHYS GCPhys, RTGCPTR GCPtr)
3804{
3805 int rc = PDMCritSectEnter(&pThis->lock, VINF_EM_RAW_EMULATE_INSTR);
3806 if (rc != VINF_SUCCESS)
3807 return rc;
3808
3809 /*
3810 * Set page dirty bit.
3811 */
3812 vga_set_dirty(pThis, GCPhys - pThis->GCPhysVRAM);
3813 pThis->fLFBUpdated = true;
3814
3815 /*
3816 * Turn of the write handler for this particular page and make it R/W.
3817 * Then return telling the caller to restart the guest instruction.
3818 * ASSUME: the guest always maps video memory RW.
3819 */
3820 rc = PGMHandlerPhysicalPageTempOff(pVM, pThis->GCPhysVRAM, GCPhys);
3821 if (RT_SUCCESS(rc))
3822 {
3823#ifndef IN_RING3
3824 rc = PGMShwModifyPage(PDMDevHlpGetVMCPU(pThis->CTX_SUFF(pDevIns)), GCPtr, 1, X86_PTE_RW, ~(uint64_t)X86_PTE_RW);
3825 PDMCritSectLeave(&pThis->lock);
3826 AssertMsgReturn( rc == VINF_SUCCESS
3827 /* In the SMP case the page table might be removed while we wait for the PGM lock in the trap handler. */
3828 || rc == VERR_PAGE_TABLE_NOT_PRESENT
3829 || rc == VERR_PAGE_NOT_PRESENT,
3830 ("PGMShwModifyPage -> GCPtr=%RGv rc=%d\n", GCPtr, rc),
3831 rc);
3832 return VINF_SUCCESS;
3833#else /* IN_RING3 : We don't have any virtual page address of the access here. */
3834 PDMCritSectLeave(&pThis->lock);
3835 Assert(GCPtr == 0);
3836 return VINF_SUCCESS;
3837#endif
3838 }
3839 else
3840 {
3841 PDMCritSectLeave(&pThis->lock);
3842 AssertMsgFailed(("PGMHandlerPhysicalPageTempOff -> rc=%d\n", rc));
3843 }
3844 return rc;
3845}
3846
3847
3848#ifdef IN_RC
3849/**
3850 * #PF Handler for VBE LFB access.
3851 *
3852 * @returns VBox status code (appropriate for GC return).
3853 * @param pVM VM Handle.
3854 * @param uErrorCode CPU Error code.
3855 * @param pRegFrame Trap register frame.
3856 * @param pvFault The fault address (cr2).
3857 * @param GCPhysFault The GC physical address corresponding to pvFault.
3858 * @param pvUser User argument, ignored.
3859 */
3860PDMBOTHCBDECL(int) vgaGCLFBAccessHandler(PVM pVM, RTGCUINT uErrorCode, PCPUMCTXCORE pRegFrame, RTGCPTR pvFault, RTGCPHYS GCPhysFault, void *pvUser)
3861{
3862 PVGASTATE pThis = (PVGASTATE)pvUser;
3863 Assert(pThis);
3864 Assert(GCPhysFault >= pThis->GCPhysVRAM);
3865 AssertMsg(uErrorCode & X86_TRAP_PF_RW, ("uErrorCode=%#x\n", uErrorCode));
3866
3867 return vgaLFBAccess(pVM, pThis, GCPhysFault, pvFault);
3868}
3869
3870#elif IN_RING0
3871
3872/**
3873 * #PF Handler for VBE LFB access.
3874 *
3875 * @returns VBox status code (appropriate for GC return).
3876 * @param pVM VM Handle.
3877 * @param uErrorCode CPU Error code.
3878 * @param pRegFrame Trap register frame.
3879 * @param pvFault The fault address (cr2).
3880 * @param GCPhysFault The GC physical address corresponding to pvFault.
3881 * @param pvUser User argument, ignored.
3882 */
3883PDMBOTHCBDECL(int) vgaR0LFBAccessHandler(PVM pVM, RTGCUINT uErrorCode, PCPUMCTXCORE pRegFrame, RTGCPTR pvFault, RTGCPHYS GCPhysFault, void *pvUser)
3884{
3885 PVGASTATE pThis = (PVGASTATE)pvUser;
3886 Assert(pThis);
3887 Assert(GCPhysFault >= pThis->GCPhysVRAM);
3888 AssertMsg(uErrorCode & X86_TRAP_PF_RW, ("uErrorCode=%#x\n", uErrorCode));
3889
3890 return vgaLFBAccess(pVM, pThis, GCPhysFault, pvFault);
3891}
3892
3893#else /* IN_RING3 */
3894
3895/**
3896 * HC access handler for the LFB.
3897 *
3898 * @returns VINF_SUCCESS if the handler have carried out the operation.
3899 * @returns VINF_PGM_HANDLER_DO_DEFAULT if the caller should carry out the access operation.
3900 * @param pVM VM Handle.
3901 * @param GCPhys The physical address the guest is writing to.
3902 * @param pvPhys The HC mapping of that address.
3903 * @param pvBuf What the guest is reading/writing.
3904 * @param cbBuf How much it's reading/writing.
3905 * @param enmAccessType The access type.
3906 * @param pvUser User argument.
3907 */
3908static DECLCALLBACK(int) vgaR3LFBAccessHandler(PVM pVM, RTGCPHYS GCPhys, void *pvPhys, void *pvBuf, size_t cbBuf, PGMACCESSTYPE enmAccessType, void *pvUser)
3909{
3910 PVGASTATE pThis = (PVGASTATE)pvUser;
3911 int rc;
3912 Assert(pThis);
3913 Assert(GCPhys >= pThis->GCPhysVRAM);
3914 rc = vgaLFBAccess(pVM, pThis, GCPhys, 0);
3915 if (RT_SUCCESS(rc))
3916 return VINF_PGM_HANDLER_DO_DEFAULT;
3917 AssertMsg(rc <= VINF_SUCCESS, ("rc=%Rrc\n", rc));
3918 return rc;
3919}
3920#endif /* IN_RING3 */
3921
3922/* -=-=-=-=-=- All rings: VGA BIOS I/Os -=-=-=-=-=- */
3923
3924/**
3925 * Port I/O Handler for VGA BIOS IN operations.
3926 *
3927 * @returns VBox status code.
3928 *
3929 * @param pDevIns The device instance.
3930 * @param pvUser User argument - ignored.
3931 * @param Port Port number used for the IN operation.
3932 * @param pu32 Where to store the result.
3933 * @param cb Number of bytes read.
3934 */
3935PDMBOTHCBDECL(int) vgaIOPortReadBIOS(PPDMDEVINS pDevIns, void *pvUser, RTIOPORT Port, uint32_t *pu32, unsigned cb)
3936{
3937 NOREF(pDevIns);
3938 NOREF(pvUser);
3939 NOREF(Port);
3940 NOREF(pu32);
3941 NOREF(cb);
3942 return VERR_IOM_IOPORT_UNUSED;
3943}
3944
3945/**
3946 * Port I/O Handler for VGA BIOS OUT operations.
3947 *
3948 * @returns VBox status code.
3949 *
3950 * @param pDevIns The device instance.
3951 * @param pvUser User argument - ignored.
3952 * @param Port Port number used for the IN operation.
3953 * @param u32 The value to output.
3954 * @param cb The value size in bytes.
3955 */
3956PDMBOTHCBDECL(int) vgaIOPortWriteBIOS(PPDMDEVINS pDevIns, void *pvUser, RTIOPORT Port, uint32_t u32, unsigned cb)
3957{
3958 static int lastWasNotNewline = 0; /* We are only called in a single-threaded way */
3959 PVGASTATE pThis = PDMINS_2_DATA(pDevIns, PVGASTATE);
3960
3961 int rc = PDMCritSectEnter(&pThis->lock, VINF_IOM_HC_IOPORT_WRITE);
3962 if (rc != VINF_SUCCESS)
3963 return rc;
3964
3965 /*
3966 * VGA BIOS char printing.
3967 */
3968 if ( cb == 1
3969 && Port == VBE_PRINTF_PORT)
3970 {
3971#if 0
3972 switch (u32)
3973 {
3974 case '\r': Log(("vgabios: <return>\n")); break;
3975 case '\n': Log(("vgabios: <newline>\n")); break;
3976 case '\t': Log(("vgabios: <tab>\n")); break;
3977 default:
3978 Log(("vgabios: %c\n", u32));
3979 }
3980#else
3981 if (lastWasNotNewline == 0)
3982 Log(("vgabios: "));
3983 if (u32 != '\r') /* return - is only sent in conjunction with '\n' */
3984 Log(("%c", u32));
3985 if (u32 == '\n')
3986 lastWasNotNewline = 0;
3987 else
3988 lastWasNotNewline = 1;
3989#endif
3990 PDMCritSectLeave(&pThis->lock);
3991 return VINF_SUCCESS;
3992 }
3993
3994 PDMCritSectLeave(&pThis->lock);
3995 /* not in use. */
3996 return VERR_IOM_IOPORT_UNUSED;
3997}
3998
3999
4000/* -=-=-=-=-=- Ring 3 -=-=-=-=-=- */
4001
4002#ifdef IN_RING3
4003
4004# ifdef VBE_NEW_DYN_LIST
4005/**
4006 * Port I/O Handler for VBE Extra OUT operations.
4007 *
4008 * @returns VBox status code.
4009 *
4010 * @param pDevIns The device instance.
4011 * @param pvUser User argument - ignored.
4012 * @param Port Port number used for the IN operation.
4013 * @param u32 The value to output.
4014 * @param cb The value size in bytes.
4015 */
4016PDMBOTHCBDECL(int) vbeIOPortWriteVBEExtra(PPDMDEVINS pDevIns, void *pvUser, RTIOPORT Port, uint32_t u32, unsigned cb)
4017{
4018 PVGASTATE pThis = PDMINS_2_DATA(pDevIns, PVGASTATE);
4019 NOREF(pvUser);
4020 NOREF(Port);
4021
4022 int rc = PDMCritSectEnter(&pThis->lock, VINF_IOM_HC_IOPORT_WRITE);
4023 if (rc != VINF_SUCCESS)
4024 return rc;
4025
4026 if (cb == 2)
4027 {
4028 Log(("vbeIOPortWriteVBEExtra: addr=%#RX32\n", u32));
4029 pThis->u16VBEExtraAddress = u32;
4030 }
4031 PDMCritSectLeave(&pThis->lock);
4032
4033 Log(("vbeIOPortWriteVBEExtra: Ignoring invalid cb=%d writes to the VBE Extra port!!!\n", cb));
4034 return VINF_SUCCESS;
4035}
4036
4037
4038/**
4039 * Port I/O Handler for VBE Extra IN operations.
4040 *
4041 * @returns VBox status code.
4042 *
4043 * @param pDevIns The device instance.
4044 * @param pvUser User argument - ignored.
4045 * @param Port Port number used for the IN operation.
4046 * @param pu32 Where to store the result.
4047 * @param cb Number of bytes read.
4048 */
4049PDMBOTHCBDECL(int) vbeIOPortReadVBEExtra(PPDMDEVINS pDevIns, void *pvUser, RTIOPORT Port, uint32_t *pu32, unsigned cb)
4050{
4051 PVGASTATE pThis = PDMINS_2_DATA(pDevIns, PVGASTATE);
4052 NOREF(pvUser);
4053 NOREF(Port);
4054
4055 int rc = PDMCritSectEnter(&pThis->lock, VINF_IOM_HC_IOPORT_READ);
4056 if (rc != VINF_SUCCESS)
4057 return rc;
4058
4059 if (pThis->u16VBEExtraAddress == 0xffff)
4060 {
4061 Log(("vbeIOPortReadVBEExtra: Requested number of 64k video banks\n"));
4062 *pu32 = pThis->vram_size / _64K;
4063 rc = VINF_SUCCESS;
4064 }
4065 else
4066 if ( pThis->u16VBEExtraAddress >= pThis->cbVBEExtraData
4067 || pThis->u16VBEExtraAddress + cb > pThis->cbVBEExtraData)
4068 {
4069 *pu32 = 0;
4070 Log(("vbeIOPortReadVBEExtra: Requested address is out of VBE data!!! Address=%#x(%d) cbVBEExtraData=%#x(%d)\n",
4071 pThis->u16VBEExtraAddress, pThis->u16VBEExtraAddress, pThis->cbVBEExtraData, pThis->cbVBEExtraData));
4072 rc = VINF_SUCCESS;
4073 }
4074 else
4075 if (cb == 1)
4076 {
4077 *pu32 = pThis->pu8VBEExtraData[pThis->u16VBEExtraAddress] & 0xFF;
4078
4079 Log(("vbeIOPortReadVBEExtra: cb=%#x %.*Rhxs\n", cb, cb, pu32));
4080 rc = VINF_SUCCESS;
4081 }
4082 else
4083 if (cb == 2)
4084 {
4085 *pu32 = pThis->pu8VBEExtraData[pThis->u16VBEExtraAddress]
4086 | pThis->pu8VBEExtraData[pThis->u16VBEExtraAddress + 1] << 8;
4087
4088 Log(("vbeIOPortReadVBEExtra: cb=%#x %.*Rhxs\n", cb, cb, pu32));
4089 rc = VINF_SUCCESS;
4090 }
4091 else
4092 rc = VERR_IOM_IOPORT_UNUSED;
4093
4094 Log(("vbeIOPortReadVBEExtra: Invalid cb=%d read from the VBE Extra port!!!\n", cb));
4095 PDMCritSectLeave(&pThis->lock);
4096 return rc;
4097}
4098# endif /* VBE_NEW_DYN_LIST */
4099
4100
4101/**
4102 * Parse the logo bitmap data at init time.
4103 *
4104 * @returns VBox status code.
4105 *
4106 * @param pThis The VGA instance data.
4107 */
4108static int vbeParseBitmap(PVGASTATE pThis)
4109{
4110 uint16_t i;
4111 PBMPINFO bmpInfo;
4112 POS2HDR pOs2Hdr;
4113 POS22HDR pOs22Hdr;
4114 PWINHDR pWinHdr;
4115
4116 /*
4117 * Get bitmap header data
4118 */
4119 bmpInfo = (PBMPINFO)(pThis->pu8Logo + sizeof(LOGOHDR));
4120 pWinHdr = (PWINHDR)(pThis->pu8Logo + sizeof(LOGOHDR) + sizeof(BMPINFO));
4121
4122 if (bmpInfo->Type == BMP_ID)
4123 {
4124 switch (pWinHdr->Size)
4125 {
4126 case BMP_HEADER_OS21:
4127 pOs2Hdr = (POS2HDR)pWinHdr;
4128 pThis->cxLogo = pOs2Hdr->Width;
4129 pThis->cyLogo = pOs2Hdr->Height;
4130 pThis->cLogoPlanes = pOs2Hdr->Planes;
4131 pThis->cLogoBits = pOs2Hdr->BitCount;
4132 pThis->LogoCompression = BMP_COMPRESS_NONE;
4133 pThis->cLogoUsedColors = 0;
4134 break;
4135
4136 case BMP_HEADER_OS22:
4137 pOs22Hdr = (POS22HDR)pWinHdr;
4138 pThis->cxLogo = pOs22Hdr->Width;
4139 pThis->cyLogo = pOs22Hdr->Height;
4140 pThis->cLogoPlanes = pOs22Hdr->Planes;
4141 pThis->cLogoBits = pOs22Hdr->BitCount;
4142 pThis->LogoCompression = pOs22Hdr->Compression;
4143 pThis->cLogoUsedColors = pOs22Hdr->ClrUsed;
4144 break;
4145
4146 case BMP_HEADER_WIN3:
4147 pThis->cxLogo = pWinHdr->Width;
4148 pThis->cyLogo = pWinHdr->Height;
4149 pThis->cLogoPlanes = pWinHdr->Planes;
4150 pThis->cLogoBits = pWinHdr->BitCount;
4151 pThis->LogoCompression = pWinHdr->Compression;
4152 pThis->cLogoUsedColors = pWinHdr->ClrUsed;
4153 break;
4154
4155 default:
4156 AssertMsgFailed(("Unsupported bitmap header.\n"));
4157 break;
4158 }
4159
4160 if (pThis->cxLogo > LOGO_MAX_WIDTH || pThis->cyLogo > LOGO_MAX_HEIGHT)
4161 {
4162 AssertMsgFailed(("Bitmap %ux%u is too big.\n", pThis->cxLogo, pThis->cyLogo));
4163 return VERR_INVALID_PARAMETER;
4164 }
4165
4166 if (pThis->cLogoPlanes != 1)
4167 {
4168 AssertMsgFailed(("Bitmap planes %u != 1.\n", pThis->cLogoPlanes));
4169 return VERR_INVALID_PARAMETER;
4170 }
4171
4172 if (pThis->cLogoBits != 4 && pThis->cLogoBits != 8 && pThis->cLogoBits != 24)
4173 {
4174 AssertMsgFailed(("Unsupported %u depth.\n", pThis->cLogoBits));
4175 return VERR_INVALID_PARAMETER;
4176 }
4177
4178 if (pThis->cLogoUsedColors > 256)
4179 {
4180 AssertMsgFailed(("Unsupported %u colors.\n", pThis->cLogoUsedColors));
4181 return VERR_INVALID_PARAMETER;
4182 }
4183
4184 if (pThis->LogoCompression != BMP_COMPRESS_NONE)
4185 {
4186 AssertMsgFailed(("Unsupported %u compression.\n", pThis->LogoCompression));
4187 return VERR_INVALID_PARAMETER;
4188 }
4189
4190 /*
4191 * Read bitmap palette
4192 */
4193 if (!pThis->cLogoUsedColors)
4194 pThis->cLogoPalEntries = 1 << (pThis->cLogoPlanes * pThis->cLogoBits);
4195 else
4196 pThis->cLogoPalEntries = pThis->cLogoUsedColors;
4197
4198 if (pThis->cLogoPalEntries)
4199 {
4200 const uint8_t *pu8Pal = pThis->pu8Logo + sizeof(LOGOHDR) + sizeof(BMPINFO) + pWinHdr->Size; /* ASSUMES Size location (safe) */
4201
4202 for (i = 0; i < pThis->cLogoPalEntries; i++)
4203 {
4204 uint16_t j;
4205 uint32_t u32Pal = 0;
4206
4207 for (j = 0; j < 3; j++)
4208 {
4209 uint8_t b = *pu8Pal++;
4210 u32Pal <<= 8;
4211 u32Pal |= b;
4212 }
4213
4214 pu8Pal++; /* skip unused byte */
4215 pThis->au32LogoPalette[i] = u32Pal;
4216 }
4217 }
4218
4219 /*
4220 * Bitmap data offset
4221 */
4222 pThis->pu8LogoBitmap = pThis->pu8Logo + sizeof(LOGOHDR) + bmpInfo->Offset;
4223 }
4224
4225 return VINF_SUCCESS;
4226}
4227
4228
4229/**
4230 * Show logo bitmap data.
4231 *
4232 * @returns VBox status code.
4233 *
4234 * @param cbDepth Logo depth.
4235 * @param xLogo Logo X position.
4236 * @param yLogo Logo Y position.
4237 * @param cxLogo Logo width.
4238 * @param cyLogo Logo height.
4239 * @param iStep Fade in/fade out step.
4240 * @param pu32Palette Palette data.
4241 * @param pu8Src Source buffer.
4242 * @param pu8Dst Destination buffer.
4243 */
4244static void vbeShowBitmap(uint16_t cBits, uint16_t xLogo, uint16_t yLogo, uint16_t cxLogo, uint16_t cyLogo, uint8_t iStep,
4245 const uint32_t *pu32Palette, const uint8_t *pu8Src, uint8_t *pu8Dst)
4246{
4247 uint16_t i;
4248 size_t cbPadBytes = 0;
4249 size_t cbLineDst = LOGO_MAX_WIDTH * 4;
4250 uint16_t cyLeft = cyLogo;
4251
4252 pu8Dst += xLogo * 4 + yLogo * cbLineDst;
4253
4254 switch (cBits)
4255 {
4256 case 1:
4257 pu8Dst += cyLogo * cbLineDst;
4258 cbPadBytes = 0;
4259 break;
4260
4261 case 4:
4262 if (((cxLogo % 8) == 0) || ((cxLogo % 8) > 6))
4263 cbPadBytes = 0;
4264 else if ((cxLogo % 8) <= 2)
4265 cbPadBytes = 3;
4266 else if ((cxLogo % 8) <= 4)
4267 cbPadBytes = 2;
4268 else
4269 cbPadBytes = 1;
4270 break;
4271
4272 case 8:
4273 cbPadBytes = ((cxLogo % 4) == 0) ? 0 : (4 - (cxLogo % 4));
4274 break;
4275
4276 case 24:
4277 cbPadBytes = cxLogo % 4;
4278 break;
4279 }
4280
4281 uint8_t j = 0, c = 0;
4282
4283 while (cyLeft-- > 0)
4284 {
4285 uint8_t *pu8TmpPtr = pu8Dst;
4286
4287 if (cBits != 1)
4288 j = 0;
4289
4290 for (i = 0; i < cxLogo; i++)
4291 {
4292 uint8_t pix;
4293
4294 switch (cBits)
4295 {
4296 case 1:
4297 {
4298 if (!j)
4299 c = *pu8Src++;
4300
4301 pix = (c & 1) ? 0xFF : 0;
4302 c >>= 1;
4303
4304 if (pix)
4305 {
4306 *pu8TmpPtr++ = pix * iStep / LOGO_SHOW_STEPS;
4307 *pu8TmpPtr++ = pix * iStep / LOGO_SHOW_STEPS;
4308 *pu8TmpPtr++ = pix * iStep / LOGO_SHOW_STEPS;
4309 *pu8TmpPtr++;
4310 }
4311 else
4312 {
4313 pu8TmpPtr += 4;
4314 }
4315
4316 j = (j + 1) % 8;
4317 break;
4318 }
4319
4320 case 4:
4321 {
4322 if (!j)
4323 c = *pu8Src++;
4324
4325 pix = (c >> 4) & 0xF;
4326 c <<= 4;
4327
4328 uint32_t u32Pal = pu32Palette[pix];
4329
4330 pix = (u32Pal >> 16) & 0xFF;
4331 *pu8TmpPtr++ = pix * iStep / LOGO_SHOW_STEPS;
4332 pix = (u32Pal >> 8) & 0xFF;
4333 *pu8TmpPtr++ = pix * iStep / LOGO_SHOW_STEPS;
4334 pix = u32Pal & 0xFF;
4335 *pu8TmpPtr++ = pix * iStep / LOGO_SHOW_STEPS;
4336 *pu8TmpPtr++;
4337
4338 j = (j + 1) % 2;
4339 break;
4340 }
4341
4342 case 8:
4343 {
4344 uint32_t u32Pal = pu32Palette[*pu8Src++];
4345
4346 pix = (u32Pal >> 16) & 0xFF;
4347 *pu8TmpPtr++ = pix * iStep / LOGO_SHOW_STEPS;
4348 pix = (u32Pal >> 8) & 0xFF;
4349 *pu8TmpPtr++ = pix * iStep / LOGO_SHOW_STEPS;
4350 pix = u32Pal & 0xFF;
4351 *pu8TmpPtr++ = pix * iStep / LOGO_SHOW_STEPS;
4352 *pu8TmpPtr++;
4353 break;
4354 }
4355
4356 case 24:
4357 *pu8TmpPtr++ = *pu8Src++ * iStep / LOGO_SHOW_STEPS;
4358 *pu8TmpPtr++ = *pu8Src++ * iStep / LOGO_SHOW_STEPS;
4359 *pu8TmpPtr++ = *pu8Src++ * iStep / LOGO_SHOW_STEPS;
4360 *pu8TmpPtr++;
4361 break;
4362 }
4363 }
4364
4365 pu8Dst -= cbLineDst;
4366 pu8Src += cbPadBytes;
4367 }
4368}
4369
4370
4371
4372
4373/**
4374 * Port I/O Handler for BIOS Logo OUT operations.
4375 *
4376 * @returns VBox status code.
4377 *
4378 * @param pDevIns The device instance.
4379 * @param pvUser User argument - ignored.
4380 * @param Port Port number used for the IN operation.
4381 * @param u32 The value to output.
4382 * @param cb The value size in bytes.
4383 */
4384PDMBOTHCBDECL(int) vbeIOPortWriteCMDLogo(PPDMDEVINS pDevIns, void *pvUser, RTIOPORT Port, uint32_t u32, unsigned cb)
4385{
4386 PVGASTATE pThis = PDMINS_2_DATA(pDevIns, PVGASTATE);
4387 NOREF(pvUser);
4388 NOREF(Port);
4389
4390 Log(("vbeIOPortWriteCMDLogo: cb=%d u32=%#04x(%#04d) (byte)\n", cb, u32, u32));
4391
4392 if (cb == 2)
4393 {
4394 /* Get the logo command */
4395 switch (u32 & 0xFF00)
4396 {
4397 case LOGO_CMD_SET_OFFSET:
4398 pThis->offLogoData = u32 & 0xFF;
4399 break;
4400
4401 case LOGO_CMD_SHOW_BMP:
4402 {
4403 uint8_t iStep = u32 & 0xFF;
4404 const uint8_t *pu8Src = pThis->pu8LogoBitmap;
4405 uint8_t *pu8Dst;
4406 PLOGOHDR pLogoHdr = (PLOGOHDR)pThis->pu8Logo;
4407 uint32_t offDirty = 0;
4408 uint16_t xLogo = (LOGO_MAX_WIDTH - pThis->cxLogo) / 2;
4409 uint16_t yLogo = LOGO_MAX_HEIGHT - (LOGO_MAX_HEIGHT - pThis->cyLogo) / 2;
4410
4411 /* Check VRAM size */
4412 if (pThis->vram_size < LOGO_MAX_SIZE)
4413 break;
4414
4415 if (pThis->vram_size >= LOGO_MAX_SIZE * 2)
4416 pu8Dst = pThis->vram_ptrR3 + LOGO_MAX_SIZE;
4417 else
4418 pu8Dst = pThis->vram_ptrR3;
4419
4420 /* Clear screen - except on power on... */
4421 if (!pThis->fLogoClearScreen)
4422 {
4423 uint32_t *pu32TmpPtr = (uint32_t *)pu8Dst;
4424
4425 /* Clear vram */
4426 for (int i = 0; i < LOGO_MAX_WIDTH; i++)
4427 {
4428 for (int j = 0; j < LOGO_MAX_HEIGHT; j++)
4429 *pu32TmpPtr++ = 0;
4430 }
4431 pThis->fLogoClearScreen = true;
4432 }
4433
4434 /* Show the bitmap. */
4435 vbeShowBitmap(pThis->cLogoBits, xLogo, yLogo,
4436 pThis->cxLogo, pThis->cyLogo,
4437 iStep, &pThis->au32LogoPalette[0],
4438 pu8Src, pu8Dst);
4439
4440 /* Show the 'Press F12...' text. */
4441 if (pLogoHdr->fu8ShowBootMenu == 2)
4442 vbeShowBitmap(1, LOGO_F12TEXT_X, LOGO_F12TEXT_Y,
4443 LOGO_F12TEXT_WIDTH, LOGO_F12TEXT_HEIGHT,
4444 iStep, &pThis->au32LogoPalette[0],
4445 &g_abLogoF12BootText[0], pu8Dst);
4446
4447 /* Blit the offscreen buffer. */
4448 if (pThis->vram_size >= LOGO_MAX_SIZE * 2)
4449 {
4450 uint32_t *pu32TmpDst = (uint32_t *)pThis->vram_ptrR3;
4451 uint32_t *pu32TmpSrc = (uint32_t *)(pThis->vram_ptrR3 + LOGO_MAX_SIZE);
4452 for (int i = 0; i < LOGO_MAX_WIDTH; i++)
4453 {
4454 for (int j = 0; j < LOGO_MAX_HEIGHT; j++)
4455 *pu32TmpDst++ = *pu32TmpSrc++;
4456 }
4457 }
4458
4459 /* Set the dirty flags. */
4460 while (offDirty <= LOGO_MAX_SIZE)
4461 {
4462 vga_set_dirty(pThis, offDirty);
4463 offDirty += PAGE_SIZE;
4464 }
4465 break;
4466 }
4467
4468 default:
4469 Log(("vbeIOPortWriteCMDLogo: invalid command %d\n", u32));
4470 pThis->LogoCommand = LOGO_CMD_NOP;
4471 break;
4472 }
4473
4474 return VINF_SUCCESS;
4475 }
4476
4477 Log(("vbeIOPortWriteCMDLogo: Ignoring invalid cb=%d writes to the VBE Extra port!!!\n", cb));
4478 return VINF_SUCCESS;
4479}
4480
4481
4482/**
4483 * Port I/O Handler for BIOS Logo IN operations.
4484 *
4485 * @returns VBox status code.
4486 *
4487 * @param pDevIns The device instance.
4488 * @param pvUser User argument - ignored.
4489 * @param Port Port number used for the IN operation.
4490 * @param pu32 Where to store the result.
4491 * @param cb Number of bytes read.
4492 */
4493PDMBOTHCBDECL(int) vbeIOPortReadCMDLogo(PPDMDEVINS pDevIns, void *pvUser, RTIOPORT Port, uint32_t *pu32, unsigned cb)
4494{
4495 PVGASTATE pThis = PDMINS_2_DATA(pDevIns, PVGASTATE);
4496 NOREF(pvUser);
4497 NOREF(Port);
4498
4499 PRTUINT64U p;
4500
4501 if (pThis->offLogoData + cb > pThis->cbLogo)
4502 {
4503 Log(("vbeIOPortReadCMDLogo: Requested address is out of Logo data!!! offLogoData=%#x(%d) cbLogo=%#x(%d)\n",
4504 pThis->offLogoData, pThis->offLogoData, pThis->cbLogo, pThis->cbLogo));
4505 return VINF_SUCCESS;
4506 }
4507 p = (PRTUINT64U)&pThis->pu8Logo[pThis->offLogoData];
4508
4509 switch (cb)
4510 {
4511 case 1: *pu32 = p->au8[0]; break;
4512 case 2: *pu32 = p->au16[0]; break;
4513 case 4: *pu32 = p->au32[0]; break;
4514 //case 8: *pu32 = p->au64[0]; break;
4515 default: AssertFailed(); break;
4516 }
4517 Log(("vbeIOPortReadCMDLogo: LogoOffset=%#x(%d) cb=%#x %.*Rhxs\n", pThis->offLogoData, pThis->offLogoData, cb, cb, pu32));
4518
4519 pThis->LogoCommand = LOGO_CMD_NOP;
4520 pThis->offLogoData += cb;
4521
4522 return VINF_SUCCESS;
4523}
4524
4525/**
4526 * Info handler, device version. Dumps VGA memory formatted as
4527 * ASCII text, no attributes. Only looks at the first page.
4528 *
4529 * @param pDevIns Device instance which registered the info.
4530 * @param pHlp Callback functions for doing output.
4531 * @param pszArgs Argument string. Optional and specific to the handler.
4532 */
4533static DECLCALLBACK(void) vgaInfoText(PPDMDEVINS pDevIns, PCDBGFINFOHLP pHlp, const char *pszArgs)
4534{
4535 PVGASTATE pThis = PDMINS_2_DATA(pDevIns, PVGASTATE);
4536 uint8_t *src;
4537 unsigned row, col;
4538 unsigned num_rows = 25, num_cols = 80;
4539
4540 /* Pure paranoia... */
4541 Assert(num_rows * num_cols * 8 <= pThis->vram_size);
4542
4543 src = pThis->vram_ptrR3;
4544 if (src) {
4545 for (col = 0; col < num_cols; ++col) pHlp->pfnPrintf(pHlp, "-"); pHlp->pfnPrintf(pHlp, "\n");
4546 for (row = 0; row < num_rows; ++row)
4547 {
4548 for (col = 0; col < num_cols; ++col)
4549 {
4550 pHlp->pfnPrintf(pHlp, "%c", *src);
4551 src += 8; /* chars are spaced 8 bytes apart */
4552 }
4553 pHlp->pfnPrintf(pHlp, "\n");
4554 }
4555 for (col = 0; col < num_cols; ++col) pHlp->pfnPrintf(pHlp, "-"); pHlp->pfnPrintf(pHlp, "\n");
4556 }
4557 else
4558 {
4559 pHlp->pfnPrintf(pHlp, "VGA memory not available!\n");
4560 }
4561}
4562
4563/**
4564 * Info handler, device version. Dumps VGA Sequencer registers.
4565 *
4566 * @param pDevIns Device instance which registered the info.
4567 * @param pHlp Callback functions for doing output.
4568 * @param pszArgs Argument string. Optional and specific to the handler.
4569 */
4570static DECLCALLBACK(void) vgaInfoSR(PPDMDEVINS pDevIns, PCDBGFINFOHLP pHlp, const char *pszArgs)
4571{
4572 PVGASTATE s = PDMINS_2_DATA(pDevIns, PVGASTATE);
4573 unsigned i;
4574
4575 pHlp->pfnPrintf(pHlp, "VGA Sequencer (3C5): SR index 3C4:%02X\n", s->sr_index);
4576 Assert(sizeof(s->sr) >= 8);
4577 for (i = 0; i < 5; ++i)
4578 {
4579 pHlp->pfnPrintf(pHlp, " SR%02X:%02X", i, s->sr[i]);
4580 }
4581 pHlp->pfnPrintf(pHlp, "\n");
4582}
4583
4584/**
4585 * Info handler, device version. Dumps VGA CRTC registers.
4586 *
4587 * @param pDevIns Device instance which registered the info.
4588 * @param pHlp Callback functions for doing output.
4589 * @param pszArgs Argument string. Optional and specific to the handler.
4590 */
4591static DECLCALLBACK(void) vgaInfoCR(PPDMDEVINS pDevIns, PCDBGFINFOHLP pHlp, const char *pszArgs)
4592{
4593 PVGASTATE s = PDMINS_2_DATA(pDevIns, PVGASTATE);
4594 unsigned i;
4595
4596 pHlp->pfnPrintf(pHlp, "VGA CRTC (3D5): CRTC index 3D4:%02X\n", s->cr_index);
4597 Assert(sizeof(s->cr) >= 24);
4598 for (i = 0; i < 10; ++i)
4599 {
4600 pHlp->pfnPrintf(pHlp, " CR%02X:%02X", i, s->cr[i]);
4601 }
4602 pHlp->pfnPrintf(pHlp, "\n");
4603 for (i = 10; i < 20; ++i)
4604 {
4605 pHlp->pfnPrintf(pHlp, " CR%02X:%02X", i, s->cr[i]);
4606 }
4607 pHlp->pfnPrintf(pHlp, "\n");
4608 for (i = 20; i < 25; ++i)
4609 {
4610 pHlp->pfnPrintf(pHlp, " CR%02X:%02X", i, s->cr[i]);
4611 }
4612 pHlp->pfnPrintf(pHlp, "\n");
4613}
4614
4615/**
4616 * Info handler, device version. Dumps VGA Sequencer registers.
4617 *
4618 * @param pDevIns Device instance which registered the info.
4619 * @param pHlp Callback functions for doing output.
4620 * @param pszArgs Argument string. Optional and specific to the handler.
4621 */
4622static DECLCALLBACK(void) vgaInfoAR(PPDMDEVINS pDevIns, PCDBGFINFOHLP pHlp, const char *pszArgs)
4623{
4624 PVGASTATE s = PDMINS_2_DATA(pDevIns, PVGASTATE);
4625 unsigned i;
4626
4627 pHlp->pfnPrintf(pHlp, "VGA Attribute Controller (3C0): index reg %02X, flip-flop: %d (%s)\n",
4628 s->ar_index, s->ar_flip_flop, s->ar_flip_flop ? "data" : "index" );
4629 Assert(sizeof(s->ar) >= 0x14);
4630 pHlp->pfnPrintf(pHlp, " Palette:");
4631 for (i = 0; i < 0x10; ++i)
4632 {
4633 pHlp->pfnPrintf(pHlp, " %02X", i, s->ar[i]);
4634 }
4635 pHlp->pfnPrintf(pHlp, "\n");
4636 for (i = 0x10; i <= 0x14; ++i)
4637 {
4638 pHlp->pfnPrintf(pHlp, " AR%02X:%02X", i, s->ar[i]);
4639 }
4640 pHlp->pfnPrintf(pHlp, "\n");
4641}
4642
4643/**
4644 * Info handler, device version. Dumps VGA DAC registers.
4645 *
4646 * @param pDevIns Device instance which registered the info.
4647 * @param pHlp Callback functions for doing output.
4648 * @param pszArgs Argument string. Optional and specific to the handler.
4649 */
4650static DECLCALLBACK(void) vgaInfoDAC(PPDMDEVINS pDevIns, PCDBGFINFOHLP pHlp, const char *pszArgs)
4651{
4652 PVGASTATE s = PDMINS_2_DATA(pDevIns, PVGASTATE);
4653 unsigned i;
4654
4655 pHlp->pfnPrintf(pHlp, "VGA DAC contents:\n");
4656 for (i = 0; i < 0x100; ++i)
4657 {
4658 pHlp->pfnPrintf(pHlp, " %02X: %02X %02X %02X\n",
4659 i, s->palette[i*3+0], s->palette[i*3+1], s->palette[i*3+2]);
4660 }
4661}
4662
4663
4664/* -=-=-=-=-=- Ring 3: IBase -=-=-=-=-=- */
4665
4666/**
4667 * Queries an interface to the driver.
4668 *
4669 * @returns Pointer to interface.
4670 * @returns NULL if the interface was not supported by the driver.
4671 * @param pInterface Pointer to this interface structure.
4672 * @param enmInterface The requested interface identification.
4673 * @thread Any thread.
4674 */
4675static DECLCALLBACK(void *) vgaPortQueryInterface(PPDMIBASE pInterface, PDMINTERFACE enmInterface)
4676{
4677 PVGASTATE pThis = (PVGASTATE)((uintptr_t)pInterface - RT_OFFSETOF(VGASTATE, Base));
4678 switch (enmInterface)
4679 {
4680 case PDMINTERFACE_BASE:
4681 return &pThis->Base;
4682 case PDMINTERFACE_DISPLAY_PORT:
4683 return &pThis->Port;
4684#if defined(VBOX_WITH_HGSMI) && defined(VBOX_WITH_VIDEOHWACCEL)
4685 case PDMINTERFACE_DISPLAY_VBVA_CALLBACKS:
4686 return &pThis->VBVACallbacks;
4687#endif
4688 default:
4689 return NULL;
4690 }
4691}
4692
4693
4694/* -=-=-=-=-=- Ring 3: Dummy IDisplayConnector -=-=-=-=-=- */
4695
4696/**
4697 * Resize the display.
4698 * This is called when the resolution changes. This usually happens on
4699 * request from the guest os, but may also happen as the result of a reset.
4700 *
4701 * @param pInterface Pointer to this interface.
4702 * @param cx New display width.
4703 * @param cy New display height
4704 * @thread The emulation thread.
4705 */
4706static DECLCALLBACK(int) vgaDummyResize(PPDMIDISPLAYCONNECTOR pInterface, uint32_t bpp, void *pvVRAM, uint32_t cbLine, uint32_t cx, uint32_t cy)
4707{
4708 return VINF_SUCCESS;
4709}
4710
4711
4712/**
4713 * Update a rectangle of the display.
4714 * PDMIDISPLAYPORT::pfnUpdateDisplay is the caller.
4715 *
4716 * @param pInterface Pointer to this interface.
4717 * @param x The upper left corner x coordinate of the rectangle.
4718 * @param y The upper left corner y coordinate of the rectangle.
4719 * @param cx The width of the rectangle.
4720 * @param cy The height of the rectangle.
4721 * @thread The emulation thread.
4722 */
4723static DECLCALLBACK(void) vgaDummyUpdateRect(PPDMIDISPLAYCONNECTOR pInterface, uint32_t x, uint32_t y, uint32_t cx, uint32_t cy)
4724{
4725}
4726
4727
4728/**
4729 * Refresh the display.
4730 *
4731 * The interval between these calls is set by
4732 * PDMIDISPLAYPORT::pfnSetRefreshRate(). The driver should call
4733 * PDMIDISPLAYPORT::pfnUpdateDisplay() if it wishes to refresh the
4734 * display. PDMIDISPLAYPORT::pfnUpdateDisplay calls pfnUpdateRect with
4735 * the changed rectangles.
4736 *
4737 * @param pInterface Pointer to this interface.
4738 * @thread The emulation thread.
4739 */
4740static DECLCALLBACK(void) vgaDummyRefresh(PPDMIDISPLAYCONNECTOR pInterface)
4741{
4742}
4743
4744
4745/* -=-=-=-=-=- Ring 3: IDisplayPort -=-=-=-=-=- */
4746
4747/** Converts a display port interface pointer to a vga state pointer. */
4748#define IDISPLAYPORT_2_VGASTATE(pInterface) ( (PVGASTATE)((uintptr_t)pInterface - RT_OFFSETOF(VGASTATE, Port)) )
4749
4750
4751/**
4752 * Update the display with any changed regions.
4753 *
4754 * @param pInterface Pointer to this interface.
4755 * @see PDMIKEYBOARDPORT::pfnUpdateDisplay() for details.
4756 */
4757static DECLCALLBACK(int) vgaPortUpdateDisplay(PPDMIDISPLAYPORT pInterface)
4758{
4759 PVGASTATE pThis = IDISPLAYPORT_2_VGASTATE(pInterface);
4760 PDMDEV_ASSERT_EMT(VGASTATE2DEVINS(pThis));
4761 PPDMDEVINS pDevIns = pThis->CTX_SUFF(pDevIns);
4762
4763 int rc = PDMCritSectEnter(&pThis->lock, VERR_SEM_BUSY);
4764 AssertRC(rc);
4765
4766#ifndef VBOX_WITH_HGSMI
4767 /* This should be called only in non VBVA mode. */
4768#else
4769 if (VBVAUpdateDisplay (pThis) == VINF_SUCCESS)
4770 {
4771 PDMCritSectLeave(&pThis->lock);
4772 return VINF_SUCCESS;
4773 }
4774#endif /* VBOX_WITH_HGSMI */
4775
4776 if (pThis->fHasDirtyBits && pThis->GCPhysVRAM && pThis->GCPhysVRAM != NIL_RTGCPHYS32)
4777 {
4778 PGMHandlerPhysicalReset(PDMDevHlpGetVM(pDevIns), pThis->GCPhysVRAM);
4779 pThis->fHasDirtyBits = false;
4780 }
4781 if (pThis->fRemappedVGA)
4782 {
4783 IOMMMIOResetRegion(PDMDevHlpGetVM(pDevIns), 0x000a0000);
4784 pThis->fRemappedVGA = false;
4785 }
4786
4787 rc = vga_update_display(pThis, false);
4788 if (rc != VINF_SUCCESS)
4789 {
4790 PDMCritSectLeave(&pThis->lock);
4791 return rc;
4792 }
4793 PDMCritSectLeave(&pThis->lock);
4794 return VINF_SUCCESS;
4795}
4796
4797
4798/**
4799 * Update the entire display.
4800 *
4801 * @param pInterface Pointer to this interface.
4802 * @see PDMIKEYBOARDPORT::pfnUpdateDisplayAll() for details.
4803 */
4804static DECLCALLBACK(int) vgaPortUpdateDisplayAll(PPDMIDISPLAYPORT pInterface)
4805{
4806 PVGASTATE pThis = IDISPLAYPORT_2_VGASTATE(pInterface);
4807 PDMDEV_ASSERT_EMT(VGASTATE2DEVINS(pThis));
4808 PPDMDEVINS pDevIns = pThis->CTX_SUFF(pDevIns);
4809
4810 /* This is called both in VBVA mode and normal modes. */
4811
4812#ifdef DEBUG_sunlover
4813 LogFlow(("vgaPortUpdateDisplayAll\n"));
4814#endif /* DEBUG_sunlover */
4815
4816 int rc = PDMCritSectEnter(&pThis->lock, VERR_SEM_BUSY);
4817 AssertRC(rc);
4818
4819 /* The dirty bits array has been just cleared, reset handlers as well. */
4820 if (pThis->GCPhysVRAM && pThis->GCPhysVRAM != NIL_RTGCPHYS32)
4821 {
4822 PGMHandlerPhysicalReset(PDMDevHlpGetVM(pDevIns), pThis->GCPhysVRAM);
4823 }
4824 if (pThis->fRemappedVGA)
4825 {
4826 IOMMMIOResetRegion(PDMDevHlpGetVM(pDevIns), 0x000a0000);
4827 pThis->fRemappedVGA = false;
4828 }
4829
4830 pThis->graphic_mode = -1; /* force full update */
4831
4832 rc = vga_update_display(pThis, true);
4833
4834 PDMCritSectLeave(&pThis->lock);
4835 return rc;
4836}
4837
4838
4839/**
4840 * Sets the refresh rate and restart the timer.
4841 *
4842 * @returns VBox status code.
4843 * @param pInterface Pointer to this interface.
4844 * @param cMilliesInterval Number of millies between two refreshes.
4845 * @see PDMIKEYBOARDPORT::pfnSetRefreshRate() for details.
4846 */
4847static DECLCALLBACK(int) vgaPortSetRefreshRate(PPDMIDISPLAYPORT pInterface, uint32_t cMilliesInterval)
4848{
4849 PVGASTATE pThis = IDISPLAYPORT_2_VGASTATE(pInterface);
4850
4851 pThis->cMilliesRefreshInterval = cMilliesInterval;
4852 if (cMilliesInterval)
4853 return TMTimerSetMillies(pThis->RefreshTimer, cMilliesInterval);
4854 return TMTimerStop(pThis->RefreshTimer);
4855}
4856
4857
4858/** @copydoc PDMIDISPLAYPORT::pfnQueryColorDepth */
4859static DECLCALLBACK(int) vgaPortQueryColorDepth(PPDMIDISPLAYPORT pInterface, uint32_t *pcBits)
4860{
4861 PVGASTATE pThis = IDISPLAYPORT_2_VGASTATE(pInterface);
4862
4863 if (!pcBits)
4864 return VERR_INVALID_PARAMETER;
4865 *pcBits = vga_get_bpp(pThis);
4866 return VINF_SUCCESS;
4867}
4868
4869/**
4870 * Create a 32-bbp snapshot of the display.
4871 *
4872 * @param pInterface Pointer to this interface.
4873 * @param pvData Pointer the buffer to copy the bits to.
4874 * @param cbData Size of the buffer.
4875 * @param pcx Where to store the width of the bitmap. (optional)
4876 * @param pcy Where to store the height of the bitmap. (optional)
4877 * @param pcbData Where to store the actual size of the bitmap. (optional)
4878 * @see PDMIKEYBOARDPORT::pfnSnapshot() for details.
4879 */
4880static DECLCALLBACK(int) vgaPortSnapshot(PPDMIDISPLAYPORT pInterface, void *pvData, size_t cbData, uint32_t *pcx, uint32_t *pcy, size_t *pcbData)
4881{
4882 /* @todo r=sunlover: replace the method with a direct VRAM rendering like in vgaPortUpdateDisplayRect. */
4883 PPDMIDISPLAYCONNECTOR pConnector;
4884 PDMIDISPLAYCONNECTOR Connector;
4885 int32_t graphic_mode;
4886 bool fRenderVRAM;
4887 size_t cbRequired;
4888 PVGASTATE pThis = IDISPLAYPORT_2_VGASTATE(pInterface);
4889 PDMDEV_ASSERT_EMT(VGASTATE2DEVINS(pThis));
4890 LogFlow(("vgaPortSnapshot: pvData=%p cbData=%d pcx=%p pcy=%p pcbData=%p\n", pvData, cbData, pcx, pcy, pcbData));
4891
4892 /*
4893 * Validate input.
4894 */
4895 if (!pvData)
4896 return VERR_INVALID_PARAMETER;
4897
4898 /*
4899 * Do a regular refresh first to resolve any pending resize issues.
4900 *
4901 * 20060317 It used to be pfnUpdateDisplay, but by VBVA design
4902 * only pfnUpdateDisplayAll is allowed to be called in VBVA mode.
4903 * Also since the goal here is to have updated display for screenshot,
4904 * the UpdateDisplayAll is even more logical to call. (sunlover)
4905 */
4906 pInterface->pfnUpdateDisplayAll(pInterface);
4907
4908 /*
4909 * Validate the buffer size.
4910 */
4911 cbRequired = RT_ALIGN_Z(pThis->last_scr_width, 4) * pThis->last_scr_height * 4;
4912 if (cbRequired > cbData)
4913 {
4914 Log(("vgaPortSnapshot: %d bytes are required, a buffer of %d bytes is profiled.\n", cbRequired, cbData));
4915 return VERR_BUFFER_OVERFLOW;
4916 }
4917
4918 /*
4919 * Temporarily replace the display connector interface with a fake one.
4920 */
4921 Connector.pu8Data = (uint8_t*)pvData;
4922 Connector.cBits = 32;
4923 Connector.cx = pThis->pDrv->cx;
4924 Connector.cy = pThis->pDrv->cy;
4925 Connector.cbScanline = RT_ALIGN_32(Connector.cx, 4) * 4;
4926 Connector.pfnRefresh = vgaDummyRefresh;
4927 Connector.pfnResize = vgaDummyResize;
4928 Connector.pfnUpdateRect = vgaDummyUpdateRect;
4929
4930 /* save & replace state data. */
4931 pConnector = pThis->pDrv;
4932 pThis->pDrv = &Connector;
4933 graphic_mode = pThis->graphic_mode;
4934 pThis->graphic_mode = -1; /* force a full refresh. */
4935 fRenderVRAM = pThis->fRenderVRAM;
4936 pThis->fRenderVRAM = 1; /* force the guest VRAM rendering to the given buffer. */
4937
4938 /* make the snapshot.
4939 * The second parameter is 'false' because the current display state, already updated by the
4940 * pfnUpdateDisplayAll call above, is being rendered to an external buffer using a fake connector.
4941 * That is if display is blanked, we expect a black screen in the external buffer.
4942 */
4943 int rc = vga_update_display(pThis, false);
4944
4945 /* restore */
4946 pThis->pDrv = pConnector;
4947 pThis->graphic_mode = graphic_mode;
4948 pThis->fRenderVRAM = fRenderVRAM;
4949
4950 if (rc != VINF_SUCCESS)
4951 return rc;
4952
4953 /*
4954 * Return the result.
4955 */
4956 if (pcx)
4957 *pcx = Connector.cx;
4958 if (pcy)
4959 *pcy = Connector.cy;
4960 if (pcbData)
4961 *pcbData = cbRequired;
4962 LogFlow(("vgaPortSnapshot: returns VINF_SUCCESS (cx=%d cy=%d cbData=%d)\n", Connector.cx, Connector.cy, cbRequired));
4963 return VINF_SUCCESS;
4964}
4965
4966
4967/**
4968 * Copy bitmap to the display.
4969 *
4970 * @param pInterface Pointer to this interface.
4971 * @param pvData Pointer to the bitmap bits.
4972 * @param x The upper left corner x coordinate of the destination rectangle.
4973 * @param y The upper left corner y coordinate of the destination rectangle.
4974 * @param cx The width of the source and destination rectangles.
4975 * @param cy The height of the source and destination rectangles.
4976 * @see PDMIDISPLAYPORT::pfnDisplayBlt() for details.
4977 */
4978static DECLCALLBACK(int) vgaPortDisplayBlt(PPDMIDISPLAYPORT pInterface, const void *pvData, uint32_t x, uint32_t y, uint32_t cx, uint32_t cy)
4979{
4980 PVGASTATE pThis = IDISPLAYPORT_2_VGASTATE(pInterface);
4981 int rc = VINF_SUCCESS;
4982 PDMDEV_ASSERT_EMT(VGASTATE2DEVINS(pThis));
4983 LogFlow(("vgaPortDisplayBlt: pvData=%p x=%d y=%d cx=%d cy=%d\n", pvData, x, y, cx, cy));
4984
4985 /*
4986 * Validate input.
4987 */
4988 if ( pvData
4989 && x < pThis->pDrv->cx
4990 && cx <= pThis->pDrv->cx
4991 && cx + x <= pThis->pDrv->cx
4992 && y < pThis->pDrv->cy
4993 && cy <= pThis->pDrv->cy
4994 && cy + y <= pThis->pDrv->cy)
4995 {
4996 /*
4997 * Determin bytes per pixel in the destination buffer.
4998 */
4999 size_t cbPixelDst = 0;
5000 switch (pThis->pDrv->cBits)
5001 {
5002 case 8:
5003 cbPixelDst = 1;
5004 break;
5005 case 15:
5006 case 16:
5007 cbPixelDst = 2;
5008 break;
5009 case 24:
5010 cbPixelDst = 3;
5011 break;
5012 case 32:
5013 cbPixelDst = 4;
5014 break;
5015 default:
5016 rc = VERR_INVALID_PARAMETER;
5017 break;
5018 }
5019 if (RT_SUCCESS(rc))
5020 {
5021 /*
5022 * The blitting loop.
5023 */
5024 size_t cbLineSrc = RT_ALIGN_Z(cx, 4) * 4;
5025 uint8_t *pu8Src = (uint8_t *)pvData;
5026 size_t cbLineDst = pThis->pDrv->cbScanline;
5027 uint8_t *pu8Dst = pThis->pDrv->pu8Data + y * cbLineDst + x * cbPixelDst;
5028 uint32_t cyLeft = cy;
5029 vga_draw_line_func *pfnVgaDrawLine = vga_draw_line_table[VGA_DRAW_LINE32 * 4 + get_depth_index(pThis->pDrv->cBits)];
5030 Assert(pfnVgaDrawLine);
5031 while (cyLeft-- > 0)
5032 {
5033 pfnVgaDrawLine(pThis, pu8Dst, pu8Src, cx);
5034 pu8Dst += cbLineDst;
5035 pu8Src += cbLineSrc;
5036 }
5037
5038 /*
5039 * Invalidate the area.
5040 */
5041 pThis->pDrv->pfnUpdateRect(pThis->pDrv, x, y, cx, cy);
5042 }
5043 }
5044 else
5045 rc = VERR_INVALID_PARAMETER;
5046
5047 LogFlow(("vgaPortDisplayBlt: returns %Rrc\n", rc));
5048 return rc;
5049}
5050
5051static DECLCALLBACK(void) vgaPortUpdateDisplayRect (PPDMIDISPLAYPORT pInterface, int32_t x, int32_t y, uint32_t w, uint32_t h)
5052{
5053 uint32_t v;
5054 vga_draw_line_func *vga_draw_line;
5055
5056 uint32_t cbPixelDst;
5057 uint32_t cbLineDst;
5058 uint8_t *pu8Dst;
5059
5060 uint32_t cbPixelSrc;
5061 uint32_t cbLineSrc;
5062 uint8_t *pu8Src;
5063
5064 uint32_t u32OffsetSrc, u32Dummy;
5065
5066 PVGASTATE s = IDISPLAYPORT_2_VGASTATE(pInterface);
5067
5068#ifdef DEBUG_sunlover
5069 LogFlow(("vgaPortUpdateDisplayRect: %d,%d %dx%d\n", x, y, w, h));
5070#endif /* DEBUG_sunlover */
5071
5072 Assert(pInterface);
5073 Assert(s->pDrv);
5074 Assert(s->pDrv->pu8Data);
5075
5076 /* Check if there is something to do at all. */
5077 if (!s->fRenderVRAM)
5078 {
5079 /* The framebuffer uses the guest VRAM directly. */
5080#ifdef DEBUG_sunlover
5081 LogFlow(("vgaPortUpdateDisplayRect: nothing to do fRender is false.\n"));
5082#endif /* DEBUG_sunlover */
5083 return;
5084 }
5085
5086 /* Correct negative x and y coordinates. */
5087 if (x < 0)
5088 {
5089 x += w; /* Compute xRight which is also the new width. */
5090 w = (x < 0) ? 0 : x;
5091 x = 0;
5092 }
5093
5094 if (y < 0)
5095 {
5096 y += h; /* Compute yBottom, which is also the new height. */
5097 h = (y < 0) ? 0 : y;
5098 y = 0;
5099 }
5100
5101 /* Also check if coords are greater than the display resolution. */
5102 if (x + w > s->pDrv->cx)
5103 {
5104#ifndef VBOX
5105 w = s->pDrv->cx > x? s->pDrv->cx - x: 0;
5106#else
5107 // x < 0 is not possible here
5108 w = s->pDrv->cx > (uint32_t)x? s->pDrv->cx - x: 0;
5109#endif
5110 }
5111
5112 if (y + h > s->pDrv->cy)
5113 {
5114#ifndef VBOX
5115 h = s->pDrv->cy > y? s->pDrv->cy - y: 0;
5116#else
5117 // y < 0 is not possible here
5118 h = s->pDrv->cy > (uint32_t)y? s->pDrv->cy - y: 0;
5119#endif
5120 }
5121
5122#ifdef DEBUG_sunlover
5123 LogFlow(("vgaPortUpdateDisplayRect: %d,%d %dx%d (corrected coords)\n", x, y, w, h));
5124#endif /* DEBUG_sunlover */
5125
5126 /* Check if there is something to do at all. */
5127 if (w == 0 || h == 0)
5128 {
5129 /* Empty rectangle. */
5130#ifdef DEBUG_sunlover
5131 LogFlow(("vgaPortUpdateDisplayRect: nothing to do: %dx%d\n", w, h));
5132#endif /* DEBUG_sunlover */
5133 return;
5134 }
5135
5136 /** @todo This method should be made universal and not only for VBVA.
5137 * VGA_DRAW_LINE* must be selected and src/dst address calculation
5138 * changed.
5139 */
5140
5141 /* Choose the rendering function. */
5142 switch(s->get_bpp(s))
5143 {
5144 default:
5145 case 0:
5146 /* A LFB mode is already disabled, but the callback is still called
5147 * by Display because VBVA buffer is being flushed.
5148 * Nothing to do, just return.
5149 */
5150 return;
5151 case 8:
5152 v = VGA_DRAW_LINE8;
5153 break;
5154 case 15:
5155 v = VGA_DRAW_LINE15;
5156 break;
5157 case 16:
5158 v = VGA_DRAW_LINE16;
5159 break;
5160 case 24:
5161 v = VGA_DRAW_LINE24;
5162 break;
5163 case 32:
5164 v = VGA_DRAW_LINE32;
5165 break;
5166 }
5167
5168 vga_draw_line = vga_draw_line_table[v * 4 + get_depth_index(s->pDrv->cBits)];
5169
5170 /* Compute source and destination addresses and pitches. */
5171 cbPixelDst = (s->pDrv->cBits + 7) / 8;
5172 cbLineDst = s->pDrv->cbScanline;
5173 pu8Dst = s->pDrv->pu8Data + y * cbLineDst + x * cbPixelDst;
5174
5175 cbPixelSrc = (s->get_bpp(s) + 7) / 8;
5176 s->get_offsets (s, &cbLineSrc, &u32OffsetSrc, &u32Dummy);
5177
5178 /* Assume that rendering is performed only on visible part of VRAM.
5179 * This is true because coordinates were verified.
5180 */
5181 pu8Src = s->vram_ptrR3;
5182 pu8Src += u32OffsetSrc + y * cbLineSrc + x * cbPixelSrc;
5183
5184 /* Render VRAM to framebuffer. */
5185
5186#ifdef DEBUG_sunlover
5187 LogFlow(("vgaPortUpdateDisplayRect: dst: %p, %d, %d. src: %p, %d, %d\n", pu8Dst, cbLineDst, cbPixelDst, pu8Src, cbLineSrc, cbPixelSrc));
5188#endif /* DEBUG_sunlover */
5189
5190 while (h-- > 0)
5191 {
5192 vga_draw_line (s, pu8Dst, pu8Src, w);
5193 pu8Dst += cbLineDst;
5194 pu8Src += cbLineSrc;
5195 }
5196
5197#ifdef DEBUG_sunlover
5198 LogFlow(("vgaPortUpdateDisplayRect: completed.\n"));
5199#endif /* DEBUG_sunlover */
5200}
5201
5202static DECLCALLBACK(void) vgaPortSetRenderVRAM(PPDMIDISPLAYPORT pInterface, bool fRender)
5203{
5204 PVGASTATE s = IDISPLAYPORT_2_VGASTATE(pInterface);
5205
5206 LogFlow(("vgaPortSetRenderVRAM: fRender = %d\n", fRender));
5207
5208 s->fRenderVRAM = fRender;
5209}
5210
5211
5212static DECLCALLBACK(void) vgaTimerRefresh(PPDMDEVINS pDevIns, PTMTIMER pTimer, void *pvUser)
5213{
5214 PVGASTATE pThis = (PVGASTATE)pvUser;
5215
5216 if (pThis->pDrv)
5217 pThis->pDrv->pfnRefresh(pThis->pDrv);
5218
5219 if (pThis->cMilliesRefreshInterval)
5220 TMTimerSetMillies(pTimer, pThis->cMilliesRefreshInterval);
5221}
5222
5223
5224/* -=-=-=-=-=- Ring 3: PCI Device -=-=-=-=-=- */
5225
5226/**
5227 * Callback function for unmapping and/or mapping the VRAM MMIO2 region (called by the PCI bus).
5228 *
5229 * @return VBox status code.
5230 * @param pPciDev Pointer to PCI device. Use pPciDev->pDevIns to get the device instance.
5231 * @param iRegion The region number.
5232 * @param GCPhysAddress Physical address of the region. If iType is PCI_ADDRESS_SPACE_IO, this is an
5233 * I/O port, else it's a physical address.
5234 * This address is *NOT* relative to pci_mem_base like earlier!
5235 * @param enmType One of the PCI_ADDRESS_SPACE_* values.
5236 */
5237static DECLCALLBACK(int) vgaR3IORegionMap(PPCIDEVICE pPciDev, /*unsigned*/ int iRegion, RTGCPHYS GCPhysAddress, uint32_t cb, PCIADDRESSSPACE enmType)
5238{
5239 int rc;
5240 PPDMDEVINS pDevIns = pPciDev->pDevIns;
5241 PVGASTATE pThis = PDMINS_2_DATA(pDevIns, PVGASTATE);
5242 LogFlow(("vgaR3IORegionMap: iRegion=%d GCPhysAddress=%RGp cb=%#x enmType=%d\n", iRegion, GCPhysAddress, cb, enmType));
5243 AssertReturn(iRegion == 0 && enmType == PCI_ADDRESS_SPACE_MEM_PREFETCH, VERR_INTERNAL_ERROR);
5244
5245 if (GCPhysAddress != NIL_RTGCPHYS)
5246 {
5247 /*
5248 * Mapping the VRAM.
5249 */
5250 rc = PDMDevHlpMMIO2Map(pDevIns, iRegion, GCPhysAddress);
5251 AssertRC(rc);
5252 if (RT_SUCCESS(rc))
5253 {
5254 rc = PGMR3HandlerPhysicalRegister(PDMDevHlpGetVM(pDevIns),
5255 PGMPHYSHANDLERTYPE_PHYSICAL_WRITE,
5256 GCPhysAddress, GCPhysAddress + (pThis->vram_size - 1),
5257 vgaR3LFBAccessHandler, pThis,
5258 g_DeviceVga.szR0Mod, "vgaR0LFBAccessHandler", pDevIns->pvInstanceDataR0,
5259 g_DeviceVga.szRCMod, "vgaGCLFBAccessHandler", pDevIns->pvInstanceDataRC,
5260 "VGA LFB");
5261 AssertRC(rc);
5262 if (RT_SUCCESS(rc))
5263 pThis->GCPhysVRAM = GCPhysAddress;
5264 }
5265 }
5266 else
5267 {
5268 /*
5269 * Unmapping of the VRAM in progress.
5270 * Deregister the access handler so PGM doesn't get upset.
5271 */
5272 Assert(pThis->GCPhysVRAM);
5273 rc = PGMHandlerPhysicalDeregister(PDMDevHlpGetVM(pDevIns), pThis->GCPhysVRAM);
5274 AssertRC(rc);
5275 pThis->GCPhysVRAM = 0;
5276 }
5277 return rc;
5278}
5279
5280
5281/* -=-=-=-=-=- Ring3: Misc Wrappers -=-=-=-=-=- */
5282
5283/**
5284 * Saves a state of the VGA device.
5285 *
5286 * @returns VBox status code.
5287 * @param pDevIns The device instance.
5288 * @param pSSMHandle The handle to save the state to.
5289 */
5290static DECLCALLBACK(int) vgaR3SaveExec(PPDMDEVINS pDevIns, PSSMHANDLE pSSMHandle)
5291{
5292 vga_save(pSSMHandle, PDMINS_2_DATA(pDevIns, PVGASTATE));
5293 return VINF_SUCCESS;
5294}
5295
5296
5297/**
5298 * Loads a saved VGA device state.
5299 *
5300 * @returns VBox status code.
5301 * @param pDevIns The device instance.
5302 * @param pSSMHandle The handle to the saved state.
5303 * @param u32Version The data unit version number.
5304 */
5305static DECLCALLBACK(int) vgaR3LoadExec(PPDMDEVINS pDevIns, PSSMHANDLE pSSMHandle, uint32_t u32Version)
5306{
5307 if (vga_load(pSSMHandle, PDMINS_2_DATA(pDevIns, PVGASTATE), u32Version))
5308 return VERR_SSM_UNSUPPORTED_DATA_UNIT_VERSION;
5309 return VINF_SUCCESS;
5310}
5311
5312
5313/* -=-=-=-=-=- Ring 3: Device callbacks -=-=-=-=-=- */
5314
5315/**
5316 * Reset notification.
5317 *
5318 * @returns VBox status.
5319 * @param pDevIns The device instance data.
5320 */
5321static DECLCALLBACK(void) vgaR3Reset(PPDMDEVINS pDevIns)
5322{
5323 PVGASTATE pThis = PDMINS_2_DATA(pDevIns, PVGASTATE);
5324 char *pchStart;
5325 char *pchEnd;
5326 LogFlow(("vgaReset\n"));
5327
5328 /* Clear the VRAM ourselves. */
5329 if (pThis->vram_ptrR3 && pThis->vram_size)
5330 {
5331#ifdef LOG_ENABLED /** @todo separate function. */
5332 /* First dump the textmode contents to the log; handy for capturing Windows blue screens. */
5333 uint8_t graphic_mode;
5334 VGAState *s = pThis;
5335
5336 if (!(s->ar_index & 0x20)) {
5337 graphic_mode = GMODE_BLANK;
5338 } else {
5339 graphic_mode = s->gr[6] & 1;
5340 }
5341 switch(graphic_mode)
5342 case GMODE_TEXT:
5343 {
5344 int cw, height, width, cheight, cx_min, cx_max, cy, cx;
5345 int x_incr;
5346 uint8_t *s1, *src, ch, cattr;
5347 int line_offset;
5348 uint16_t ch_attr;
5349
5350 line_offset = s->line_offset;
5351 s1 = s->CTX_SUFF(vram_ptr) + (s->start_addr * 4);
5352
5353 /* total width & height */
5354 cheight = (s->cr[9] & 0x1f) + 1;
5355 cw = 8;
5356 if (!(s->sr[1] & 0x01))
5357 cw = 9;
5358 if (s->sr[1] & 0x08)
5359 cw = 16; /* NOTE: no 18 pixel wide */
5360 x_incr = cw * ((s->pDrv->cBits + 7) >> 3);
5361 width = (s->cr[0x01] + 1);
5362 if (s->cr[0x06] == 100) {
5363 /* ugly hack for CGA 160x100x16 - explain me the logic */
5364 height = 100;
5365 } else {
5366 height = s->cr[0x12] |
5367 ((s->cr[0x07] & 0x02) << 7) |
5368 ((s->cr[0x07] & 0x40) << 3);
5369 height = (height + 1) / cheight;
5370 }
5371 if ((height * width) > CH_ATTR_SIZE) {
5372 /* better than nothing: exit if transient size is too big */
5373 break;
5374 }
5375 RTLogPrintf("VGA textmode BEGIN (%dx%d):\n\n", height, width);
5376 for(cy = 0; cy < height; cy++) {
5377 src = s1;
5378 cx_min = width;
5379 cx_max = -1;
5380 for(cx = 0; cx < width; cx++) {
5381 ch_attr = *(uint16_t *)src;
5382 if (cx < cx_min)
5383 cx_min = cx;
5384 if (cx > cx_max)
5385 cx_max = cx;
5386# ifdef WORDS_BIGENDIAN
5387 ch = ch_attr >> 8;
5388 cattr = ch_attr & 0xff;
5389# else
5390 ch = ch_attr & 0xff;
5391 cattr = ch_attr >> 8;
5392# endif
5393 RTLogPrintf("%c", ch);
5394
5395#ifndef VBOX
5396 src += 4;
5397#else
5398 src += 8; /* Every second byte of a plane is used in text mode. */
5399#endif
5400 }
5401 if (cx_max != -1)
5402 RTLogPrintf("\n");
5403
5404 s1 += line_offset;
5405 }
5406 RTLogPrintf("VGA textmode END:\n\n");
5407 }
5408
5409#endif /* LOG_ENABLED */
5410 memset(pThis->vram_ptrR3, 0, pThis->vram_size);
5411 }
5412
5413 /*
5414 * Zero most of it.
5415 *
5416 * Unlike vga_reset we're leaving out a few members which we believe
5417 * must remain unchanged....
5418 */
5419 /* 1st part. */
5420 pchStart = (char *)&pThis->latch;
5421 pchEnd = (char *)&pThis->invalidated_y_table;
5422 memset(pchStart, 0, pchEnd - pchStart);
5423
5424 /* 2nd part. */
5425 pchStart = (char *)&pThis->last_palette;
5426 pchEnd = (char *)&pThis->u32Marker;
5427 memset(pchStart, 0, pchEnd - pchStart);
5428
5429
5430 /*
5431 * Restore and re-init some bits.
5432 */
5433 pThis->get_bpp = vga_get_bpp;
5434 pThis->get_offsets = vga_get_offsets;
5435 pThis->get_resolution = vga_get_resolution;
5436 pThis->graphic_mode = -1; /* Force full update. */
5437#ifdef CONFIG_BOCHS_VBE
5438 pThis->vbe_regs[VBE_DISPI_INDEX_ID] = VBE_DISPI_ID0;
5439 pThis->vbe_regs[VBE_DISPI_INDEX_VBOX_VIDEO] = 0;
5440 pThis->vbe_bank_max = pThis->vram_size >> 16;
5441#endif /* CONFIG_BOCHS_VBE */
5442
5443 /*
5444 * Reset the LBF mapping.
5445 */
5446 pThis->fLFBUpdated = false;
5447 if ( ( pThis->fGCEnabled
5448 || pThis->fR0Enabled)
5449 && pThis->GCPhysVRAM
5450 && pThis->GCPhysVRAM != NIL_RTGCPHYS32)
5451 {
5452 int rc = PGMHandlerPhysicalReset(PDMDevHlpGetVM(pDevIns), pThis->GCPhysVRAM);
5453 AssertRC(rc);
5454 }
5455 if (pThis->fRemappedVGA)
5456 {
5457 IOMMMIOResetRegion(PDMDevHlpGetVM(pDevIns), 0x000a0000);
5458 pThis->fRemappedVGA = false;
5459 }
5460
5461 /*
5462 * Reset the logo data.
5463 */
5464 pThis->LogoCommand = LOGO_CMD_NOP;
5465 pThis->offLogoData = 0;
5466
5467 /* notify port handler */
5468 if (pThis->pDrv)
5469 pThis->pDrv->pfnReset(pThis->pDrv);
5470
5471 /* Reset latched access mask. */
5472 pThis->uMaskLatchAccess = 0x3ff;
5473 pThis->cLatchAccesses = 0;
5474 pThis->u64LastLatchedAccess = 0;
5475 pThis->iMask = 0;
5476}
5477
5478
5479/**
5480 * Device relocation callback.
5481 *
5482 * @param pDevIns Pointer to the device instance.
5483 * @param offDelta The relocation delta relative to the old location.
5484 *
5485 * @see FNPDMDEVRELOCATE for details.
5486 */
5487static DECLCALLBACK(void) vgaR3Relocate(PPDMDEVINS pDevIns, RTGCINTPTR offDelta)
5488{
5489 if (offDelta)
5490 {
5491 PVGASTATE pThis = PDMINS_2_DATA(pDevIns, PVGASTATE);
5492 LogFlow(("vgaRelocate: offDelta = %08X\n", offDelta));
5493
5494 pThis->RCPtrLFBHandler += offDelta;
5495 pThis->vram_ptrRC += offDelta;
5496 pThis->pDevInsRC = PDMDEVINS_2_RCPTR(pDevIns);
5497 }
5498}
5499
5500
5501/**
5502 * Attach command.
5503 *
5504 * This is called to let the device attach to a driver for a specified LUN
5505 * during runtime. This is not called during VM construction, the device
5506 * constructor have to attach to all the available drivers.
5507 *
5508 * This is like plugging in the monitor after turning on the PC.
5509 *
5510 * @returns VBox status code.
5511 * @param pDevIns The device instance.
5512 * @param iLUN The logical unit which is being detached.
5513 * @param fFlags Flags, combination of the PDMDEVATT_FLAGS_* \#defines.
5514 */
5515static DECLCALLBACK(int) vgaAttach(PPDMDEVINS pDevIns, unsigned iLUN, uint32_t fFlags)
5516{
5517 PVGASTATE pThis = PDMINS_2_DATA(pDevIns, PVGASTATE);
5518
5519 AssertMsgReturn(fFlags & PDMDEVATT_FLAGS_NOT_HOT_PLUG,
5520 ("VGA device does not support hotplugging\n"),
5521 VERR_INVALID_PARAMETER);
5522
5523 switch (iLUN)
5524 {
5525 /* LUN #0: Display port. */
5526 case 0:
5527 {
5528 int rc = PDMDevHlpDriverAttach(pDevIns, iLUN, &pThis->Base, &pThis->pDrvBase, "Display Port");
5529 if (RT_SUCCESS(rc))
5530 {
5531 pThis->pDrv = (PDMIDISPLAYCONNECTOR*)pThis->pDrvBase->pfnQueryInterface(pThis->pDrvBase, PDMINTERFACE_DISPLAY_CONNECTOR);
5532 if (pThis->pDrv)
5533 {
5534 /* pThis->pDrv->pu8Data can be NULL when there is no framebuffer. */
5535 if ( pThis->pDrv->pfnRefresh
5536 && pThis->pDrv->pfnResize
5537 && pThis->pDrv->pfnUpdateRect)
5538 rc = VINF_SUCCESS;
5539 else
5540 {
5541 Assert(pThis->pDrv->pfnRefresh);
5542 Assert(pThis->pDrv->pfnResize);
5543 Assert(pThis->pDrv->pfnUpdateRect);
5544 pThis->pDrv = NULL;
5545 pThis->pDrvBase = NULL;
5546 rc = VERR_INTERNAL_ERROR;
5547 }
5548 }
5549 else
5550 {
5551 AssertMsgFailed(("LUN #0 doesn't have a display connector interface! rc=%Rrc\n", rc));
5552 pThis->pDrvBase = NULL;
5553 rc = VERR_PDM_MISSING_INTERFACE;
5554 }
5555 }
5556 else if (rc == VERR_PDM_NO_ATTACHED_DRIVER)
5557 {
5558 Log(("%s/%d: warning: no driver attached to LUN #0!\n", pDevIns->pDevReg->szDeviceName, pDevIns->iInstance));
5559 rc = VINF_SUCCESS;
5560 }
5561 else
5562 AssertLogRelMsgFailed(("Failed to attach LUN #0! rc=%Rrc\n", rc));
5563 return rc;
5564 }
5565
5566 default:
5567 AssertMsgFailed(("Invalid LUN #%d\n", iLUN));
5568 return VERR_PDM_NO_SUCH_LUN;
5569 }
5570}
5571
5572
5573/**
5574 * Detach notification.
5575 *
5576 * This is called when a driver is detaching itself from a LUN of the device.
5577 * The device should adjust it's state to reflect this.
5578 *
5579 * This is like unplugging the monitor while the PC is still running.
5580 *
5581 * @param pDevIns The device instance.
5582 * @param iLUN The logical unit which is being detached.
5583 * @param fFlags Flags, combination of the PDMDEVATT_FLAGS_* \#defines.
5584 */
5585static DECLCALLBACK(void) vgaDetach(PPDMDEVINS pDevIns, unsigned iLUN, uint32_t fFlags)
5586{
5587 /*
5588 * Reset the interfaces and update the controller state.
5589 */
5590 PVGASTATE pThis = PDMINS_2_DATA(pDevIns, PVGASTATE);
5591
5592 AssertMsg(fFlags & PDMDEVATT_FLAGS_NOT_HOT_PLUG,
5593 ("VGA device does not support hotplugging\n"));
5594
5595 switch (iLUN)
5596 {
5597 /* LUN #0: Display port. */
5598 case 0:
5599 pThis->pDrv = NULL;
5600 pThis->pDrvBase = NULL;
5601 break;
5602
5603 default:
5604 AssertMsgFailed(("Invalid LUN #%d\n", iLUN));
5605 break;
5606 }
5607}
5608
5609
5610
5611/**
5612 * Construct a VGA device instance for a VM.
5613 *
5614 * @returns VBox status.
5615 * @param pDevIns The device instance data.
5616 * If the registration structure is needed, pDevIns->pDevReg points to it.
5617 * @param iInstance Instance number. Use this to figure out which registers and such to use.
5618 * The device number is also found in pDevIns->iInstance, but since it's
5619 * likely to be freqently used PDM passes it as parameter.
5620 * @param pCfgHandle Configuration node handle for the device. Use this to obtain the configuration
5621 * of the device instance. It's also found in pDevIns->pCfgHandle, but like
5622 * iInstance it's expected to be used a bit in this function.
5623 */
5624static DECLCALLBACK(int) vgaR3Construct(PPDMDEVINS pDevIns, int iInstance, PCFGMNODE pCfgHandle)
5625{
5626 static bool s_fExpandDone = false;
5627 int rc;
5628 unsigned i;
5629 PVGASTATE pThis = PDMINS_2_DATA(pDevIns, PVGASTATE);
5630 PVM pVM = PDMDevHlpGetVM(pDevIns);
5631#ifdef VBE_NEW_DYN_LIST
5632 uint32_t cCustomModes;
5633 uint32_t cyReduction;
5634 PVBEHEADER pVBEDataHdr;
5635 ModeInfoListItem *pCurMode;
5636 unsigned cb;
5637#endif
5638 Assert(iInstance == 0);
5639 Assert(pVM);
5640
5641 /*
5642 * Init static data.
5643 */
5644 if (!s_fExpandDone)
5645 {
5646 s_fExpandDone = true;
5647 vga_init_expand();
5648 }
5649
5650 /*
5651 * Validate configuration.
5652 */
5653 if (!CFGMR3AreValuesValid(pCfgHandle, "VRamSize\0"
5654 "GCEnabled\0"
5655 "R0Enabled\0"
5656 "FadeIn\0"
5657 "FadeOut\0"
5658 "LogoTime\0"
5659 "LogoFile\0"
5660 "ShowBootMenu\0"
5661 "CustomVideoModes\0"
5662 "HeightReduction\0"
5663 "CustomVideoMode1\0"
5664 "CustomVideoMode2\0"
5665 "CustomVideoMode3\0"
5666 "CustomVideoMode4\0"
5667 "CustomVideoMode5\0"
5668 "CustomVideoMode6\0"
5669 "CustomVideoMode7\0"
5670 "CustomVideoMode8\0"
5671 "CustomVideoMode9\0"
5672 "CustomVideoMode10\0"
5673 "CustomVideoMode11\0"
5674 "CustomVideoMode12\0"
5675 "CustomVideoMode13\0"
5676 "CustomVideoMode14\0"
5677 "CustomVideoMode15\0"
5678 "CustomVideoMode16\0"))
5679 return PDMDEV_SET_ERROR(pDevIns, VERR_PDM_DEVINS_UNKNOWN_CFG_VALUES,
5680 N_("Invalid configuration for vga device"));
5681
5682 /*
5683 * Init state data.
5684 */
5685 rc = CFGMR3QueryU32Def(pCfgHandle, "VRamSize", &pThis->vram_size, VGA_VRAM_DEFAULT);
5686 AssertLogRelRCReturn(rc, rc);
5687 if (pThis->vram_size > VGA_VRAM_MAX)
5688 return PDMDevHlpVMSetError(pDevIns, VERR_INVALID_PARAMETER, RT_SRC_POS,
5689 "VRamSize is too large, %#x, max %#x", pThis->vram_size, VGA_VRAM_MAX);
5690 if (pThis->vram_size < VGA_VRAM_MIN)
5691 return PDMDevHlpVMSetError(pDevIns, VERR_INVALID_PARAMETER, RT_SRC_POS,
5692 "VRamSize is too small, %#x, max %#x", pThis->vram_size, VGA_VRAM_MIN);
5693
5694 rc = CFGMR3QueryBoolDef(pCfgHandle, "GCEnabled", &pThis->fGCEnabled, true);
5695 AssertLogRelRCReturn(rc, rc);
5696
5697 rc = CFGMR3QueryBoolDef(pCfgHandle, "R0Enabled", &pThis->fR0Enabled, true);
5698 AssertLogRelRCReturn(rc, rc);
5699 Log(("VGA: VRamSize=%#x fGCenabled=%RTbool fR0Enabled=%RTbool\n", pThis->vram_size, pThis->fGCEnabled, pThis->fR0Enabled));
5700
5701 pThis->pDevInsR3 = pDevIns;
5702 pThis->pDevInsR0 = PDMDEVINS_2_R0PTR(pDevIns);
5703 pThis->pDevInsRC = PDMDEVINS_2_RCPTR(pDevIns);
5704
5705 vgaR3Reset(pDevIns);
5706
5707 /* The PCI devices configuration. */
5708 PCIDevSetVendorId( &pThis->Dev, 0x80ee); /* PCI vendor, just a free bogus value */
5709 PCIDevSetDeviceId( &pThis->Dev, 0xbeef);
5710 PCIDevSetClassSub( &pThis->Dev, 0x00); /* VGA controller */
5711 PCIDevSetClassBase( &pThis->Dev, 0x03);
5712 PCIDevSetHeaderType(&pThis->Dev, 0x00);
5713#if defined(VBOX_WITH_HGSMI) && defined(VBOX_WITH_VIDEOHWACCEL)
5714 PCIDevSetInterruptPin(&pThis->Dev, 1);
5715#endif
5716
5717 /* The LBF access handler - error handling is better here than in the map function. */
5718 rc = PDMR3LdrGetSymbolRCLazy(pVM, pDevIns->pDevReg->szRCMod, "vgaGCLFBAccessHandler", &pThis->RCPtrLFBHandler);
5719 if (RT_FAILURE(rc))
5720 {
5721 AssertReleaseMsgFailed(("PDMR3LdrGetSymbolRC(, %s, \"vgaGCLFBAccessHandler\",) -> %Rrc\n", pDevIns->pDevReg->szRCMod, rc));
5722 return rc;
5723 }
5724
5725 /* the interfaces. */
5726 pThis->Base.pfnQueryInterface = vgaPortQueryInterface;
5727
5728 pThis->Port.pfnUpdateDisplay = vgaPortUpdateDisplay;
5729 pThis->Port.pfnUpdateDisplayAll = vgaPortUpdateDisplayAll;
5730 pThis->Port.pfnQueryColorDepth = vgaPortQueryColorDepth;
5731 pThis->Port.pfnSetRefreshRate = vgaPortSetRefreshRate;
5732 pThis->Port.pfnSnapshot = vgaPortSnapshot;
5733 pThis->Port.pfnDisplayBlt = vgaPortDisplayBlt;
5734 pThis->Port.pfnUpdateDisplayRect = vgaPortUpdateDisplayRect;
5735 pThis->Port.pfnSetRenderVRAM = vgaPortSetRenderVRAM;
5736
5737#if defined(VBOX_WITH_HGSMI) && defined(VBOX_WITH_VIDEOHWACCEL)
5738 pThis->VBVACallbacks.pfnVHWACommandCompleteAsynch = vbvaVHWACommandCompleteAsynch;
5739#endif
5740
5741 /*
5742 * Allocate the VRAM and map the first 512KB of it into GC so we can speed up VGA support.
5743 */
5744 rc = PDMDevHlpMMIO2Register(pDevIns, 0 /* iRegion */, pThis->vram_size, 0, (void **)&pThis->vram_ptrR3, "VRam");
5745 AssertLogRelMsgRCReturn(rc, ("PDMDevHlpMMIO2Register(%#x,) -> %Rrc\n", pThis->vram_size, rc), rc);
5746 pThis->vram_ptrR0 = (RTR0PTR)pThis->vram_ptrR3; /** @todo #1865 Map parts into R0 or just use PGM access (Mac only). */
5747
5748 if (pThis->fGCEnabled)
5749 {
5750 RTRCPTR pRCMapping = 0;
5751 rc = PDMDevHlpMMHyperMapMMIO2(pDevIns, 0 /* iRegion */, 0 /* off */, VGA_MAPPING_SIZE, "VGA VRam", &pRCMapping);
5752 AssertLogRelMsgRCReturn(rc, ("PDMDevHlpMMHyperMapMMIO2(%#x,) -> %Rrc\n", VGA_MAPPING_SIZE, rc), rc);
5753 pThis->vram_ptrRC = pRCMapping;
5754 }
5755
5756#if defined(VBOX_WITH_2X_4GB_ADDR_SPACE)
5757 if (pThis->fR0Enabled)
5758 {
5759 RTR0PTR pR0Mapping = 0;
5760 rc = PDMDevHlpMMIO2MapKernel(pDevIns, 0 /* iRegion */, 0 /* off */, VGA_MAPPING_SIZE, "VGA VRam", &pR0Mapping);
5761 AssertLogRelMsgRCReturn(rc, ("PDMDevHlpMapMMIO2IntoR0(%#x,) -> %Rrc\n", VGA_MAPPING_SIZE, rc), rc);
5762 pThis->vram_ptrR0 = pR0Mapping;
5763 }
5764#endif
5765
5766 /*
5767 * Register I/O ports, ROM and save state.
5768 */
5769 rc = PDMDevHlpIOPortRegister(pDevIns, 0x3c0, 16, NULL, vgaIOPortWrite, vgaIOPortRead, NULL, NULL, "VGA - 3c0");
5770 if (RT_FAILURE(rc))
5771 return rc;
5772 rc = PDMDevHlpIOPortRegister(pDevIns, 0x3b4, 2, NULL, vgaIOPortWrite, vgaIOPortRead, NULL, NULL, "VGA - 3b4");
5773 if (RT_FAILURE(rc))
5774 return rc;
5775 rc = PDMDevHlpIOPortRegister(pDevIns, 0x3ba, 1, NULL, vgaIOPortWrite, vgaIOPortRead, NULL, NULL, "VGA - 3ba");
5776 if (RT_FAILURE(rc))
5777 return rc;
5778 rc = PDMDevHlpIOPortRegister(pDevIns, 0x3d4, 2, NULL, vgaIOPortWrite, vgaIOPortRead, NULL, NULL, "VGA - 3d4");
5779 if (RT_FAILURE(rc))
5780 return rc;
5781 rc = PDMDevHlpIOPortRegister(pDevIns, 0x3da, 1, NULL, vgaIOPortWrite, vgaIOPortRead, NULL, NULL, "VGA - 3da");
5782 if (RT_FAILURE(rc))
5783 return rc;
5784
5785#ifdef CONFIG_BOCHS_VBE
5786 rc = PDMDevHlpIOPortRegister(pDevIns, 0x1ce, 1, NULL, vgaIOPortWriteVBEIndex, vgaIOPortReadVBEIndex, NULL, NULL, "VGA/VBE - Index");
5787 if (RT_FAILURE(rc))
5788 return rc;
5789 rc = PDMDevHlpIOPortRegister(pDevIns, 0x1cf, 1, NULL, vgaIOPortWriteVBEData, vgaIOPortReadVBEData, NULL, NULL, "VGA/VBE - Data");
5790 if (RT_FAILURE(rc))
5791 return rc;
5792#if 0
5793 /* This now causes conflicts with Win2k & XP; it is not aware this range is taken
5794 and tries to map other devices there */
5795 /* Old Bochs. */
5796 rc = PDMDevHlpIOPortRegister(pDevIns, 0xff80, 1, NULL, vgaIOPortWriteVBEIndex, vgaIOPortReadVBEIndex, "VGA/VBE - Index Old");
5797 if (RT_FAILURE(rc))
5798 return rc;
5799 rc = PDMDevHlpIOPortRegister(pDevIns, 0xff81, 1, NULL, vgaIOPortWriteVBEData, vgaIOPortReadVBEData, "VGA/VBE - Data Old");
5800 if (RT_FAILURE(rc))
5801 return rc;
5802#endif
5803#endif /* CONFIG_BOCHS_VBE */
5804
5805 /* guest context extension */
5806 if (pThis->fGCEnabled)
5807 {
5808 rc = PDMDevHlpIOPortRegisterGC(pDevIns, 0x3c0, 16, 0, "vgaIOPortWrite", "vgaIOPortRead", NULL, NULL, "VGA - 3c0 (GC)");
5809 if (RT_FAILURE(rc))
5810 return rc;
5811 rc = PDMDevHlpIOPortRegisterGC(pDevIns, 0x3b4, 2, 0, "vgaIOPortWrite", "vgaIOPortRead", NULL, NULL, "VGA - 3b4 (GC)");
5812 if (RT_FAILURE(rc))
5813 return rc;
5814 rc = PDMDevHlpIOPortRegisterGC(pDevIns, 0x3ba, 1, 0, "vgaIOPortWrite", "vgaIOPortRead", NULL, NULL, "VGA - 3ba (GC)");
5815 if (RT_FAILURE(rc))
5816 return rc;
5817 rc = PDMDevHlpIOPortRegisterGC(pDevIns, 0x3d4, 2, 0, "vgaIOPortWrite", "vgaIOPortRead", NULL, NULL, "VGA - 3d4 (GC)");
5818 if (RT_FAILURE(rc))
5819 return rc;
5820 rc = PDMDevHlpIOPortRegisterGC(pDevIns, 0x3da, 1, 0, "vgaIOPortWrite", "vgaIOPortRead", NULL, NULL, "VGA - 3da (GC)");
5821 if (RT_FAILURE(rc))
5822 return rc;
5823#ifdef CONFIG_BOCHS_VBE
5824 rc = PDMDevHlpIOPortRegisterGC(pDevIns, 0x1ce, 1, 0, "vgaIOPortWriteVBEIndex", "vgaIOPortReadVBEIndex", NULL, NULL, "VGA/VBE - Index (GC)");
5825 if (RT_FAILURE(rc))
5826 return rc;
5827 rc = PDMDevHlpIOPortRegisterGC(pDevIns, 0x1cf, 1, 0, "vgaIOPortWriteVBEData", "vgaIOPortReadVBEData", NULL, NULL, "VGA/VBE - Data (GC)");
5828 if (RT_FAILURE(rc))
5829 return rc;
5830
5831#if 0
5832 /* This now causes conflicts with Win2k & XP; they are not aware this range is taken
5833 and try to map other devices there */
5834 /* Old Bochs. */
5835 rc = PDMDevHlpIOPortRegisterGC(pDevIns, 0xff80, 1, 0, "vgaIOPortWriteVBEIndex", "vgaIOPortReadVBEIndex", "VGA/VBE - Index Old (GC)");
5836 if (RT_FAILURE(rc))
5837 return rc;
5838 rc = PDMDevHlpIOPortRegisterGC(pDevIns, 0xff81, 1, 0, "vgaIOPortWriteVBEData", "vgaIOPortReadVBEData", "VGA/VBE - Index Old (GC)");
5839 if (RT_FAILURE(rc))
5840 return rc;
5841#endif
5842
5843#endif /* CONFIG_BOCHS_VBE */
5844 }
5845
5846 /* R0 context extension */
5847 if (pThis->fR0Enabled)
5848 {
5849 rc = PDMDevHlpIOPortRegisterR0(pDevIns, 0x3c0, 16, 0, "vgaIOPortWrite", "vgaIOPortRead", NULL, NULL, "VGA - 3c0 (GC)");
5850 if (RT_FAILURE(rc))
5851 return rc;
5852 rc = PDMDevHlpIOPortRegisterR0(pDevIns, 0x3b4, 2, 0, "vgaIOPortWrite", "vgaIOPortRead", NULL, NULL, "VGA - 3b4 (GC)");
5853 if (RT_FAILURE(rc))
5854 return rc;
5855 rc = PDMDevHlpIOPortRegisterR0(pDevIns, 0x3ba, 1, 0, "vgaIOPortWrite", "vgaIOPortRead", NULL, NULL, "VGA - 3ba (GC)");
5856 if (RT_FAILURE(rc))
5857 return rc;
5858 rc = PDMDevHlpIOPortRegisterR0(pDevIns, 0x3d4, 2, 0, "vgaIOPortWrite", "vgaIOPortRead", NULL, NULL, "VGA - 3d4 (GC)");
5859 if (RT_FAILURE(rc))
5860 return rc;
5861 rc = PDMDevHlpIOPortRegisterR0(pDevIns, 0x3da, 1, 0, "vgaIOPortWrite", "vgaIOPortRead", NULL, NULL, "VGA - 3da (GC)");
5862 if (RT_FAILURE(rc))
5863 return rc;
5864#ifdef CONFIG_BOCHS_VBE
5865 rc = PDMDevHlpIOPortRegisterR0(pDevIns, 0x1ce, 1, 0, "vgaIOPortWriteVBEIndex", "vgaIOPortReadVBEIndex", NULL, NULL, "VGA/VBE - Index (GC)");
5866 if (RT_FAILURE(rc))
5867 return rc;
5868 rc = PDMDevHlpIOPortRegisterR0(pDevIns, 0x1cf, 1, 0, "vgaIOPortWriteVBEData", "vgaIOPortReadVBEData", NULL, NULL, "VGA/VBE - Data (GC)");
5869 if (RT_FAILURE(rc))
5870 return rc;
5871
5872#if 0
5873 /* This now causes conflicts with Win2k & XP; they are not aware this range is taken
5874 and try to map other devices there */
5875 /* Old Bochs. */
5876 rc = PDMDevHlpIOPortRegisterR0(pDevIns, 0xff80, 1, 0, "vgaIOPortWriteVBEIndex", "vgaIOPortReadVBEIndex", "VGA/VBE - Index Old (GC)");
5877 if (RT_FAILURE(rc))
5878 return rc;
5879 rc = PDMDevHlpIOPortRegisterR0(pDevIns, 0xff81, 1, 0, "vgaIOPortWriteVBEData", "vgaIOPortReadVBEData", "VGA/VBE - Index Old (GC)");
5880 if (RT_FAILURE(rc))
5881 return rc;
5882#endif
5883
5884#endif /* CONFIG_BOCHS_VBE */
5885 }
5886
5887 /* vga mmio */
5888 rc = PDMDevHlpMMIORegister(pDevIns, 0x000a0000, 0x00020000, 0, vgaMMIOWrite, vgaMMIORead, vgaMMIOFill, "VGA - VGA Video Buffer");
5889 if (RT_FAILURE(rc))
5890 return rc;
5891 if (pThis->fGCEnabled)
5892 {
5893 rc = PDMDevHlpMMIORegisterGC(pDevIns, 0x000a0000, 0x00020000, 0, "vgaMMIOWrite", "vgaMMIORead", "vgaMMIOFill");
5894 if (RT_FAILURE(rc))
5895 return rc;
5896 }
5897 if (pThis->fR0Enabled)
5898 {
5899 rc = PDMDevHlpMMIORegisterR0(pDevIns, 0x000a0000, 0x00020000, 0, "vgaMMIOWrite", "vgaMMIORead", "vgaMMIOFill");
5900 if (RT_FAILURE(rc))
5901 return rc;
5902 }
5903
5904 /* vga bios */
5905 rc = PDMDevHlpIOPortRegister(pDevIns, VBE_PRINTF_PORT, 1, NULL, vgaIOPortWriteBIOS, vgaIOPortReadBIOS, NULL, NULL, "VGA BIOS debug/panic");
5906 if (RT_FAILURE(rc))
5907 return rc;
5908 if (pThis->fR0Enabled)
5909 {
5910 rc = PDMDevHlpIOPortRegisterR0(pDevIns, VBE_PRINTF_PORT, 1, 0, "vgaIOPortWriteBIOS", "vgaIOPortReadBIOS", NULL, NULL, "VGA BIOS debug/panic");
5911 if (RT_FAILURE(rc))
5912 return rc;
5913 }
5914
5915 AssertReleaseMsg(g_cbVgaBiosBinary <= _64K && g_cbVgaBiosBinary >= 32*_1K, ("g_cbVgaBiosBinary=%#x\n", g_cbVgaBiosBinary));
5916 AssertReleaseMsg(RT_ALIGN_Z(g_cbVgaBiosBinary, PAGE_SIZE) == g_cbVgaBiosBinary, ("g_cbVgaBiosBinary=%#x\n", g_cbVgaBiosBinary));
5917 rc = PDMDevHlpROMRegister(pDevIns, 0x000c0000, g_cbVgaBiosBinary, &g_abVgaBiosBinary[0],
5918 PGMPHYS_ROM_FLAGS_PERMANENT_BINARY, "VGA BIOS");
5919 if (RT_FAILURE(rc))
5920 return rc;
5921
5922 /* save */
5923 rc = PDMDevHlpSSMRegister(pDevIns, pDevIns->pDevReg->szDeviceName, iInstance, VGA_SAVEDSTATE_VERSION,
5924 sizeof(*pThis), NULL, vgaR3SaveExec, NULL, NULL, vgaR3LoadExec, NULL);
5925 if (RT_FAILURE(rc))
5926 return rc;
5927
5928 /* PCI */
5929 rc = PDMDevHlpPCIRegister(pDevIns, &pThis->Dev);
5930 if (RT_FAILURE(rc))
5931 return rc;
5932 /*AssertMsg(pThis->Dev.devfn == 16 || iInstance != 0, ("pThis->Dev.devfn=%d\n", pThis->Dev.devfn));*/
5933 if (pThis->Dev.devfn != 16 && iInstance == 0)
5934 Log(("!!WARNING!!: pThis->dev.devfn=%d (ignore if testcase or not started by Main)\n", pThis->Dev.devfn));
5935
5936 rc = PDMDevHlpPCIIORegionRegister(pDevIns, 0 /* iRegion */, pThis->vram_size, PCI_ADDRESS_SPACE_MEM_PREFETCH, vgaR3IORegionMap);
5937 if (RT_FAILURE(rc))
5938 return rc;
5939
5940 /* Initialize the PDM lock. */
5941 rc = PDMDevHlpCritSectInit(pDevIns, &pThis->lock, "VGA");
5942 if (RT_FAILURE(rc))
5943 {
5944 Log(("%s: Failed to create critical section.\n", __FUNCTION__));
5945 return rc;
5946 }
5947
5948 /*
5949 * Create the refresh timer.
5950 */
5951 rc = PDMDevHlpTMTimerCreate(pDevIns, TMCLOCK_REAL, vgaTimerRefresh,
5952 pThis, TMTIMER_FLAGS_DEFAULT_CRIT_SECT, /** @todo This needs to be fixed! We cannot take the I/O lock at this point! */
5953 "VGA Refresh Timer", &pThis->RefreshTimer);
5954 if (RT_FAILURE(rc))
5955 return rc;
5956
5957 /*
5958 * Attach to the display.
5959 */
5960 rc = vgaAttach(pDevIns, 0 /* display LUN # */, PDMDEVATT_FLAGS_NOT_HOT_PLUG);
5961 if (RT_FAILURE(rc))
5962 return rc;
5963
5964#ifdef VBE_NEW_DYN_LIST
5965 /*
5966 * Compute buffer size for the VBE BIOS Extra Data.
5967 */
5968 cb = sizeof(mode_info_list) + sizeof(ModeInfoListItem);
5969
5970 rc = CFGMR3QueryU32(pCfgHandle, "HeightReduction", &cyReduction);
5971 if (RT_SUCCESS(rc) && cyReduction)
5972 cb *= 2; /* Default mode list will be twice long */
5973 else
5974 cyReduction = 0;
5975
5976 rc = CFGMR3QueryU32(pCfgHandle, "CustomVideoModes", &cCustomModes);
5977 if (RT_SUCCESS(rc) && cCustomModes)
5978 cb += sizeof(ModeInfoListItem) * cCustomModes;
5979 else
5980 cCustomModes = 0;
5981
5982 /*
5983 * Allocate and initialize buffer for the VBE BIOS Extra Data.
5984 */
5985 AssertRelease(sizeof(VBEHEADER) + cb < 65536);
5986 pThis->cbVBEExtraData = (uint16_t)(sizeof(VBEHEADER) + cb);
5987 pThis->pu8VBEExtraData = (uint8_t *)PDMDevHlpMMHeapAllocZ(pDevIns, pThis->cbVBEExtraData);
5988 if (!pThis->pu8VBEExtraData)
5989 return VERR_NO_MEMORY;
5990
5991 pVBEDataHdr = (PVBEHEADER)pThis->pu8VBEExtraData;
5992 pVBEDataHdr->u16Signature = VBEHEADER_MAGIC;
5993 pVBEDataHdr->cbData = cb;
5994
5995# ifndef VRAM_SIZE_FIX
5996 pCurMode = memcpy(pVBEDataHdr + 1, &mode_info_list, sizeof(mode_info_list));
5997 pCurMode = (ModeInfoListItem *)((uintptr_t)pCurMode + sizeof(mode_info_list));
5998# else /* VRAM_SIZE_FIX defined */
5999 pCurMode = (ModeInfoListItem *)(pVBEDataHdr + 1);
6000 for (i = 0; i < MODE_INFO_SIZE; i++)
6001 {
6002 uint32_t pixelWidth, reqSize;
6003 if (mode_info_list[i].info.MemoryModel == VBE_MEMORYMODEL_TEXT_MODE)
6004 pixelWidth = 2;
6005 else
6006 pixelWidth = (mode_info_list[i].info.BitsPerPixel +7) / 8;
6007 reqSize = mode_info_list[i].info.XResolution
6008 * mode_info_list[i].info.YResolution
6009 * pixelWidth;
6010 if (reqSize >= pThis->vram_size)
6011 continue;
6012 *pCurMode = mode_info_list[i];
6013 pCurMode++;
6014 }
6015# endif /* VRAM_SIZE_FIX defined */
6016
6017 /*
6018 * Copy default modes with subtractred YResolution.
6019 */
6020 if (cyReduction)
6021 {
6022 ModeInfoListItem *pDefMode = mode_info_list;
6023 Log(("vgaR3Construct: cyReduction=%u\n", cyReduction));
6024# ifndef VRAM_SIZE_FIX
6025 for (i = 0; i < MODE_INFO_SIZE; i++, pCurMode++, pDefMode++)
6026 {
6027 *pCurMode = *pDefMode;
6028 pCurMode->mode += 0x30;
6029 pCurMode->info.YResolution -= cyReduction;
6030 }
6031# else /* VRAM_SIZE_FIX defined */
6032 for (i = 0; i < MODE_INFO_SIZE; i++, pDefMode++)
6033 {
6034 uint32_t pixelWidth, reqSize;
6035 if (pDefMode->info.MemoryModel == VBE_MEMORYMODEL_TEXT_MODE)
6036 pixelWidth = 2;
6037 else
6038 pixelWidth = (pDefMode->info.BitsPerPixel + 7) / 8;
6039 reqSize = pDefMode->info.XResolution * pDefMode->info.YResolution * pixelWidth;
6040 if (reqSize >= pThis->vram_size)
6041 continue;
6042 *pCurMode = *pDefMode;
6043 pCurMode->mode += 0x30;
6044 pCurMode->info.YResolution -= cyReduction;
6045 pCurMode++;
6046 }
6047# endif /* VRAM_SIZE_FIX defined */
6048 }
6049
6050
6051 /*
6052 * Add custom modes.
6053 */
6054 if (cCustomModes)
6055 {
6056 uint16_t u16CurMode = 0x160;
6057 for (i = 1; i <= cCustomModes; i++)
6058 {
6059 char szExtraDataKey[sizeof("CustomVideoModeXX")];
6060 char *pszExtraData = NULL;
6061
6062 /* query and decode the custom mode string. */
6063 RTStrPrintf(szExtraDataKey, sizeof(szExtraDataKey), "CustomVideoMode%d", i);
6064 rc = CFGMR3QueryStringAlloc(pCfgHandle, szExtraDataKey, &pszExtraData);
6065 if (RT_SUCCESS(rc))
6066 {
6067 ModeInfoListItem *pDefMode = mode_info_list;
6068 unsigned int cx, cy, cBits, cParams, j;
6069 uint16_t u16DefMode;
6070
6071 cParams = sscanf(pszExtraData, "%ux%ux%u", &cx, &cy, &cBits);
6072 if ( cParams != 3
6073 || (cBits != 16 && cBits != 24 && cBits != 32))
6074 {
6075 AssertMsgFailed(("Configuration error: Invalid mode data '%s' for '%s'! cBits=%d\n", pszExtraData, szExtraDataKey, cBits));
6076 return VERR_VGA_INVALID_CUSTOM_MODE;
6077 }
6078 /* Round up the X resolution to a multiple of eight. */
6079 cx = (cx + 7) & ~7;
6080# ifdef VRAM_SIZE_FIX
6081 if (cx * cy * cBits / 8 >= pThis->vram_size)
6082 {
6083 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",
6084 cx, cy, cBits, pThis->vram_size / _1M));
6085 return VERR_VGA_INVALID_CUSTOM_MODE;
6086 }
6087# endif /* VRAM_SIZE_FIX defined */
6088 MMR3HeapFree(pszExtraData);
6089
6090 /* Use defaults from max@bpp mode. */
6091 switch (cBits)
6092 {
6093 case 16:
6094 u16DefMode = VBE_VESA_MODE_1024X768X565;
6095 break;
6096
6097 case 24:
6098 u16DefMode = VBE_VESA_MODE_1024X768X888;
6099 break;
6100
6101 case 32:
6102 u16DefMode = VBE_OWN_MODE_1024X768X8888;
6103 break;
6104
6105 default: /* gcc, shut up! */
6106 AssertMsgFailed(("gone postal!\n"));
6107 continue;
6108 }
6109
6110 /* mode_info_list is not terminated */
6111 for (j = 0; j < MODE_INFO_SIZE && pDefMode->mode != u16DefMode; j++)
6112 pDefMode++;
6113 Assert(j < MODE_INFO_SIZE);
6114
6115 *pCurMode = *pDefMode;
6116 pCurMode->mode = u16CurMode++;
6117
6118 /* adjust defaults */
6119 pCurMode->info.XResolution = cx;
6120 pCurMode->info.YResolution = cy;
6121
6122 switch (cBits)
6123 {
6124 case 16:
6125 pCurMode->info.BytesPerScanLine = cx * 2;
6126 pCurMode->info.LinBytesPerScanLine = cx * 2;
6127 break;
6128
6129 case 24:
6130 pCurMode->info.BytesPerScanLine = cx * 3;
6131 pCurMode->info.LinBytesPerScanLine = cx * 3;
6132 break;
6133
6134 case 32:
6135 pCurMode->info.BytesPerScanLine = cx * 4;
6136 pCurMode->info.LinBytesPerScanLine = cx * 4;
6137 break;
6138 }
6139
6140 /* commit it */
6141 pCurMode++;
6142 }
6143 else if (rc != VERR_CFGM_VALUE_NOT_FOUND)
6144 {
6145 AssertMsgFailed(("CFGMR3QueryStringAlloc(,'%s',) -> %Rrc\n", szExtraDataKey, rc));
6146 return rc;
6147 }
6148 } /* foreach custom mode key */
6149 }
6150
6151 /*
6152 * Add the "End of list" mode.
6153 */
6154 memset(pCurMode, 0, sizeof(*pCurMode));
6155 pCurMode->mode = VBE_VESA_MODE_END_OF_LIST;
6156
6157 /*
6158 * Register I/O Port for the VBE BIOS Extra Data.
6159 */
6160 rc = PDMDevHlpIOPortRegister(pDevIns, VBE_EXTRA_PORT, 1, NULL, vbeIOPortWriteVBEExtra, vbeIOPortReadVBEExtra, NULL, NULL, "VBE BIOS Extra Data");
6161 if (RT_FAILURE(rc))
6162 return rc;
6163#endif /* VBE_NEW_DYN_LIST */
6164
6165 /*
6166 * Register I/O Port for the BIOS Logo.
6167 */
6168 rc = PDMDevHlpIOPortRegister(pDevIns, LOGO_IO_PORT, 1, NULL, vbeIOPortWriteCMDLogo, vbeIOPortReadCMDLogo, NULL, NULL, "BIOS Logo");
6169 if (RT_FAILURE(rc))
6170 return rc;
6171
6172 /*
6173 * Register debugger info callbacks.
6174 */
6175 PDMDevHlpDBGFInfoRegister(pDevIns, "vgatext", "Display VGA memory formatted as text.", vgaInfoText);
6176 PDMDevHlpDBGFInfoRegister(pDevIns, "vgacr", "Dump VGA CRTC registers.", vgaInfoCR);
6177 PDMDevHlpDBGFInfoRegister(pDevIns, "vgasr", "Dump VGA Sequencer registers.", vgaInfoSR);
6178 PDMDevHlpDBGFInfoRegister(pDevIns, "vgaar", "Dump VGA Attribute Controller registers.", vgaInfoAR);
6179 PDMDevHlpDBGFInfoRegister(pDevIns, "vgadac", "Dump VGA DAC registers.", vgaInfoDAC);
6180
6181 /*
6182 * Construct the logo header.
6183 */
6184 LOGOHDR LogoHdr = { LOGO_HDR_MAGIC, 0, 0, 0, 0, 0, 0 };
6185
6186 rc = CFGMR3QueryU8(pCfgHandle, "FadeIn", &LogoHdr.fu8FadeIn);
6187 if (rc == VERR_CFGM_VALUE_NOT_FOUND)
6188 LogoHdr.fu8FadeIn = 1;
6189 else if (RT_FAILURE(rc))
6190 return PDMDEV_SET_ERROR(pDevIns, rc,
6191 N_("Configuration error: Querying \"FadeIn\" as integer failed"));
6192
6193 rc = CFGMR3QueryU8(pCfgHandle, "FadeOut", &LogoHdr.fu8FadeOut);
6194 if (rc == VERR_CFGM_VALUE_NOT_FOUND)
6195 LogoHdr.fu8FadeOut = 1;
6196 else if (RT_FAILURE(rc))
6197 return PDMDEV_SET_ERROR(pDevIns, rc,
6198 N_("Configuration error: Querying \"FadeOut\" as integer failed"));
6199
6200 rc = CFGMR3QueryU16(pCfgHandle, "LogoTime", &LogoHdr.u16LogoMillies);
6201 if (rc == VERR_CFGM_VALUE_NOT_FOUND)
6202 LogoHdr.u16LogoMillies = 0;
6203 else if (RT_FAILURE(rc))
6204 return PDMDEV_SET_ERROR(pDevIns, rc,
6205 N_("Configuration error: Querying \"LogoTime\" as integer failed"));
6206
6207 /* Delay the logo a little bit */
6208 if (LogoHdr.fu8FadeIn && LogoHdr.fu8FadeOut && !LogoHdr.u16LogoMillies)
6209 LogoHdr.u16LogoMillies = RT_MAX(LogoHdr.u16LogoMillies, LOGO_DELAY_TIME);
6210
6211 rc = CFGMR3QueryU8(pCfgHandle, "ShowBootMenu", &LogoHdr.fu8ShowBootMenu);
6212 if (rc == VERR_CFGM_VALUE_NOT_FOUND)
6213 LogoHdr.fu8ShowBootMenu = 0;
6214 else if (RT_FAILURE(rc))
6215 return PDMDEV_SET_ERROR(pDevIns, rc,
6216 N_("Configuration error: Querying \"ShowBootMenu\" as integer failed"));
6217
6218 /*
6219 * Get the Logo file name.
6220 */
6221 rc = CFGMR3QueryStringAlloc(pCfgHandle, "LogoFile", &pThis->pszLogoFile);
6222 if (rc == VERR_CFGM_VALUE_NOT_FOUND)
6223 pThis->pszLogoFile = NULL;
6224 else if (RT_FAILURE(rc))
6225 return PDMDEV_SET_ERROR(pDevIns, rc,
6226 N_("Configuration error: Querying \"LogoFile\" as a string failed"));
6227 else if (!*pThis->pszLogoFile)
6228 {
6229 MMR3HeapFree(pThis->pszLogoFile);
6230 pThis->pszLogoFile = NULL;
6231 }
6232
6233 /*
6234 * Determine the logo size, open any specified logo file in the process.
6235 */
6236 LogoHdr.cbLogo = g_cbVgaDefBiosLogo;
6237 RTFILE FileLogo = NIL_RTFILE;
6238 if (pThis->pszLogoFile)
6239 {
6240 rc = RTFileOpen(&FileLogo, pThis->pszLogoFile,
6241 RTFILE_O_READ | RTFILE_O_OPEN | RTFILE_O_DENY_WRITE);
6242 if (RT_SUCCESS(rc))
6243 {
6244 uint64_t cbFile;
6245 rc = RTFileGetSize(FileLogo, &cbFile);
6246 if (RT_SUCCESS(rc))
6247 {
6248 if (cbFile > 0 && cbFile < 32*_1M)
6249 LogoHdr.cbLogo = (uint32_t)cbFile;
6250 else
6251 rc = VERR_TOO_MUCH_DATA;
6252 }
6253 }
6254 if (RT_FAILURE(rc))
6255 {
6256 /*
6257 * Ignore failure and fall back to the default logo.
6258 */
6259 LogRel(("vgaR3Construct: Failed to open logo file '%s', rc=%Rrc!\n", pThis->pszLogoFile, rc));
6260 if (FileLogo != NIL_RTFILE)
6261 RTFileClose(FileLogo);
6262 FileLogo = NIL_RTFILE;
6263 MMR3HeapFree(pThis->pszLogoFile);
6264 pThis->pszLogoFile = NULL;
6265 }
6266 }
6267
6268 /*
6269 * Disable graphic splash screen if it doesn't fit into VRAM.
6270 */
6271 if (pThis->vram_size < LOGO_MAX_SIZE)
6272 LogoHdr.fu8FadeIn = LogoHdr.fu8FadeOut = LogoHdr.u16LogoMillies = 0;
6273
6274 /*
6275 * Allocate buffer for the logo data.
6276 * RT_MAX() is applied to let us fall back to default logo on read failure.
6277 */
6278 pThis->cbLogo = sizeof(LogoHdr) + LogoHdr.cbLogo;
6279 pThis->pu8Logo = (uint8_t *)PDMDevHlpMMHeapAlloc(pDevIns, RT_MAX(pThis->cbLogo, g_cbVgaDefBiosLogo + sizeof(LogoHdr)));
6280 if (pThis->pu8Logo)
6281 {
6282 /*
6283 * Write the logo header.
6284 */
6285 PLOGOHDR pLogoHdr = (PLOGOHDR)pThis->pu8Logo;
6286 *pLogoHdr = LogoHdr;
6287
6288 /*
6289 * Write the logo bitmap.
6290 */
6291 if (pThis->pszLogoFile)
6292 {
6293 rc = RTFileRead(FileLogo, pLogoHdr + 1, LogoHdr.cbLogo, NULL);
6294 if (RT_FAILURE(rc))
6295 {
6296 AssertMsgFailed(("RTFileRead(,,%d,NULL) -> %Rrc\n", LogoHdr.cbLogo, rc));
6297 pLogoHdr->cbLogo = LogoHdr.cbLogo = g_cbVgaDefBiosLogo;
6298 memcpy(pLogoHdr + 1, g_abVgaDefBiosLogo, LogoHdr.cbLogo);
6299 }
6300 }
6301 else
6302 memcpy(pLogoHdr + 1, g_abVgaDefBiosLogo, LogoHdr.cbLogo);
6303
6304 rc = vbeParseBitmap(pThis);
6305 if (RT_FAILURE(rc))
6306 {
6307 AssertMsgFailed(("vbeParseBitmap() -> %Rrc\n", rc));
6308 pLogoHdr->cbLogo = LogoHdr.cbLogo = g_cbVgaDefBiosLogo;
6309 memcpy(pLogoHdr + 1, g_abVgaDefBiosLogo, LogoHdr.cbLogo);
6310 }
6311
6312 rc = vbeParseBitmap(pThis);
6313 if (RT_FAILURE(rc))
6314 AssertReleaseMsgFailed(("Internal bitmap failed! vbeParseBitmap() -> %Rrc\n", rc));
6315
6316 rc = VINF_SUCCESS;
6317 }
6318 else
6319 rc = VERR_NO_MEMORY;
6320
6321 /*
6322 * Cleanup.
6323 */
6324 if (FileLogo != NIL_RTFILE)
6325 RTFileClose(FileLogo);
6326
6327#ifdef VBOX_WITH_HGSMI
6328 VBVAInit (pThis);
6329#endif /* VBOX_WITH_HGSMI */
6330
6331 /*
6332 * Statistics.
6333 */
6334 STAM_REG(pVM, &pThis->StatRZMemoryRead, STAMTYPE_PROFILE, "/Devices/VGA/RZ/MMIO-Read", STAMUNIT_TICKS_PER_CALL, "Profiling of the VGAGCMemoryRead() body.");
6335 STAM_REG(pVM, &pThis->StatR3MemoryRead, STAMTYPE_PROFILE, "/Devices/VGA/R3/MMIO-Read", STAMUNIT_TICKS_PER_CALL, "Profiling of the VGAGCMemoryRead() body.");
6336 STAM_REG(pVM, &pThis->StatRZMemoryWrite, STAMTYPE_PROFILE, "/Devices/VGA/RZ/MMIO-Write", STAMUNIT_TICKS_PER_CALL, "Profiling of the VGAGCMemoryWrite() body.");
6337 STAM_REG(pVM, &pThis->StatR3MemoryWrite, STAMTYPE_PROFILE, "/Devices/VGA/R3/MMIO-Write", STAMUNIT_TICKS_PER_CALL, "Profiling of the VGAGCMemoryWrite() body.");
6338 STAM_REG(pVM, &pThis->StatMapPage, STAMTYPE_COUNTER, "/Devices/VGA/MapPageCalls", STAMUNIT_OCCURENCES, "Calls to IOMMMIOMapMMIO2Page.");
6339
6340 /* Init latched access mask. */
6341 pThis->uMaskLatchAccess = 0x3ff;
6342 return rc;
6343}
6344
6345
6346/**
6347 * Destruct a device instance.
6348 *
6349 * Most VM resources are freed by the VM. This callback is provided so that any non-VM
6350 * resources can be freed correctly.
6351 *
6352 * @param pDevIns The device instance data.
6353 */
6354static DECLCALLBACK(int) vgaR3Destruct(PPDMDEVINS pDevIns)
6355{
6356#ifdef VBE_NEW_DYN_LIST
6357 PVGASTATE pThis = PDMINS_2_DATA(pDevIns, PVGASTATE);
6358 LogFlow(("vgaR3Destruct:\n"));
6359
6360 /*
6361 * Free MM heap pointers.
6362 */
6363 if (pThis->pu8VBEExtraData)
6364 {
6365 MMR3HeapFree(pThis->pu8VBEExtraData);
6366 pThis->pu8VBEExtraData = NULL;
6367 }
6368#endif
6369
6370 PDMR3CritSectDelete(&pThis->lock);
6371 return VINF_SUCCESS;
6372}
6373
6374
6375/**
6376 * The device registration structure.
6377 */
6378const PDMDEVREG g_DeviceVga =
6379{
6380 /* u32Version */
6381 PDM_DEVREG_VERSION,
6382 /* szDeviceName */
6383 "vga",
6384 /* szRCMod */
6385 "VBoxDDGC.gc",
6386 /* szR0Mod */
6387 "VBoxDDR0.r0",
6388 /* pszDescription */
6389 "VGA Adaptor with VESA extensions.",
6390 /* fFlags */
6391 PDM_DEVREG_FLAGS_DEFAULT_BITS | PDM_DEVREG_FLAGS_RC | PDM_DEVREG_FLAGS_R0,
6392 /* fClass */
6393 PDM_DEVREG_CLASS_GRAPHICS,
6394 /* cMaxInstances */
6395 1,
6396 /* cbInstance */
6397 sizeof(VGASTATE),
6398 /* pfnConstruct */
6399 vgaR3Construct,
6400 /* pfnDestruct */
6401 vgaR3Destruct,
6402 /* pfnRelocate */
6403 vgaR3Relocate,
6404 /* pfnIOCtl */
6405 NULL,
6406 /* pfnPowerOn */
6407 NULL,
6408 /* pfnReset */
6409 vgaR3Reset,
6410 /* pfnSuspend */
6411 NULL,
6412 /* pfnResume */
6413 NULL,
6414 /* pfnAttach */
6415 vgaAttach,
6416 /* pfnDetach */
6417 vgaDetach,
6418 /* pfnQueryInterface */
6419 NULL,
6420 /* pfnInitComplete */
6421 NULL,
6422 /* pfnPowerOff */
6423 NULL,
6424 /* pfnSoftReset */
6425 NULL,
6426 /* u32VersionEnd */
6427 PDM_DEVREG_VERSION
6428};
6429
6430#endif /* !IN_RING3 */
6431#endif /* VBOX */
6432#endif /* !VBOX_DEVICE_STRUCT_TESTCASE */
6433
6434/*
6435 * Local Variables:
6436 * nuke-trailing-whitespace-p:nil
6437 * End:
6438 */
Note: See TracBrowser for help on using the repository browser.

© 2025 Oracle Support Privacy / Do Not Sell My Info Terms of Use Trademark Policy Automated Access Etiquette