VirtualBox

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

Last change on this file since 103914 was 103419, checked in by vboxsync, 8 months ago

Devices/Graphics: Committed a bit too much in r161740, bugref:3409

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 266.1 KB
Line 
1/* $Id: DevVGA.cpp 103419 2024-02-19 08:59:56Z vboxsync $ */
2/** @file
3 * DevVGA - VBox VGA/VESA device.
4 */
5
6/*
7 * Copyright (C) 2006-2023 Oracle and/or its affiliates.
8 *
9 * This file is part of VirtualBox base platform packages, as
10 * available from https://www.virtualbox.org.
11 *
12 * This program is free software; you can redistribute it and/or
13 * modify it under the terms of the GNU General Public License
14 * as published by the Free Software Foundation, in version 3 of the
15 * License.
16 *
17 * This program is distributed in the hope that it will be useful, but
18 * WITHOUT ANY WARRANTY; without even the implied warranty of
19 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
20 * General Public License for more details.
21 *
22 * You should have received a copy of the GNU General Public License
23 * along with this program; if not, see <https://www.gnu.org/licenses>.
24 *
25 * SPDX-License-Identifier: GPL-3.0-only
26 * --------------------------------------------------------------------
27 *
28 * This code is based on:
29 *
30 * QEMU VGA Emulator.
31 *
32 * Copyright (c) 2003 Fabrice Bellard
33 *
34 * Permission is hereby granted, free of charge, to any person obtaining a copy
35 * of this software and associated documentation files (the "Software"), to deal
36 * in the Software without restriction, including without limitation the rights
37 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
38 * copies of the Software, and to permit persons to whom the Software is
39 * furnished to do so, subject to the following conditions:
40 *
41 * The above copyright notice and this permission notice shall be included in
42 * all copies or substantial portions of the Software.
43 *
44 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
45 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
46 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
47 * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
48 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
49 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
50 * THE SOFTWARE.
51 */
52
53
54/*********************************************************************************************************************************
55* Defined Constants And Macros *
56*********************************************************************************************************************************/
57
58/* WARNING!!! All defines that affect VGAState should be placed in DevVGA.h !!!
59 * NEVER place them here as this would lead to VGASTATE inconsistency
60 * across different .cpp files !!!
61 */
62
63#ifdef VBOX_WITH_HGSMI
64#define PCIDEV_2_VGASTATE(pPciDev) ((PVGASTATE)((uintptr_t)pPciDev - RT_OFFSETOF(VGASTATE, Dev)))
65#endif /* VBOX_WITH_HGSMI */
66
67/* VGA text mode blinking constants (cursor and blinking chars). */
68#define VGA_BLINK_PERIOD_FULL (RT_NS_100MS * 4) /**< Blink cycle length. */
69#define VGA_BLINK_PERIOD_ON (RT_NS_100MS * 2) /**< How long cursor/text is visible. */
70
71/* EGA compatible switch values (in high nibble).
72 * XENIX 2.1.x/2.2.x is known to rely on the switch values.
73 */
74#define EGA_SWITCHES 0x90 /* Off-on-on-off, high-res color EGA display. */
75
76
77/*********************************************************************************************************************************
78* Header Files *
79*********************************************************************************************************************************/
80#define LOG_GROUP LOG_GROUP_DEV_VGA
81#include <iprt/asm-mem.h>
82#include <VBox/vmm/pdmdev.h>
83#include <VBox/vmm/pgm.h>
84#include <VBox/AssertGuest.h>
85#ifdef IN_RING3
86# include <iprt/mem.h>
87# include <iprt/ctype.h>
88#endif /* IN_RING3 */
89#include <iprt/assert.h>
90#include <iprt/asm.h>
91#include <iprt/file.h>
92#include <iprt/time.h>
93#include <iprt/string.h>
94#include <iprt/uuid.h>
95
96#include <iprt/formats/bmp.h>
97
98#include <VBox/VMMDev.h>
99#include <VBoxVideo.h>
100#include <VBox/bioslogo.h>
101
102/* should go BEFORE any other DevVGA include to make all DevVGA.h config defines be visible */
103#include "DevVGA.h"
104
105#if defined(IN_RING3) && !defined(VBOX_DEVICE_STRUCT_TESTCASE)
106# include "DevVGAModes.h"
107# include <stdio.h> /* sscan */
108#endif
109
110#include "VBoxDD.h"
111#include "VBoxDD2.h"
112
113#ifdef VBOX_WITH_VMSVGA
114#include "DevVGA-SVGA.h"
115#endif
116
117
118/*********************************************************************************************************************************
119* Structures and Typedefs *
120*********************************************************************************************************************************/
121
122/** The BIOS boot menu text position, X. */
123#define LOGO_F12TEXT_X 304
124/** The BIOS boot menu text position, Y. */
125#define LOGO_F12TEXT_Y 460
126
127/** Width of the "Press F12 to select boot device." bitmap.
128 Anything that exceeds the limit of F12BootText below is filled with
129 background. */
130#define LOGO_F12TEXT_WIDTH 286
131/** Height of the boot device selection bitmap, see LOGO_F12TEXT_WIDTH. */
132#define LOGO_F12TEXT_HEIGHT 12
133
134/** The BIOS logo delay time (msec). */
135#define LOGO_DELAY_TIME 2000
136
137#define LOGO_MAX_WIDTH 640
138#define LOGO_MAX_HEIGHT 480
139#define LOGO_MAX_SIZE LOGO_MAX_WIDTH * LOGO_MAX_HEIGHT * 4
140
141
142/*********************************************************************************************************************************
143* Global Variables *
144*********************************************************************************************************************************/
145#ifdef IN_RING3
146/* "Press F12 to select boot device." bitmap. */
147static const uint8_t g_abLogoF12BootText[] =
148{
149 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
150 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
151 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x07, 0x0F, 0x7C,
152 0xF8, 0xF0, 0x01, 0xE0, 0x81, 0x9F, 0x3F, 0x00, 0x70, 0xF8, 0x00, 0xE0, 0xC3,
153 0x07, 0x0F, 0x1F, 0x3E, 0x70, 0x00, 0xF0, 0xE1, 0xC3, 0x07, 0x0E, 0x00, 0x6E,
154 0x7C, 0x60, 0xE0, 0xE1, 0xC3, 0x07, 0xC6, 0x80, 0x81, 0x31, 0x63, 0xC6, 0x00,
155 0x30, 0x80, 0x61, 0x0C, 0x00, 0x36, 0x63, 0x00, 0x8C, 0x19, 0x83, 0x61, 0xCC,
156 0x18, 0x36, 0x00, 0xCC, 0x8C, 0x19, 0xC3, 0x06, 0xC0, 0x8C, 0x31, 0x3C, 0x30,
157 0x8C, 0x19, 0x83, 0x31, 0x60, 0x60, 0x00, 0x0C, 0x18, 0x00, 0x0C, 0x60, 0x18,
158 0x00, 0x80, 0xC1, 0x18, 0x00, 0x30, 0x06, 0x60, 0x18, 0x30, 0x80, 0x01, 0x00,
159 0x33, 0x63, 0xC6, 0x30, 0x00, 0x30, 0x63, 0x80, 0x19, 0x0C, 0x03, 0x06, 0x00,
160 0x0C, 0x18, 0x18, 0xC0, 0x81, 0x03, 0x00, 0x03, 0x18, 0x0C, 0x00, 0x60, 0x30,
161 0x06, 0x00, 0x87, 0x01, 0x18, 0x06, 0x0C, 0x60, 0x00, 0xC0, 0xCC, 0x98, 0x31,
162 0x0C, 0x00, 0xCC, 0x18, 0x30, 0x0C, 0xC3, 0x80, 0x01, 0x00, 0x03, 0x66, 0xFE,
163 0x18, 0x30, 0x00, 0xC0, 0x02, 0x06, 0x06, 0x00, 0x18, 0x8C, 0x01, 0x60, 0xE0,
164 0x0F, 0x86, 0x3F, 0x03, 0x18, 0x00, 0x30, 0x33, 0x66, 0x0C, 0x03, 0x00, 0x33,
165 0xFE, 0x0C, 0xC3, 0x30, 0xE0, 0x0F, 0xC0, 0x87, 0x9B, 0x31, 0x63, 0xC6, 0x00,
166 0xF0, 0x80, 0x01, 0x03, 0x00, 0x06, 0x63, 0x00, 0x8C, 0x19, 0x83, 0x61, 0xCC,
167 0x18, 0x06, 0x00, 0x6C, 0x8C, 0x19, 0xC3, 0x00, 0x80, 0x8D, 0x31, 0xC3, 0x30,
168 0x8C, 0x19, 0x03, 0x30, 0xB3, 0xC3, 0x87, 0x0F, 0x1F, 0x00, 0x2C, 0x60, 0x80,
169 0x01, 0xE0, 0x87, 0x0F, 0x00, 0x3E, 0x7C, 0x60, 0xF0, 0xE1, 0xE3, 0x07, 0x00,
170 0x0F, 0x3E, 0x7C, 0xFC, 0x00, 0xC0, 0xC3, 0xC7, 0x30, 0x0E, 0x3E, 0x7C, 0x00,
171 0xCC, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x23, 0x1E, 0xC0, 0x00, 0x60, 0x00,
172 0x00, 0x00, 0x00, 0x00, 0x18, 0x00, 0x00, 0x60, 0x00, 0xC0, 0x00, 0x00, 0x00,
173 0x0C, 0x00, 0xC0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x33, 0x00, 0x00,
174 0x00, 0x00, 0x00, 0xC0, 0x0C, 0x87, 0x31, 0x00, 0x18, 0x00, 0x00, 0x00, 0x00,
175 0x00, 0x06, 0x00, 0x00, 0x18, 0x00, 0x30, 0x00, 0x00, 0x00, 0x03, 0x00, 0x30,
176 0x00, 0x00, 0xC0, 0x00, 0x00, 0x00, 0xE0, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00,
177 0xF8, 0x83, 0xC1, 0x07, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0xC0, 0x01, 0x00,
178 0x00, 0x04, 0x00, 0x0E, 0x00, 0x00, 0x80, 0x00, 0x00, 0x0E, 0x00, 0x00, 0x30,
179 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
180 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
181 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
182};
183#endif /* IN_RING3 */
184
185#ifndef VBOX_DEVICE_STRUCT_TESTCASE /* Till the end of the file - doesn't count indent wise. */
186
187#ifdef _MSC_VER
188# pragma warning(push)
189# pragma warning(disable:4310 4245) /* Buggy warnings: cast truncates constant value; conversion from 'int' to 'const uint8_t', signed/unsigned mismatch */
190#endif
191
192/* force some bits to zero */
193static const uint8_t sr_mask[8] = {
194 (uint8_t)~0xfc,
195 (uint8_t)~0xc2,
196 (uint8_t)~0xf0,
197 (uint8_t)~0xc0,
198 (uint8_t)~0xf1,
199 (uint8_t)~0xff,
200 (uint8_t)~0xff,
201 (uint8_t)~0x01,
202};
203
204static const uint8_t gr_mask[16] = {
205 (uint8_t)~0xf0, /* 0x00 */
206 (uint8_t)~0xf0, /* 0x01 */
207 (uint8_t)~0xf0, /* 0x02 */
208 (uint8_t)~0xe0, /* 0x03 */
209 (uint8_t)~0xfc, /* 0x04 */
210 (uint8_t)~0x84, /* 0x05 */
211 (uint8_t)~0xf0, /* 0x06 */
212 (uint8_t)~0xf0, /* 0x07 */
213 (uint8_t)~0x00, /* 0x08 */
214 (uint8_t)~0xff, /* 0x09 */
215 (uint8_t)~0xff, /* 0x0a */
216 (uint8_t)~0xff, /* 0x0b */
217 (uint8_t)~0xff, /* 0x0c */
218 (uint8_t)~0xff, /* 0x0d */
219 (uint8_t)~0xff, /* 0x0e */
220 (uint8_t)~0xff, /* 0x0f */
221};
222
223#ifdef _MSC_VER
224# pragma warning(pop)
225#endif
226
227#define cbswap_32(__x) \
228 ((uint32_t)((((uint32_t)(__x) & (uint32_t)0x000000ffUL) << 24) | \
229 (((uint32_t)(__x) & (uint32_t)0x0000ff00UL) << 8) | \
230 (((uint32_t)(__x) & (uint32_t)0x00ff0000UL) >> 8) | \
231 (((uint32_t)(__x) & (uint32_t)0xff000000UL) >> 24) ))
232
233#ifdef WORDS_BIGENDIAN
234# define PAT(x) cbswap_32(x)
235#else
236# define PAT(x) (x)
237#endif
238
239#ifdef WORDS_BIGENDIAN
240# define BIG 1
241#else
242# define BIG 0
243#endif
244
245#ifdef WORDS_BIGENDIAN
246#define GET_PLANE(data, p) (((data) >> (24 - (p) * 8)) & 0xff)
247#else
248#define GET_PLANE(data, p) (((data) >> ((p) * 8)) & 0xff)
249#endif
250
251static const uint32_t mask16[16] = {
252 PAT(0x00000000),
253 PAT(0x000000ff),
254 PAT(0x0000ff00),
255 PAT(0x0000ffff),
256 PAT(0x00ff0000),
257 PAT(0x00ff00ff),
258 PAT(0x00ffff00),
259 PAT(0x00ffffff),
260 PAT(0xff000000),
261 PAT(0xff0000ff),
262 PAT(0xff00ff00),
263 PAT(0xff00ffff),
264 PAT(0xffff0000),
265 PAT(0xffff00ff),
266 PAT(0xffffff00),
267 PAT(0xffffffff),
268};
269
270#undef PAT
271
272#ifdef WORDS_BIGENDIAN
273# define PAT(x) (x)
274#else
275# define PAT(x) cbswap_32(x)
276#endif
277
278#ifdef IN_RING3
279
280static const uint32_t dmask16[16] = {
281 PAT(0x00000000),
282 PAT(0x000000ff),
283 PAT(0x0000ff00),
284 PAT(0x0000ffff),
285 PAT(0x00ff0000),
286 PAT(0x00ff00ff),
287 PAT(0x00ffff00),
288 PAT(0x00ffffff),
289 PAT(0xff000000),
290 PAT(0xff0000ff),
291 PAT(0xff00ff00),
292 PAT(0xff00ffff),
293 PAT(0xffff0000),
294 PAT(0xffff00ff),
295 PAT(0xffffff00),
296 PAT(0xffffffff),
297};
298
299static const uint32_t dmask4[4] = {
300 PAT(0x00000000),
301 PAT(0x0000ffff),
302 PAT(0xffff0000),
303 PAT(0xffffffff),
304};
305
306static uint32_t expand4[256];
307static uint16_t expand2[256];
308static uint8_t expand4to8[16];
309
310#endif /* IN_RING3 */
311
312
313/**
314 * Mark a page in VGA A0000-BFFFF range as remapped.
315 *
316 * @param pThis VGA instance data.
317 * @param offVGAMem The offset within VGA memory.
318 */
319DECLINLINE(void) vgaMarkRemapped(PVGASTATE pThis, RTGCPHYS offVGAMem)
320{
321 AssertMsg(offVGAMem < _128K, ("offVGAMem = %RGp\n", offVGAMem));
322 pThis->bmPageRemappedVGA |= RT_BIT_32((uint32_t)offVGAMem >> GUEST_PAGE_SHIFT);
323}
324
325
326/**
327 * Checks if a page in VGA A0000-BFFFF range is remapped.
328 *
329 * @returns true if remapped.
330 * @returns false if not remapped (accesses will trap).
331 * @param pThis VGA instance data.
332 * @param offVGAMem The offset within VGA memory.
333 */
334DECLINLINE(bool) vgaIsRemapped(PVGASTATE pThis, RTGCPHYS offVGAMem)
335{
336 AssertMsg(offVGAMem < _128K, ("offVGAMem = %RGp\n", offVGAMem));
337 return pThis->bmPageRemappedVGA & RT_BIT_32((uint32_t)offVGAMem >> GUEST_PAGE_SHIFT);
338}
339
340
341/**
342 * Reset page remap tracking bits.
343 *
344 * @param pThis VGA instance data.
345 */
346DECLINLINE(void) vgaResetRemapped(PVGASTATE pThis)
347{
348 pThis->bmPageRemappedVGA = 0;
349}
350
351
352/**
353 * Set a VRAM page dirty.
354 *
355 * @param pThis VGA instance data.
356 * @param offVRAM The VRAM offset of the page to set.
357 */
358DECLINLINE(void) vgaR3MarkDirty(PVGASTATE pThis, RTGCPHYS offVRAM)
359{
360 AssertMsg(offVRAM < pThis->vram_size, ("offVRAM = %p, pThis->vram_size = %p\n", offVRAM, pThis->vram_size));
361 ASMBitSet(&pThis->bmDirtyBitmap[0], offVRAM >> GUEST_PAGE_SHIFT);
362 pThis->fHasDirtyBits = true;
363}
364
365#ifdef IN_RING3
366
367/**
368 * Tests if a VRAM page is dirty.
369 *
370 * @returns true if dirty.
371 * @returns false if clean.
372 * @param pThis VGA instance data.
373 * @param offVRAM The VRAM offset of the page to check.
374 */
375DECLINLINE(bool) vgaR3IsDirty(PVGASTATE pThis, RTGCPHYS offVRAM)
376{
377 AssertMsg(offVRAM < pThis->vram_size, ("offVRAM = %p, pThis->vram_size = %p\n", offVRAM, pThis->vram_size));
378 return ASMBitTest(&pThis->bmDirtyBitmap[0], offVRAM >> GUEST_PAGE_SHIFT);
379}
380
381
382/**
383 * Reset dirty flags in a give range.
384 *
385 * @param pThis VGA instance data.
386 * @param offVRAMStart Offset into the VRAM buffer of the first page.
387 * @param offVRAMEnd Offset into the VRAM buffer of the last page - exclusive.
388 */
389DECLINLINE(void) vgaR3ResetDirty(PVGASTATE pThis, RTGCPHYS offVRAMStart, RTGCPHYS offVRAMEnd)
390{
391 Assert(offVRAMStart < pThis->vram_size);
392 Assert(offVRAMEnd <= pThis->vram_size);
393 Assert(offVRAMStart < offVRAMEnd);
394 ASMBitClearRange(&pThis->bmDirtyBitmap[0], offVRAMStart >> GUEST_PAGE_SHIFT, offVRAMEnd >> GUEST_PAGE_SHIFT);
395}
396
397
398/**
399 * Queries the VRAM dirty bits and resets the monitoring.
400 */
401static void vgaR3UpdateDirtyBitsAndResetMonitoring(PPDMDEVINS pDevIns, PVGASTATE pThis)
402{
403 size_t const cbBitmap = RT_ALIGN_Z(RT_MIN(pThis->vram_size, VGA_VRAM_MAX), GUEST_PAGE_SIZE * 64) / GUEST_PAGE_SIZE / 8;
404
405 /*
406 * If we don't have any dirty bits from MMIO accesses, we can just query
407 * straight into the dirty buffer.
408 */
409 if (!pThis->fHasDirtyBits)
410 {
411 int rc = PDMDevHlpMmio2QueryAndResetDirtyBitmap(pDevIns, pThis->hMmio2VRam, pThis->bmDirtyBitmap, cbBitmap);
412 AssertRC(rc);
413 }
414 /*
415 * Otherwise we'll have to query and merge the two.
416 */
417 else
418 {
419 uint64_t bmDirtyPages[VGA_VRAM_MAX / GUEST_PAGE_SIZE / 64]; /* (256 MB VRAM -> 8KB bitmap) */
420 int rc = PDMDevHlpMmio2QueryAndResetDirtyBitmap(pDevIns, pThis->hMmio2VRam, bmDirtyPages, cbBitmap);
421 if (RT_SUCCESS(rc))
422 {
423 /** @todo could use ORPS or VORPS here, I think. */
424 uint64_t *pbmDst = pThis->bmDirtyBitmap;
425 size_t const cTodo = cbBitmap / sizeof(uint64_t);
426
427 /* Do 64 bytes at a time first. */
428 size_t const cTodoFirst = cTodo & ~(size_t)7;
429 size_t idx;
430 for (idx = 0; idx < cTodoFirst; idx += 8)
431 {
432 pbmDst[idx + 0] |= bmDirtyPages[idx + 0];
433 pbmDst[idx + 1] |= bmDirtyPages[idx + 1];
434 pbmDst[idx + 2] |= bmDirtyPages[idx + 2];
435 pbmDst[idx + 3] |= bmDirtyPages[idx + 3];
436 pbmDst[idx + 4] |= bmDirtyPages[idx + 4];
437 pbmDst[idx + 5] |= bmDirtyPages[idx + 5];
438 pbmDst[idx + 6] |= bmDirtyPages[idx + 6];
439 pbmDst[idx + 7] |= bmDirtyPages[idx + 7];
440 }
441
442 /* Then do a mopup of anything remaining. */
443 switch (cTodo - idx)
444 {
445 case 7: pbmDst[idx + 6] |= bmDirtyPages[idx + 6]; RT_FALL_THRU();
446 case 6: pbmDst[idx + 5] |= bmDirtyPages[idx + 5]; RT_FALL_THRU();
447 case 5: pbmDst[idx + 4] |= bmDirtyPages[idx + 4]; RT_FALL_THRU();
448 case 4: pbmDst[idx + 3] |= bmDirtyPages[idx + 3]; RT_FALL_THRU();
449 case 3: pbmDst[idx + 2] |= bmDirtyPages[idx + 2]; RT_FALL_THRU();
450 case 2: pbmDst[idx + 1] |= bmDirtyPages[idx + 1]; RT_FALL_THRU();
451 case 1: pbmDst[idx] |= bmDirtyPages[idx]; break;
452 case 0: break;
453 default: AssertFailedBreak();
454 }
455
456 pThis->fHasDirtyBits = false;
457 }
458 }
459}
460
461#endif /* IN_RING3 */
462
463/* Update the values needed for calculating Vertical Retrace and
464 * Display Enable status bits more or less accurately. The Display Enable
465 * bit is set (indicating *disabled* display signal) when either the
466 * horizontal (hblank) or vertical (vblank) blanking is active. The
467 * Vertical Retrace bit is set when vertical retrace (vsync) is active.
468 * Unless the CRTC is horribly misprogrammed, vsync implies vblank.
469 */
470static void vga_update_retrace_state(PVGASTATE pThis)
471{
472 unsigned htotal_cclks, vtotal_lines, chars_per_sec;
473 unsigned hblank_start_cclk, hblank_end_cclk, hblank_width, hblank_skew_cclks;
474 unsigned vsync_start_line, vsync_end, vsync_width;
475 unsigned vblank_start_line, vblank_end, vblank_width;
476 unsigned char_dots, clock_doubled, clock_index;
477 const int clocks[] = {25175000, 28322000, 25175000, 25175000};
478 vga_retrace_s *r = &pThis->retrace_state;
479
480 /* For horizontal timings, we only care about the blanking start/end. */
481 htotal_cclks = pThis->cr[0x00] + 5;
482 hblank_start_cclk = pThis->cr[0x02];
483 hblank_end_cclk = (pThis->cr[0x03] & 0x1f) + ((pThis->cr[0x05] & 0x80) >> 2);
484 hblank_skew_cclks = (pThis->cr[0x03] >> 5) & 3;
485
486 /* For vertical timings, we need both the blanking start/end... */
487 vtotal_lines = pThis->cr[0x06] + ((pThis->cr[0x07] & 1) << 8) + ((pThis->cr[0x07] & 0x20) << 4) + 2;
488 vblank_start_line = pThis->cr[0x15] + ((pThis->cr[0x07] & 8) << 5) + ((pThis->cr[0x09] & 0x20) << 4);
489 vblank_end = pThis->cr[0x16];
490 /* ... and the vertical retrace (vsync) start/end. */
491 vsync_start_line = pThis->cr[0x10] + ((pThis->cr[0x07] & 4) << 6) + ((pThis->cr[0x07] & 0x80) << 2);
492 vsync_end = pThis->cr[0x11] & 0xf;
493
494 /* Calculate the blanking and sync widths. The way it's implemented in
495 * the VGA with limited-width compare counters is quite a piece of work.
496 */
497 hblank_width = (hblank_end_cclk - hblank_start_cclk) & 0x3f;/* 6 bits */
498 vblank_width = (vblank_end - vblank_start_line) & 0xff; /* 8 bits */
499 vsync_width = (vsync_end - vsync_start_line) & 0xf; /* 4 bits */
500
501 /* Calculate the dot and character clock rates. */
502 clock_doubled = (pThis->sr[0x01] >> 3) & 1; /* Clock doubling bit. */
503 clock_index = (pThis->msr >> 2) & 3;
504 char_dots = (pThis->sr[0x01] & 1) ? 8 : 9; /* 8 or 9 dots per cclk. */
505
506 chars_per_sec = clocks[clock_index] / char_dots;
507 Assert(chars_per_sec); /* Can't possibly be zero. */
508
509 htotal_cclks <<= clock_doubled;
510
511 /* Calculate the number of cclks per entire frame. */
512 r->frame_cclks = vtotal_lines * htotal_cclks;
513 Assert(r->frame_cclks); /* Can't possibly be zero. */
514
515 if (r->v_freq_hz) { /* Could be set to emulate a specific rate. */
516 r->cclk_ns = 1000000000 / (r->frame_cclks * r->v_freq_hz);
517 } else {
518 r->cclk_ns = 1000000000 / chars_per_sec;
519 }
520 Assert(r->cclk_ns);
521 r->frame_ns = r->frame_cclks * r->cclk_ns;
522
523 /* Calculate timings in cclks/lines. Stored but not directly used. */
524 r->hb_start = hblank_start_cclk + hblank_skew_cclks;
525 r->hb_end = hblank_start_cclk + hblank_width + hblank_skew_cclks;
526 r->h_total = htotal_cclks;
527 Assert(r->h_total); /* Can't possibly be zero. */
528
529 r->vb_start = vblank_start_line;
530 r->vb_end = vblank_start_line + vblank_width + 1;
531 r->vs_start = vsync_start_line;
532 r->vs_end = vsync_start_line + vsync_width + 1;
533
534 /* Calculate timings in nanoseconds. For easier comparisons, the frame
535 * is considered to start at the beginning of the vertical and horizontal
536 * blanking period.
537 */
538 r->h_total_ns = htotal_cclks * r->cclk_ns;
539 r->hb_end_ns = hblank_width * r->cclk_ns;
540 r->vb_end_ns = vblank_width * r->h_total_ns;
541 r->vs_start_ns = (r->vs_start - r->vb_start) * r->h_total_ns;
542 r->vs_end_ns = (r->vs_end - r->vb_start) * r->h_total_ns;
543 Assert(r->h_total_ns); /* See h_total. */
544}
545
546static uint8_t vga_retrace(PPDMDEVINS pDevIns, PVGASTATE pThis)
547{
548 vga_retrace_s *r = &pThis->retrace_state;
549
550 if (r->frame_ns) {
551 uint8_t val = pThis->st01 & ~(ST01_V_RETRACE | ST01_DISP_ENABLE);
552 unsigned cur_frame_ns, cur_line_ns;
553 uint64_t time_ns;
554
555 time_ns = PDMDevHlpTMTimeVirtGetNano(pDevIns);
556
557 /* Determine the time within the frame. */
558 cur_frame_ns = time_ns % r->frame_ns;
559
560 /* See if we're in the vertical blanking period... */
561 if (cur_frame_ns < r->vb_end_ns) {
562 val |= ST01_DISP_ENABLE;
563 /* ... and additionally in the vertical sync period. */
564 if (cur_frame_ns >= r->vs_start_ns && cur_frame_ns <= r->vs_end_ns)
565 val |= ST01_V_RETRACE;
566 } else {
567 /* Determine the time within the current scanline. */
568 cur_line_ns = cur_frame_ns % r->h_total_ns;
569 /* See if we're in the horizontal blanking period. */
570 if (cur_line_ns < r->hb_end_ns)
571 val |= ST01_DISP_ENABLE;
572 }
573 return val;
574 } else {
575 return pThis->st01 ^ (ST01_V_RETRACE | ST01_DISP_ENABLE);
576 }
577}
578
579static int vga_ioport_invalid(PVGASTATE pThis, uint32_t addr)
580{
581 if (pThis->msr & MSR_COLOR_EMULATION) {
582 /* Color */
583 return (addr >= 0x3b0 && addr <= 0x3bf);
584 } else {
585 /* Monochrome */
586 return (addr >= 0x3d0 && addr <= 0x3df);
587 }
588}
589
590static uint32_t vga_ioport_read(PPDMDEVINS pDevIns, PVGASTATE pThis, uint32_t addr)
591{
592 int val, index;
593
594 /* check port range access depending on color/monochrome mode */
595 if (vga_ioport_invalid(pThis, addr)) {
596 val = 0xff;
597 Log(("VGA: following read ignored\n"));
598 } else {
599 switch(addr) {
600 case 0x3c0:
601 if (pThis->ar_flip_flop == 0) {
602 val = pThis->ar_index;
603 } else {
604 val = 0;
605 }
606 break;
607 case 0x3c1:
608 index = pThis->ar_index & 0x1f;
609 if (index < 21)
610 val = pThis->ar[index];
611 else
612 val = 0;
613 break;
614 case 0x3c2:
615 val = pThis->st00;
616 break;
617 case 0x3c4:
618 val = pThis->sr_index;
619 break;
620 case 0x3c5:
621 val = pThis->sr[pThis->sr_index];
622 Log2(("vga: read SR%x = 0x%02x\n", pThis->sr_index, val));
623 break;
624 case 0x3c7:
625 val = pThis->dac_state;
626 break;
627 case 0x3c8:
628 val = pThis->dac_write_index;
629 break;
630 case 0x3c9:
631 Assert(pThis->dac_sub_index < 3);
632 val = pThis->palette[pThis->dac_read_index * 3 + pThis->dac_sub_index];
633 if (++pThis->dac_sub_index == 3) {
634 pThis->dac_sub_index = 0;
635 pThis->dac_read_index++;
636 }
637 break;
638 case 0x3ca:
639 val = pThis->fcr;
640 break;
641 case 0x3cc:
642 val = pThis->msr;
643 break;
644 case 0x3ce:
645 val = pThis->gr_index;
646 break;
647 case 0x3cf:
648 val = pThis->gr[pThis->gr_index];
649 Log2(("vga: read GR%x = 0x%02x\n", pThis->gr_index, val));
650 break;
651 case 0x3b4:
652 case 0x3d4:
653 val = pThis->cr_index;
654 break;
655 case 0x3b5:
656 case 0x3d5:
657 val = pThis->cr[pThis->cr_index];
658 Log2(("vga: read CR%x = 0x%02x\n", pThis->cr_index, val));
659 break;
660 case 0x3ba:
661 case 0x3da:
662 val = pThis->st01 = vga_retrace(pDevIns, pThis);
663 pThis->ar_flip_flop = 0;
664 break;
665 default:
666 val = 0x00;
667 break;
668 }
669 }
670 Log(("VGA: read addr=0x%04x data=0x%02x\n", addr, val));
671 return val;
672}
673
674static void vga_ioport_write(PPDMDEVINS pDevIns, PVGASTATE pThis, uint32_t addr, uint32_t val)
675{
676 int index;
677
678 Log(("VGA: write addr=0x%04x data=0x%02x\n", addr, val));
679
680 /* check port range access depending on color/monochrome mode */
681 if (vga_ioport_invalid(pThis, addr)) {
682 Log(("VGA: previous write ignored\n"));
683 return;
684 }
685
686 switch(addr) {
687 case 0x3c0:
688 case 0x3c1:
689 if (pThis->ar_flip_flop == 0) {
690 val &= 0x3f;
691 pThis->ar_index = val;
692 } else {
693 index = pThis->ar_index & 0x1f;
694 switch(index) {
695 case 0x00: case 0x01: case 0x02: case 0x03: case 0x04: case 0x05: case 0x06: case 0x07:
696 case 0x08: case 0x09: case 0x0a: case 0x0b: case 0x0c: case 0x0d: case 0x0e: case 0x0f:
697 pThis->ar[index] = val & 0x3f;
698 break;
699 case 0x10:
700 pThis->ar[index] = val & ~0x10;
701 break;
702 case 0x11:
703 pThis->ar[index] = val;
704 break;
705 case 0x12:
706 pThis->ar[index] = val & ~0xc0;
707 break;
708 case 0x13:
709 pThis->ar[index] = val & ~0xf0;
710 break;
711 case 0x14:
712 pThis->ar[index] = val & ~0xf0;
713 break;
714 default:
715 break;
716 }
717 }
718 pThis->ar_flip_flop ^= 1;
719 break;
720 case 0x3c2:
721 pThis->msr = val & ~0x10;
722 if (pThis->fRealRetrace)
723 vga_update_retrace_state(pThis);
724 /* The two clock select bits also determine which of the four switches
725 * is reflected in bit 4 of Input Status Register 0.
726 * This is EGA compatible behavior. See the IBM EGA Tech Ref.
727 */
728 pThis->st00 = (pThis->st00 & ~0x10) | ((EGA_SWITCHES >> ((val >> 2) & 0x3) & 0x10));
729 break;
730 case 0x3c4:
731 pThis->sr_index = val & 7;
732 break;
733 case 0x3c5:
734 Log2(("vga: write SR%x = 0x%02x\n", pThis->sr_index, val));
735 pThis->sr[pThis->sr_index] = val & sr_mask[pThis->sr_index];
736 /* Allow SR07 to disable VBE. */
737 if (pThis->sr_index == 0x07 && !(val & 1))
738 {
739 pThis->vbe_regs[VBE_DISPI_INDEX_ENABLE] = VBE_DISPI_DISABLED;
740 pThis->bank_offset = 0;
741 }
742 if (pThis->fRealRetrace && pThis->sr_index == 0x01)
743 vga_update_retrace_state(pThis);
744#ifndef IN_RC
745 /* The VGA region is (could be) affected by this change; reset all aliases we've created. */
746 if ( pThis->sr_index == 4 /* mode */
747 || pThis->sr_index == 2 /* plane mask */)
748 {
749 if (pThis->bmPageRemappedVGA != 0)
750 {
751 PDMDevHlpMmioResetRegion(pDevIns, pThis->hMmioLegacy);
752 STAM_COUNTER_INC(&pThis->StatMapReset);
753 vgaResetRemapped(pThis);
754 }
755 }
756#endif
757 break;
758 case 0x3c7:
759 pThis->dac_read_index = val;
760 pThis->dac_sub_index = 0;
761 pThis->dac_state = 3;
762 break;
763 case 0x3c8:
764 pThis->dac_write_index = val;
765 pThis->dac_sub_index = 0;
766 pThis->dac_state = 0;
767 break;
768 case 0x3c9:
769 Assert(pThis->dac_sub_index < 3);
770 pThis->dac_cache[pThis->dac_sub_index] = val;
771 if (++pThis->dac_sub_index == 3) {
772 memcpy(&pThis->palette[pThis->dac_write_index * 3], pThis->dac_cache, 3);
773 pThis->dac_sub_index = 0;
774 pThis->dac_write_index++;
775 }
776 break;
777 case 0x3ce:
778 pThis->gr_index = val & 0x0f;
779 break;
780 case 0x3cf:
781 Log2(("vga: write GR%x = 0x%02x\n", pThis->gr_index, val));
782 Assert(pThis->gr_index < RT_ELEMENTS(gr_mask));
783 pThis->gr[pThis->gr_index] = val & gr_mask[pThis->gr_index];
784
785#ifndef IN_RC
786 /* The VGA region is (could be) affected by this change; reset all aliases we've created. */
787 if (pThis->gr_index == 6 /* memory map mode */)
788 {
789 if (pThis->bmPageRemappedVGA != 0)
790 {
791 PDMDevHlpMmioResetRegion(pDevIns, pThis->hMmioLegacy);
792 STAM_COUNTER_INC(&pThis->StatMapReset);
793 vgaResetRemapped(pThis);
794 }
795 }
796#endif
797 break;
798
799 case 0x3b4:
800 case 0x3d4:
801 pThis->cr_index = val;
802 break;
803 case 0x3b5:
804 case 0x3d5:
805 Log2(("vga: write CR%x = 0x%02x\n", pThis->cr_index, val));
806 /* handle CR0-7 protection */
807 if ((pThis->cr[0x11] & 0x80) && pThis->cr_index <= 7) {
808 /* can always write bit 4 of CR7 */
809 if (pThis->cr_index == 7)
810 pThis->cr[7] = (pThis->cr[7] & ~0x10) | (val & 0x10);
811 return;
812 }
813 pThis->cr[pThis->cr_index] = val;
814
815 if (pThis->fRealRetrace) {
816 /* The following registers are only updated during a mode set. */
817 switch(pThis->cr_index) {
818 case 0x00:
819 case 0x02:
820 case 0x03:
821 case 0x05:
822 case 0x06:
823 case 0x07:
824 case 0x09:
825 case 0x10:
826 case 0x11:
827 case 0x15:
828 case 0x16:
829 vga_update_retrace_state(pThis);
830 break;
831 }
832 }
833 break;
834 case 0x3ba:
835 case 0x3da:
836 pThis->fcr = val & 0x10;
837 break;
838 }
839}
840
841#ifdef CONFIG_BOCHS_VBE
842
843static uint32_t vbe_read_cfg(PVGASTATE pThis)
844{
845 const uint16_t u16Cfg = pThis->vbe_regs[VBE_DISPI_INDEX_CFG];
846 const uint16_t u16Id = u16Cfg & VBE_DISPI_CFG_MASK_ID;
847 const bool fQuerySupport = RT_BOOL(u16Cfg & VBE_DISPI_CFG_MASK_SUPPORT);
848
849 uint32_t val = 0;
850 switch (u16Id)
851 {
852 case VBE_DISPI_CFG_ID_VERSION: val = 1; break;
853 case VBE_DISPI_CFG_ID_VRAM_SIZE: val = pThis->vram_size; break;
854 case VBE_DISPI_CFG_ID_3D: val = pThis->f3DEnabled; break;
855# ifdef VBOX_WITH_VMSVGA
856 case VBE_DISPI_CFG_ID_VMSVGA: val = pThis->fVMSVGAEnabled; break;
857 case VBE_DISPI_CFG_ID_VMSVGA_DX: val = pThis->fVMSVGA10; break;
858# endif
859 default:
860 return 0; /* Not supported. */
861 }
862
863 return fQuerySupport ? 1 : val;
864}
865
866static uint32_t vbe_ioport_read_index(PVGASTATE pThis, uint32_t addr)
867{
868 uint32_t val = pThis->vbe_index;
869 NOREF(addr);
870 return val;
871}
872
873static uint32_t vbe_ioport_read_data(PVGASTATE pThis, uint32_t addr)
874{
875 uint32_t val;
876 NOREF(addr);
877
878 uint16_t const idxVbe = pThis->vbe_index;
879 if (idxVbe < VBE_DISPI_INDEX_NB)
880 {
881 RT_UNTRUSTED_VALIDATED_FENCE();
882 if (pThis->vbe_regs[VBE_DISPI_INDEX_ENABLE] & VBE_DISPI_GETCAPS)
883 {
884 switch (idxVbe)
885 {
886 /* XXX: do not hardcode ? */
887 case VBE_DISPI_INDEX_XRES:
888 val = VBE_DISPI_MAX_XRES;
889 break;
890 case VBE_DISPI_INDEX_YRES:
891 val = VBE_DISPI_MAX_YRES;
892 break;
893 case VBE_DISPI_INDEX_BPP:
894 val = VBE_DISPI_MAX_BPP;
895 break;
896 default:
897 Assert(idxVbe < VBE_DISPI_INDEX_NB);
898 val = pThis->vbe_regs[idxVbe];
899 break;
900 }
901 }
902 else
903 {
904 switch (idxVbe)
905 {
906 case VBE_DISPI_INDEX_VBOX_VIDEO:
907 /* Reading from the port means that the old additions are requesting the number of monitors. */
908 val = 1;
909 break;
910 case VBE_DISPI_INDEX_CFG:
911 val = vbe_read_cfg(pThis);
912 break;
913 default:
914 Assert(idxVbe < VBE_DISPI_INDEX_NB);
915 val = pThis->vbe_regs[idxVbe];
916 break;
917 }
918 }
919 }
920 else
921 val = 0;
922 Log(("VBE: read index=0x%x val=0x%x\n", idxVbe, val));
923 return val;
924}
925
926# define VBE_PITCH_ALIGN 4 /* Align pitch to 32 bits - Qt requires that. */
927
928/* Calculate scanline pitch based on bit depth and width in pixels. */
929static uint32_t calc_line_pitch(uint16_t bpp, uint16_t width)
930{
931 uint32_t pitch, aligned_pitch;
932
933 if (bpp <= 4)
934 pitch = width >> 1;
935 else
936 pitch = width * ((bpp + 7) >> 3);
937
938 /* Align the pitch to some sensible value. */
939 aligned_pitch = (pitch + (VBE_PITCH_ALIGN - 1)) & ~(VBE_PITCH_ALIGN - 1);
940 if (aligned_pitch != pitch)
941 Log(("VBE: Line pitch %d aligned to %d bytes\n", pitch, aligned_pitch));
942
943 return aligned_pitch;
944}
945
946static void recalculate_data(PVGASTATE pThis)
947{
948 uint16_t cBPP = pThis->vbe_regs[VBE_DISPI_INDEX_BPP];
949 uint16_t cVirtWidth = pThis->vbe_regs[VBE_DISPI_INDEX_VIRT_WIDTH];
950 uint16_t cX = pThis->vbe_regs[VBE_DISPI_INDEX_XRES];
951 if (!cBPP || !cX)
952 return; /* Not enough data has been set yet. */
953 uint32_t cbLinePitch = calc_line_pitch(cBPP, cVirtWidth);
954 if (!cbLinePitch)
955 cbLinePitch = calc_line_pitch(cBPP, cX);
956 if (!cbLinePitch)
957 return;
958 uint32_t cVirtHeight = pThis->vram_size / cbLinePitch;
959 uint16_t offX = pThis->vbe_regs[VBE_DISPI_INDEX_X_OFFSET];
960 uint16_t offY = pThis->vbe_regs[VBE_DISPI_INDEX_Y_OFFSET];
961 uint32_t offStart = cbLinePitch * offY;
962 if (cBPP == 4)
963 offStart += offX >> 1;
964 else
965 offStart += offX * ((cBPP + 7) >> 3);
966 offStart >>= 2;
967 pThis->vbe_line_offset = RT_MIN(cbLinePitch, pThis->vram_size);
968 pThis->vbe_start_addr = RT_MIN(offStart, pThis->vram_size);
969
970 /* The VBE_DISPI_INDEX_VIRT_HEIGHT is used to prevent setting resolution bigger than
971 * the VRAM size permits. It is used instead of VBE_DISPI_INDEX_YRES *only* in case
972 * pThis->vbe_regs[VBE_DISPI_INDEX_VIRT_HEIGHT] < pThis->vbe_regs[VBE_DISPI_INDEX_YRES].
973 * Note that VBE_DISPI_INDEX_VIRT_HEIGHT has to be clipped to UINT16_MAX, which happens
974 * with small resolutions and big VRAM. */
975 pThis->vbe_regs[VBE_DISPI_INDEX_VIRT_HEIGHT] = cVirtHeight >= UINT16_MAX ? UINT16_MAX : (uint16_t)cVirtHeight;
976}
977
978static void vbe_ioport_write_index(PVGASTATE pThis, uint32_t addr, uint32_t val)
979{
980 pThis->vbe_index = val;
981 NOREF(addr);
982}
983
984static VBOXSTRICTRC vbe_ioport_write_data(PPDMDEVINS pDevIns, PVGASTATE pThis, PVGASTATECC pThisCC, uint32_t addr, uint32_t val)
985{
986 uint32_t max_bank;
987 RT_NOREF(pThisCC, addr);
988
989 if (pThis->vbe_index <= VBE_DISPI_INDEX_NB) {
990 bool fRecalculate = false;
991 Log(("VBE: write index=0x%x val=0x%x\n", pThis->vbe_index, val));
992 switch(pThis->vbe_index) {
993 case VBE_DISPI_INDEX_ID:
994 if (val == VBE_DISPI_ID0 ||
995 val == VBE_DISPI_ID1 ||
996 val == VBE_DISPI_ID2 ||
997 val == VBE_DISPI_ID3 ||
998 val == VBE_DISPI_ID4 ||
999 /* VBox extensions. */
1000 val == VBE_DISPI_ID_VBOX_VIDEO ||
1001 val == VBE_DISPI_ID_ANYX ||
1002# ifdef VBOX_WITH_HGSMI
1003 val == VBE_DISPI_ID_HGSMI ||
1004# endif
1005 val == VBE_DISPI_ID_CFG)
1006 {
1007 pThis->vbe_regs[pThis->vbe_index] = val;
1008 }
1009 break;
1010 case VBE_DISPI_INDEX_XRES:
1011 if (val <= VBE_DISPI_MAX_XRES)
1012 {
1013 pThis->vbe_regs[pThis->vbe_index] = val;
1014 pThis->vbe_regs[VBE_DISPI_INDEX_VIRT_WIDTH] = val;
1015 fRecalculate = true;
1016 }
1017 break;
1018 case VBE_DISPI_INDEX_YRES:
1019 if (val <= VBE_DISPI_MAX_YRES)
1020 pThis->vbe_regs[pThis->vbe_index] = val;
1021 break;
1022 case VBE_DISPI_INDEX_BPP:
1023 if (val == 0)
1024 val = 8;
1025 if (val == 4 || val == 8 || val == 15 ||
1026 val == 16 || val == 24 || val == 32) {
1027 pThis->vbe_regs[pThis->vbe_index] = val;
1028 fRecalculate = true;
1029 }
1030 break;
1031 case VBE_DISPI_INDEX_BANK:
1032 if (pThis->vbe_regs[VBE_DISPI_INDEX_BPP] <= 4)
1033 max_bank = pThis->vbe_bank_max >> 2; /* Each bank really covers 256K */
1034 else
1035 max_bank = pThis->vbe_bank_max;
1036 /* Old software may pass garbage in the high byte of bank. If the maximum
1037 * bank fits into a single byte, toss the high byte the user supplied.
1038 */
1039 if (max_bank < 0x100)
1040 val &= 0xff;
1041 if (val > max_bank)
1042 val = max_bank;
1043 pThis->vbe_regs[pThis->vbe_index] = val;
1044 pThis->bank_offset = (val << 16);
1045
1046# ifndef IN_RC
1047 /* The VGA region is (could be) affected by this change; reset all aliases we've created. */
1048 if (pThis->bmPageRemappedVGA != 0)
1049 {
1050 PDMDevHlpMmioResetRegion(pDevIns, pThis->hMmioLegacy);
1051 STAM_COUNTER_INC(&pThis->StatMapReset);
1052 vgaResetRemapped(pThis);
1053 }
1054# endif
1055 break;
1056
1057 case VBE_DISPI_INDEX_ENABLE:
1058# ifndef IN_RING3
1059 return VINF_IOM_R3_IOPORT_WRITE;
1060# else
1061 {
1062 if ((val & VBE_DISPI_ENABLED) &&
1063 !(pThis->vbe_regs[VBE_DISPI_INDEX_ENABLE] & VBE_DISPI_ENABLED)) {
1064 int h, shift_control;
1065 /* Check the values before we screw up with a resolution which is too big or small. */
1066 size_t cb = pThis->vbe_regs[VBE_DISPI_INDEX_XRES];
1067 if (pThis->vbe_regs[VBE_DISPI_INDEX_BPP] == 4)
1068 cb = pThis->vbe_regs[VBE_DISPI_INDEX_XRES] >> 1;
1069 else
1070 cb = pThis->vbe_regs[VBE_DISPI_INDEX_XRES] * ((pThis->vbe_regs[VBE_DISPI_INDEX_BPP] + 7) >> 3);
1071 cb *= pThis->vbe_regs[VBE_DISPI_INDEX_YRES];
1072 uint16_t cVirtWidth = pThis->vbe_regs[VBE_DISPI_INDEX_VIRT_WIDTH];
1073 if (!cVirtWidth)
1074 cVirtWidth = pThis->vbe_regs[VBE_DISPI_INDEX_XRES];
1075 if ( !cVirtWidth
1076 || !pThis->vbe_regs[VBE_DISPI_INDEX_YRES]
1077 || cb > pThis->vram_size)
1078 {
1079 AssertMsgFailed(("VIRT WIDTH=%d YRES=%d cb=%d vram_size=%d\n",
1080 pThis->vbe_regs[VBE_DISPI_INDEX_VIRT_WIDTH], pThis->vbe_regs[VBE_DISPI_INDEX_YRES], cb, pThis->vram_size));
1081 return VINF_SUCCESS; /* Note: silent failure like before */
1082 }
1083
1084 /* When VBE interface is enabled, it is reset. */
1085 pThis->vbe_regs[VBE_DISPI_INDEX_X_OFFSET] = 0;
1086 pThis->vbe_regs[VBE_DISPI_INDEX_Y_OFFSET] = 0;
1087 fRecalculate = true;
1088
1089 /* clear the screen (should be done in BIOS) */
1090 if (!(val & VBE_DISPI_NOCLEARMEM)) {
1091 uint16_t cY = RT_MIN(pThis->vbe_regs[VBE_DISPI_INDEX_YRES],
1092 pThis->vbe_regs[VBE_DISPI_INDEX_VIRT_HEIGHT]);
1093 uint16_t cbLinePitch = pThis->vbe_line_offset;
1094 memset(pThisCC->pbVRam, 0,
1095 cY * cbLinePitch);
1096 }
1097
1098 /* we initialize the VGA graphic mode (should be done
1099 in BIOS) */
1100 pThis->gr[0x06] = (pThis->gr[0x06] & ~0x0c) | 0x05; /* graphic mode + memory map 1 */
1101 pThis->cr[0x17] |= 3; /* no CGA modes */
1102 pThis->cr[0x13] = pThis->vbe_line_offset >> 3;
1103 /* width */
1104 pThis->cr[0x01] = (cVirtWidth >> 3) - 1;
1105 /* height (only meaningful if < 1024) */
1106 h = pThis->vbe_regs[VBE_DISPI_INDEX_YRES] - 1;
1107 pThis->cr[0x12] = h;
1108 pThis->cr[0x07] = (pThis->cr[0x07] & ~0x42) |
1109 ((h >> 7) & 0x02) | ((h >> 3) & 0x40);
1110 /* line compare to 1023 */
1111 pThis->cr[0x18] = 0xff;
1112 pThis->cr[0x07] |= 0x10;
1113 pThis->cr[0x09] |= 0x40;
1114
1115 if (pThis->vbe_regs[VBE_DISPI_INDEX_BPP] == 4) {
1116 shift_control = 0;
1117 pThis->sr[0x01] &= ~8; /* no double line */
1118 } else {
1119 shift_control = 2;
1120 pThis->sr[4] |= 0x08; /* set chain 4 mode */
1121 pThis->sr[2] |= 0x0f; /* activate all planes */
1122 /* Indicate non-VGA mode in SR07. */
1123 pThis->sr[7] |= 1;
1124 }
1125 pThis->gr[0x05] = (pThis->gr[0x05] & ~0x60) | (shift_control << 5);
1126 pThis->cr[0x09] &= ~0x9f; /* no double scan */
1127 /* sunlover 30.05.2007
1128 * The ar_index remains with bit 0x20 cleared after a switch from fullscreen
1129 * DOS mode on Windows XP guest. That leads to GMODE_BLANK in vgaR3UpdateDisplay.
1130 * But the VBE mode is graphics, so not a blank anymore.
1131 */
1132 pThis->ar_index |= 0x20;
1133 } else {
1134 /* XXX: the bios should do that */
1135 /* sunlover 21.12.2006
1136 * Here is probably more to reset. When this was executed in GC
1137 * then the *update* functions could not detect a mode change.
1138 * Or may be these update function should take the pThis->vbe_regs[pThis->vbe_index]
1139 * into account when detecting a mode change.
1140 *
1141 * The 'mode reset not detected' problem is now fixed by executing the
1142 * VBE_DISPI_INDEX_ENABLE case always in RING3 in order to call the
1143 * LFBChange callback.
1144 */
1145 pThis->bank_offset = 0;
1146 }
1147 pThis->vbe_regs[pThis->vbe_index] = val;
1148 /*
1149 * LFB video mode is either disabled or changed. Notify the display
1150 * and reset VBVA.
1151 */
1152 pThisCC->pDrv->pfnLFBModeChange(pThisCC->pDrv, (val & VBE_DISPI_ENABLED) != 0);
1153# ifdef VBOX_WITH_HGSMI
1154 VBVAOnVBEChanged(pThis, pThisCC);
1155# endif
1156
1157 /* The VGA region is (could be) affected by this change; reset all aliases we've created. */
1158 if (pThis->bmPageRemappedVGA != 0)
1159 {
1160 PDMDevHlpMmioResetRegion(pDevIns, pThis->hMmioLegacy);
1161 STAM_COUNTER_INC(&pThis->StatMapReset);
1162 vgaResetRemapped(pThis);
1163 }
1164 break;
1165 }
1166# endif /* IN_RING3 */
1167 case VBE_DISPI_INDEX_VIRT_WIDTH:
1168 case VBE_DISPI_INDEX_X_OFFSET:
1169 case VBE_DISPI_INDEX_Y_OFFSET:
1170 {
1171 pThis->vbe_regs[pThis->vbe_index] = val;
1172 fRecalculate = true;
1173 }
1174 break;
1175 case VBE_DISPI_INDEX_VBOX_VIDEO:
1176# ifndef IN_RING3
1177 return VINF_IOM_R3_IOPORT_WRITE;
1178# else
1179 /* Changes in the VGA device are minimal. The device is bypassed. The driver does all work. */
1180 if (val == VBOX_VIDEO_DISABLE_ADAPTER_MEMORY)
1181 pThisCC->pDrv->pfnProcessAdapterData(pThisCC->pDrv, NULL, 0);
1182 else if (val == VBOX_VIDEO_INTERPRET_ADAPTER_MEMORY)
1183 pThisCC->pDrv->pfnProcessAdapterData(pThisCC->pDrv, pThisCC->pbVRam, pThis->vram_size);
1184 else if ((val & 0xFFFF0000) == VBOX_VIDEO_INTERPRET_DISPLAY_MEMORY_BASE)
1185 pThisCC->pDrv->pfnProcessDisplayData(pThisCC->pDrv, pThisCC->pbVRam, val & 0xFFFF);
1186# endif /* IN_RING3 */
1187 break;
1188 case VBE_DISPI_INDEX_CFG:
1189 pThis->vbe_regs[pThis->vbe_index] = val;
1190 break;
1191 default:
1192 break;
1193 }
1194
1195 if (fRecalculate)
1196 recalculate_data(pThis);
1197 }
1198 return VINF_SUCCESS;
1199}
1200
1201#endif /* CONFIG_BOCHS_VBE */
1202
1203/* called for accesses between 0xa0000 and 0xc0000 */
1204static uint32_t vga_mem_readb(PPDMDEVINS pDevIns, PVGASTATE pThis, PVGASTATECC pThisCC, RTGCPHYS addr, int *prc)
1205{
1206 int plane;
1207 uint32_t ret;
1208
1209 Log3(("vga: read [0x%x] -> ", addr));
1210
1211#ifdef VMSVGA_WITH_VGA_FB_BACKUP_AND_IN_RZ
1212 /* VMSVGA keeps the VGA and SVGA framebuffers separate unlike this boch-based
1213 VGA implementation, so we fake it by going to ring-3 and using a heap buffer. */
1214 if (!pThis->svga.fEnabled)
1215 { /*likely*/ }
1216 else
1217 {
1218 *prc = VINF_IOM_R3_MMIO_READ;
1219 return 0;
1220 }
1221#endif
1222
1223
1224 /* convert to VGA memory offset */
1225 addr &= 0x1ffff;
1226#ifndef IN_RC
1227 RTGCPHYS const offMmio = addr; /* save original MMIO range offset */
1228#endif
1229
1230 int const memory_map_mode = (pThis->gr[6] >> 2) & 3;
1231 switch(memory_map_mode) {
1232 case 0:
1233 break;
1234 case 1:
1235 if (addr >= 0x10000)
1236 return 0xff;
1237 addr += pThis->bank_offset;
1238 break;
1239 case 2:
1240 addr -= 0x10000;
1241 if (addr >= 0x8000)
1242 return 0xff;
1243 break;
1244 default:
1245 case 3:
1246 addr -= 0x18000;
1247 if (addr >= 0x8000)
1248 return 0xff;
1249 break;
1250 }
1251
1252 if (pThis->sr[4] & 0x08) {
1253 /* chain 4 mode : simplest access */
1254#ifndef IN_RC
1255 /* If all planes are accessible, then map the page to the frame buffer and make it writable. */
1256 if ( (pThis->sr[2] & 3) == 3
1257 && !vgaIsRemapped(pThis, offMmio)
1258 && pThis->GCPhysVRAM)
1259 {
1260 /** @todo only allow read access (doesn't work now) */
1261 STAM_COUNTER_INC(&pThis->StatMapPage);
1262 PDMDevHlpMmioMapMmio2Page(pDevIns, pThis->hMmioLegacy, offMmio, pThis->hMmio2VRam, addr, X86_PTE_RW | X86_PTE_P);
1263 /* Set as dirty as write accesses won't be noticed now. */
1264 vgaR3MarkDirty(pThis, addr);
1265 vgaMarkRemapped(pThis, offMmio);
1266 }
1267#endif /* !IN_RC */
1268 VERIFY_VRAM_READ_OFF_RETURN(pThis, addr, *prc);
1269#ifdef VMSVGA_WITH_VGA_FB_BACKUP_AND_IN_RING3
1270 ret = !pThis->svga.fEnabled ? pThisCC->pbVRam[addr]
1271 : addr < VMSVGA_VGA_FB_BACKUP_SIZE ? pThisCC->svga.pbVgaFrameBufferR3[addr] : 0xff;
1272#else
1273 ret = pThisCC->pbVRam[addr];
1274#endif
1275 } else if (!(pThis->sr[4] & 0x04)) { /* Host access is controlled by SR4, not GR5! */
1276 /* odd/even mode (aka text mode mapping) */
1277 plane = (pThis->gr[4] & 2) | (addr & 1);
1278 /* See the comment for a similar line in vga_mem_writeb. */
1279 RTGCPHYS off = ((addr & ~1) * 4) | plane;
1280 VERIFY_VRAM_READ_OFF_RETURN(pThis, off, *prc);
1281#ifdef VMSVGA_WITH_VGA_FB_BACKUP_AND_IN_RING3
1282 ret = !pThis->svga.fEnabled ? pThisCC->pbVRam[off]
1283 : off < VMSVGA_VGA_FB_BACKUP_SIZE ? pThisCC->svga.pbVgaFrameBufferR3[off] : 0xff;
1284#else
1285 ret = pThisCC->pbVRam[off];
1286#endif
1287 } else {
1288 /* standard VGA latched access */
1289 VERIFY_VRAM_READ_OFF_RETURN(pThis, addr * 4 + 3, *prc);
1290#ifdef VMSVGA_WITH_VGA_FB_BACKUP_AND_IN_RING3
1291 pThis->latch = !pThis->svga.fEnabled ? ((uint32_t *)pThisCC->pbVRam)[addr]
1292 : addr * 4 + 3 < VMSVGA_VGA_FB_BACKUP_SIZE ? ((uint32_t *)pThisCC->svga.pbVgaFrameBufferR3)[addr] : UINT32_MAX;
1293#else
1294 pThis->latch = ((uint32_t *)pThisCC->pbVRam)[addr];
1295#endif
1296 if (!(pThis->gr[5] & 0x08)) {
1297 /* read mode 0 */
1298 plane = pThis->gr[4];
1299 ret = GET_PLANE(pThis->latch, plane);
1300 } else {
1301 /* read mode 1 */
1302 ret = (pThis->latch ^ mask16[pThis->gr[2]]) & mask16[pThis->gr[7]];
1303 ret |= ret >> 16;
1304 ret |= ret >> 8;
1305 ret = (~ret) & 0xff;
1306 }
1307 }
1308 Log3((" 0x%02x\n", ret));
1309 return ret;
1310}
1311
1312/**
1313 * called for accesses between 0xa0000 and 0xc0000
1314 */
1315static VBOXSTRICTRC vga_mem_writeb(PPDMDEVINS pDevIns, PVGASTATE pThis, PVGASTATECC pThisCC, RTGCPHYS addr, uint32_t val)
1316{
1317 int plane, write_mode, b, func_select, mask;
1318 uint32_t write_mask, bit_mask, set_mask;
1319
1320 Log3(("vga: [0x%x] = 0x%02x\n", addr, val));
1321
1322#ifdef VMSVGA_WITH_VGA_FB_BACKUP_AND_IN_RZ
1323 /* VMSVGA keeps the VGA and SVGA framebuffers separate unlike this boch-based
1324 VGA implementation, so we fake it by going to ring-3 and using a heap buffer. */
1325 if (!pThis->svga.fEnabled) { /*likely*/ }
1326 else return VINF_IOM_R3_MMIO_WRITE;
1327#endif
1328
1329 /* convert to VGA memory offset */
1330 addr &= 0x1ffff;
1331#ifndef IN_RC
1332 RTGCPHYS const offMmio = addr; /* save original MMIO range offset */
1333#endif
1334
1335 int const memory_map_mode = (pThis->gr[6] >> 2) & 3;
1336 switch(memory_map_mode) {
1337 case 0:
1338 break;
1339 case 1:
1340 if (addr >= 0x10000)
1341 return VINF_SUCCESS;
1342 addr += pThis->bank_offset;
1343 break;
1344 case 2:
1345 addr -= 0x10000;
1346 if (addr >= 0x8000)
1347 return VINF_SUCCESS;
1348 break;
1349 default:
1350 case 3:
1351 addr -= 0x18000;
1352 if (addr >= 0x8000)
1353 return VINF_SUCCESS;
1354 break;
1355 }
1356
1357 if (pThis->sr[4] & 0x08) {
1358 /* chain 4 mode : simplest access */
1359 plane = addr & 3;
1360 mask = (1 << plane);
1361 if (pThis->sr[2] & mask) {
1362#ifndef IN_RC
1363 /* If all planes are accessible, then map the page to the frame buffer and make it writable. */
1364 if ( (pThis->sr[2] & 3) == 3
1365 && !vgaIsRemapped(pThis, offMmio)
1366 && pThis->GCPhysVRAM)
1367 {
1368 STAM_COUNTER_INC(&pThis->StatMapPage);
1369 PDMDevHlpMmioMapMmio2Page(pDevIns, pThis->hMmioLegacy, offMmio, pThis->hMmio2VRam, addr, X86_PTE_RW | X86_PTE_P);
1370 vgaMarkRemapped(pThis, offMmio);
1371 }
1372#endif /* !IN_RC */
1373
1374 VERIFY_VRAM_WRITE_OFF_RETURN(pThis, addr);
1375#ifdef VMSVGA_WITH_VGA_FB_BACKUP_AND_IN_RING3
1376 if (!pThis->svga.fEnabled)
1377 pThisCC->pbVRam[addr] = val;
1378 else if (addr < VMSVGA_VGA_FB_BACKUP_SIZE)
1379 pThisCC->svga.pbVgaFrameBufferR3[addr] = val;
1380 else
1381 {
1382 Log(("vga: chain4: out of vmsvga VGA framebuffer bounds! addr=%#x\n", addr));
1383 return VINF_SUCCESS;
1384 }
1385#else
1386 pThisCC->pbVRam[addr] = val;
1387#endif
1388 Log3(("vga: chain4: [0x%x]\n", addr));
1389 pThis->plane_updated |= mask; /* only used to detect font change */
1390 vgaR3MarkDirty(pThis, addr);
1391 }
1392 } else if (!(pThis->sr[4] & 0x04)) { /* Host access is controlled by SR4, not GR5! */
1393 /* odd/even mode (aka text mode mapping); GR4 does not affect writes! */
1394 plane = addr & 1;
1395 mask = (1 << plane);
1396 if (pThis->sr[2] & mask) {
1397 /* 'addr' is offset in a plane, bit 0 selects the plane.
1398 * Mask the bit 0, convert plane index to vram offset,
1399 * that is multiply by the number of planes,
1400 * and select the plane byte in the vram offset.
1401 */
1402 addr = ((addr & ~1) * 4) | plane;
1403 VERIFY_VRAM_WRITE_OFF_RETURN(pThis, addr);
1404#ifdef VMSVGA_WITH_VGA_FB_BACKUP_AND_IN_RING3
1405 if (!pThis->svga.fEnabled)
1406 pThisCC->pbVRam[addr] = val;
1407 else if (addr < VMSVGA_VGA_FB_BACKUP_SIZE)
1408 pThisCC->svga.pbVgaFrameBufferR3[addr] = val;
1409 else
1410 {
1411 Log(("vga: odd/even: out of vmsvga VGA framebuffer bounds! addr=%#x\n", addr));
1412 return VINF_SUCCESS;
1413 }
1414#else
1415 pThisCC->pbVRam[addr] = val;
1416#endif
1417 Log3(("vga: odd/even: [0x%x]\n", addr));
1418 pThis->plane_updated |= mask; /* only used to detect font change */
1419 vgaR3MarkDirty(pThis, addr);
1420 }
1421 } else {
1422 /* standard VGA latched access */
1423 VERIFY_VRAM_WRITE_OFF_RETURN(pThis, addr * 4 + 3);
1424
1425 write_mode = pThis->gr[5] & 3;
1426 switch(write_mode) {
1427 default:
1428 case 0:
1429 /* rotate */
1430 b = pThis->gr[3] & 7;
1431 val = ((val >> b) | (val << (8 - b))) & 0xff;
1432 val |= val << 8;
1433 val |= val << 16;
1434
1435 /* apply set/reset mask */
1436 set_mask = mask16[pThis->gr[1]];
1437 val = (val & ~set_mask) | (mask16[pThis->gr[0]] & set_mask);
1438 bit_mask = pThis->gr[8];
1439 break;
1440 case 1:
1441 val = pThis->latch;
1442 goto do_write;
1443 case 2:
1444 val = mask16[val & 0x0f];
1445 bit_mask = pThis->gr[8];
1446 break;
1447 case 3:
1448 /* rotate */
1449 b = pThis->gr[3] & 7;
1450 val = (val >> b) | (val << (8 - b));
1451
1452 bit_mask = pThis->gr[8] & val;
1453 val = mask16[pThis->gr[0]];
1454 break;
1455 }
1456
1457 /* apply logical operation */
1458 func_select = pThis->gr[3] >> 3;
1459 switch(func_select) {
1460 case 0:
1461 default:
1462 /* nothing to do */
1463 break;
1464 case 1:
1465 /* and */
1466 val &= pThis->latch;
1467 break;
1468 case 2:
1469 /* or */
1470 val |= pThis->latch;
1471 break;
1472 case 3:
1473 /* xor */
1474 val ^= pThis->latch;
1475 break;
1476 }
1477
1478 /* apply bit mask */
1479 bit_mask |= bit_mask << 8;
1480 bit_mask |= bit_mask << 16;
1481 val = (val & bit_mask) | (pThis->latch & ~bit_mask);
1482
1483 do_write:
1484 /* mask data according to sr[2] */
1485 mask = pThis->sr[2];
1486 pThis->plane_updated |= mask; /* only used to detect font change */
1487 write_mask = mask16[mask];
1488#ifdef VMSVGA_WITH_VGA_FB_BACKUP_AND_IN_RING3
1489 uint32_t *pu32Dst;
1490 if (!pThis->svga.fEnabled)
1491 pu32Dst = &((uint32_t *)pThisCC->pbVRam)[addr];
1492 else if (addr * 4 + 3 < VMSVGA_VGA_FB_BACKUP_SIZE)
1493 pu32Dst = &((uint32_t *)pThisCC->svga.pbVgaFrameBufferR3)[addr];
1494 else
1495 {
1496 Log(("vga: latch: out of vmsvga VGA framebuffer bounds! addr=%#x\n", addr));
1497 return VINF_SUCCESS;
1498 }
1499 *pu32Dst = (*pu32Dst & ~write_mask) | (val & write_mask);
1500#else
1501 ((uint32_t *)pThisCC->pbVRam)[addr] = (((uint32_t *)pThisCC->pbVRam)[addr] & ~write_mask)
1502 | (val & write_mask);
1503#endif
1504 Log3(("vga: latch: [0x%x] mask=0x%08x val=0x%08x\n", addr * 4, write_mask, val));
1505 vgaR3MarkDirty(pThis, (addr * 4));
1506 }
1507
1508 return VINF_SUCCESS;
1509}
1510
1511#ifdef IN_RING3
1512
1513typedef void vga_draw_glyph8_func(uint8_t *d, int linesize,
1514 const uint8_t *font_ptr, int h,
1515 uint32_t fgcol, uint32_t bgcol,
1516 int dscan);
1517typedef void vga_draw_glyph9_func(uint8_t *d, int linesize,
1518 const uint8_t *font_ptr, int h,
1519 uint32_t fgcol, uint32_t bgcol, int dup9);
1520typedef void vga_draw_line_func(PVGASTATE pThis, PVGASTATECC pThisCC, uint8_t *pbDst, const uint8_t *pbSrc, int width);
1521
1522static inline unsigned int rgb_to_pixel8(unsigned int r, unsigned int g, unsigned b)
1523{
1524 return ((r >> 5) << 5) | ((g >> 5) << 2) | (b >> 6);
1525}
1526
1527static inline unsigned int rgb_to_pixel15(unsigned int r, unsigned int g, unsigned b)
1528{
1529 return ((r >> 3) << 10) | ((g >> 3) << 5) | (b >> 3);
1530}
1531
1532static inline unsigned int rgb_to_pixel16(unsigned int r, unsigned int g, unsigned b)
1533{
1534 return ((r >> 3) << 11) | ((g >> 2) << 5) | (b >> 3);
1535}
1536
1537static inline unsigned int rgb_to_pixel32(unsigned int r, unsigned int g, unsigned b)
1538{
1539 return (r << 16) | (g << 8) | b;
1540}
1541
1542#define DEPTH 8
1543#include "DevVGATmpl.h"
1544
1545#define DEPTH 15
1546#include "DevVGATmpl.h"
1547
1548#define DEPTH 16
1549#include "DevVGATmpl.h"
1550
1551#define DEPTH 32
1552#include "DevVGATmpl.h"
1553
1554static unsigned int rgb_to_pixel8_dup(unsigned int r, unsigned int g, unsigned b)
1555{
1556 unsigned int col;
1557 col = rgb_to_pixel8(r, g, b);
1558 col |= col << 8;
1559 col |= col << 16;
1560 return col;
1561}
1562
1563static unsigned int rgb_to_pixel15_dup(unsigned int r, unsigned int g, unsigned b)
1564{
1565 unsigned int col;
1566 col = rgb_to_pixel15(r, g, b);
1567 col |= col << 16;
1568 return col;
1569}
1570
1571static unsigned int rgb_to_pixel16_dup(unsigned int r, unsigned int g, unsigned b)
1572{
1573 unsigned int col;
1574 col = rgb_to_pixel16(r, g, b);
1575 col |= col << 16;
1576 return col;
1577}
1578
1579static unsigned int rgb_to_pixel32_dup(unsigned int r, unsigned int g, unsigned b)
1580{
1581 return rgb_to_pixel32(r, g, b);
1582}
1583
1584/** return true if the palette was modified */
1585static bool vgaR3UpdatePalette16(PVGASTATE pThis, PVGASTATER3 pThisCC)
1586{
1587 bool full_update = false;
1588 int i;
1589 uint32_t v, col, *palette;
1590
1591 palette = pThis->last_palette;
1592 for(i = 0; i < 16; i++) {
1593 v = pThis->ar[i];
1594 if (pThis->ar[0x10] & 0x80)
1595 v = ((pThis->ar[0x14] & 0xf) << 4) | (v & 0xf);
1596 else
1597 v = ((pThis->ar[0x14] & 0xc) << 4) | (v & 0x3f);
1598 v = v * 3;
1599 col = pThisCC->rgb_to_pixel(c6_to_8(pThis->palette[v]),
1600 c6_to_8(pThis->palette[v + 1]),
1601 c6_to_8(pThis->palette[v + 2]));
1602 if (col != palette[i]) {
1603 full_update = true;
1604 palette[i] = col;
1605 }
1606 }
1607 return full_update;
1608}
1609
1610/** return true if the palette was modified */
1611static bool vgaR3UpdatePalette256(PVGASTATE pThis, PVGASTATER3 pThisCC)
1612{
1613 bool full_update = false;
1614 int i;
1615 uint32_t v, col, *palette;
1616 int wide_dac;
1617
1618 palette = pThis->last_palette;
1619 v = 0;
1620 wide_dac = (pThis->vbe_regs[VBE_DISPI_INDEX_ENABLE] & (VBE_DISPI_ENABLED | VBE_DISPI_8BIT_DAC))
1621 == (VBE_DISPI_ENABLED | VBE_DISPI_8BIT_DAC);
1622 for(i = 0; i < 256; i++) {
1623 if (wide_dac)
1624 col = pThisCC->rgb_to_pixel(pThis->palette[v],
1625 pThis->palette[v + 1],
1626 pThis->palette[v + 2]);
1627 else
1628 col = pThisCC->rgb_to_pixel(c6_to_8(pThis->palette[v]),
1629 c6_to_8(pThis->palette[v + 1]),
1630 c6_to_8(pThis->palette[v + 2]));
1631 if (col != palette[i]) {
1632 full_update = true;
1633 palette[i] = col;
1634 }
1635 v += 3;
1636 }
1637 return full_update;
1638}
1639
1640static void vgaR3GetOffsets(PVGASTATE pThis,
1641 uint32_t *pline_offset,
1642 uint32_t *pstart_addr,
1643 uint32_t *pline_compare)
1644{
1645 uint32_t start_addr, line_offset, line_compare;
1646#ifdef CONFIG_BOCHS_VBE
1647 if (pThis->vbe_regs[VBE_DISPI_INDEX_ENABLE] & VBE_DISPI_ENABLED) {
1648 line_offset = pThis->vbe_line_offset;
1649 start_addr = pThis->vbe_start_addr;
1650 line_compare = 65535;
1651 } else
1652#endif
1653 {
1654 /* compute line_offset in bytes */
1655 line_offset = pThis->cr[0x13];
1656 line_offset <<= 3;
1657 if (!(pThis->cr[0x14] & 0x40) && !(pThis->cr[0x17] & 0x40))
1658 {
1659 /* Word mode. Used for odd/even modes. */
1660 line_offset *= 2;
1661 }
1662
1663 /* starting address */
1664 start_addr = pThis->cr[0x0d] | (pThis->cr[0x0c] << 8);
1665
1666 /* line compare */
1667 line_compare = pThis->cr[0x18] |
1668 ((pThis->cr[0x07] & 0x10) << 4) |
1669 ((pThis->cr[0x09] & 0x40) << 3);
1670 }
1671 *pline_offset = line_offset;
1672 *pstart_addr = start_addr;
1673 *pline_compare = line_compare;
1674}
1675
1676/** update start_addr and line_offset. Return TRUE if modified */
1677static bool vgaR3UpdateBasicParams(PVGASTATE pThis, PVGASTATER3 pThisCC)
1678{
1679 bool full_update = false;
1680 uint32_t start_addr, line_offset, line_compare;
1681
1682 pThisCC->get_offsets(pThis, &line_offset, &start_addr, &line_compare);
1683
1684 if (line_offset != pThis->line_offset ||
1685 start_addr != pThis->start_addr ||
1686 line_compare != pThis->line_compare) {
1687 pThis->line_offset = line_offset;
1688 pThis->start_addr = start_addr;
1689 pThis->line_compare = line_compare;
1690 full_update = true;
1691 }
1692 return full_update;
1693}
1694
1695static inline int vgaR3GetDepthIndex(int depth)
1696{
1697 switch(depth) {
1698 default:
1699 case 8:
1700 return 0;
1701 case 15:
1702 return 1;
1703 case 16:
1704 return 2;
1705 case 32:
1706 return 3;
1707 }
1708}
1709
1710static vga_draw_glyph8_func * const vga_draw_glyph8_table[4] = {
1711 vga_draw_glyph8_8,
1712 vga_draw_glyph8_16,
1713 vga_draw_glyph8_16,
1714 vga_draw_glyph8_32,
1715};
1716
1717static vga_draw_glyph8_func * const vga_draw_glyph16_table[4] = {
1718 vga_draw_glyph16_8,
1719 vga_draw_glyph16_16,
1720 vga_draw_glyph16_16,
1721 vga_draw_glyph16_32,
1722};
1723
1724static vga_draw_glyph9_func * const vga_draw_glyph9_table[4] = {
1725 vga_draw_glyph9_8,
1726 vga_draw_glyph9_16,
1727 vga_draw_glyph9_16,
1728 vga_draw_glyph9_32,
1729};
1730
1731static const uint8_t cursor_glyph[32 * 4] = {
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 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
1747 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
1748};
1749
1750static const uint8_t empty_glyph[32 * 4] = { 0 };
1751
1752/**
1753 * Text mode update
1754 */
1755static int vgaR3DrawText(PPDMDEVINS pDevIns, PVGASTATE pThis, PVGASTATER3 pThisCC, bool full_update,
1756 bool fFailOnResize, bool reset_dirty, PDMIDISPLAYCONNECTOR *pDrv)
1757{
1758 int cx, cy, cheight, cw, ch, cattr, height, width, ch_attr;
1759 int cx_min, cx_max, linesize, x_incr;
1760 int cx_min_upd, cx_max_upd, cy_start;
1761 uint32_t offset, fgcol, bgcol, v, cursor_offset;
1762 uint8_t *d1, *d, *src, *s1, *dest, *cursor_ptr;
1763 const uint8_t *font_ptr, *font_base[2];
1764 int dup9, line_offset, depth_index, dscan;
1765 uint32_t *palette;
1766 uint32_t *ch_attr_ptr;
1767 vga_draw_glyph8_func *vga_draw_glyph8;
1768 vga_draw_glyph9_func *vga_draw_glyph9;
1769 uint64_t time_ns;
1770 bool blink_on, chr_blink_flip, cur_blink_flip;
1771 bool blink_enabled, blink_do_redraw;
1772 int uline_pos;
1773 int s_incr;
1774
1775 full_update |= vgaR3UpdatePalette16(pThis, pThisCC);
1776 palette = pThis->last_palette;
1777
1778 /* compute font data address (in plane 2) */
1779 v = pThis->sr[3];
1780 offset = (((v >> 4) & 1) | ((v << 1) & 6)) * 8192 * 4 + 2;
1781 if (offset != pThis->font_offsets[0]) {
1782 pThis->font_offsets[0] = offset;
1783 full_update = true;
1784 }
1785 font_base[0] = pThisCC->pbVRam + offset;
1786
1787 offset = (((v >> 5) & 1) | ((v >> 1) & 6)) * 8192 * 4 + 2;
1788 font_base[1] = pThisCC->pbVRam + offset;
1789 if (offset != pThis->font_offsets[1]) {
1790 pThis->font_offsets[1] = offset;
1791 full_update = true;
1792 }
1793 if (pThis->plane_updated & (1 << 2)) {
1794 /* if the plane 2 was modified since the last display, it
1795 indicates the font may have been modified */
1796 pThis->plane_updated = 0;
1797 full_update = true;
1798 }
1799
1800 /* Underline position */
1801 uline_pos = pThis->cr[0x14] & 0x1f;
1802 if (uline_pos != pThis->last_uline) {
1803 pThis->last_uline = uline_pos;
1804 full_update = true;
1805 }
1806
1807 blink_enabled = !!(pThis->ar[0x10] & 0x08); /* Attribute controller blink enable. */
1808 if (blink_enabled != pThis->last_blink) {
1809 pThis->last_blink = blink_enabled;
1810 full_update = true;
1811 }
1812
1813 full_update |= vgaR3UpdateBasicParams(pThis, pThisCC);
1814
1815 /* Evaluate word/byte mode. Need to count by 4 because text is only in plane 0. */
1816 s_incr = pThis->cr[0x17] & 0x40 ? 4 : 8;
1817
1818 unsigned addr_mask;
1819 if (!(pThis->cr[0x17] & 0x40) && !(pThis->cr[0x17] & 0x20))
1820 addr_mask = 0xffff; /* Wrap at 64K, for CGA and 64K EGA compatibility. */
1821 else
1822 addr_mask = 0x3ffff; /* Wrap at 256K, standard VGA. */
1823
1824 line_offset = pThis->line_offset;
1825 s1 = pThisCC->pbVRam + ((pThis->start_addr * s_incr) & addr_mask);
1826
1827 /* double scanning - not for 9-wide modes */
1828 dscan = (pThis->cr[9] >> 7) & 1;
1829
1830 /* total width & height */
1831 cheight = (pThis->cr[9] & 0x1f) + 1;
1832 cw = 8;
1833 if (!(pThis->sr[1] & 0x01))
1834 cw = 9;
1835 if (pThis->sr[1] & 0x08)
1836 cw = 16; /* NOTE: no 18 pixel wide */
1837 x_incr = cw * ((pDrv->cBits + 7) >> 3);
1838 width = (pThis->cr[0x01] + 1);
1839 if (pThis->cr[0x06] == 100) {
1840 /* ugly hack for CGA 160x100x16 - explain me the logic */
1841 height = 100;
1842 } else {
1843 height = pThis->cr[0x12] |
1844 ((pThis->cr[0x07] & 0x02) << 7) |
1845 ((pThis->cr[0x07] & 0x40) << 3);
1846 height = (height + 1) / cheight;
1847 }
1848 /** @todo r=michaln This conditional is questionable; we should be able
1849 * to draw whatver the guest asks for. */
1850 if ((height * width) > CH_ATTR_SIZE) {
1851 /* better than nothing: exit if transient size is too big */
1852 return VINF_SUCCESS;
1853 }
1854
1855 if (width != (int)pThis->last_width || height != (int)pThis->last_height ||
1856 cw != pThis->last_cw || cheight != pThis->last_ch) {
1857 if (fFailOnResize)
1858 {
1859 /* The caller does not want to call the pfnResize. */
1860 return VERR_TRY_AGAIN;
1861 }
1862 pThis->last_scr_width = width * cw;
1863 pThis->last_scr_height = height * cheight;
1864 /* For text modes the direct use of guest VRAM is not implemented, so bpp and cbLine are 0 here. */
1865 int rc = pDrv->pfnResize(pDrv, 0, NULL, 0, pThis->last_scr_width, pThis->last_scr_height);
1866 pThis->last_width = width;
1867 pThis->last_height = height;
1868 pThis->last_ch = cheight;
1869 pThis->last_cw = cw;
1870 full_update = true;
1871 if (rc == VINF_VGA_RESIZE_IN_PROGRESS)
1872 return rc;
1873 AssertRC(rc);
1874 }
1875 cursor_offset = ((pThis->cr[0x0e] << 8) | pThis->cr[0x0f]) - pThis->start_addr;
1876 if (cursor_offset != pThis->cursor_offset ||
1877 pThis->cr[0xa] != pThis->cursor_start ||
1878 pThis->cr[0xb] != pThis->cursor_end) {
1879 /* if the cursor position changed, we update the old and new
1880 chars */
1881 if (pThis->cursor_offset < CH_ATTR_SIZE)
1882 pThis->last_ch_attr[pThis->cursor_offset] = UINT32_MAX;
1883 if (cursor_offset < CH_ATTR_SIZE)
1884 pThis->last_ch_attr[cursor_offset] = UINT32_MAX;
1885 pThis->cursor_offset = cursor_offset;
1886 pThis->cursor_start = pThis->cr[0xa];
1887 pThis->cursor_end = pThis->cr[0xb];
1888 }
1889 cursor_ptr = pThisCC->pbVRam + (((pThis->start_addr + cursor_offset) * s_incr) & addr_mask);
1890 depth_index = vgaR3GetDepthIndex(pDrv->cBits);
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 dest = pDrv->pbData;
1898 linesize = pDrv->cbScanline;
1899 ch_attr_ptr = pThis->last_ch_attr;
1900 cy_start = -1;
1901 cx_max_upd = -1;
1902 cx_min_upd = width;
1903
1904 /* Figure out if we're in the visible period of the blink cycle. */
1905 time_ns = PDMDevHlpTMTimeVirtGetNano(pDevIns);
1906 blink_on = (time_ns % VGA_BLINK_PERIOD_FULL) < VGA_BLINK_PERIOD_ON;
1907 chr_blink_flip = false;
1908 cur_blink_flip = false;
1909 if (pThis->last_chr_blink != blink_on)
1910 {
1911 /* Currently cursor and characters blink at the same rate, but they might not. */
1912 pThis->last_chr_blink = blink_on;
1913 pThis->last_cur_blink = blink_on;
1914 chr_blink_flip = true;
1915 cur_blink_flip = true;
1916 }
1917
1918 for(cy = 0; cy < (height - dscan); cy = cy + (1 << dscan)) {
1919 d1 = dest;
1920 src = s1;
1921 cx_min = width;
1922 cx_max = -1;
1923 for(cx = 0; cx < width; cx++) {
1924 ch_attr = *(uint16_t *)src;
1925 /* Figure out if character needs redrawing due to blink state change. */
1926 blink_do_redraw = blink_enabled && chr_blink_flip && (ch_attr & 0x8000);
1927 if (full_update || ch_attr != (int)*ch_attr_ptr || blink_do_redraw || (src == cursor_ptr && cur_blink_flip)) {
1928 if (cx < cx_min)
1929 cx_min = cx;
1930 if (cx > cx_max)
1931 cx_max = cx;
1932 if (reset_dirty)
1933 *ch_attr_ptr = ch_attr;
1934#ifdef WORDS_BIGENDIAN
1935 ch = ch_attr >> 8;
1936 cattr = ch_attr & 0xff;
1937#else
1938 ch = ch_attr & 0xff;
1939 cattr = ch_attr >> 8;
1940#endif
1941 font_ptr = font_base[(cattr >> 3) & 1];
1942 font_ptr += 32 * 4 * ch;
1943 bgcol = palette[cattr >> 4];
1944 fgcol = palette[cattr & 0x0f];
1945
1946 if (blink_enabled && (cattr & 0x80))
1947 {
1948 bgcol = palette[(cattr >> 4) & 7];
1949 if (!blink_on)
1950 font_ptr = empty_glyph;
1951 }
1952
1953 if (cw != 9) {
1954 if (pThis->fRenderVRAM)
1955 vga_draw_glyph8(d1, linesize, font_ptr, cheight, fgcol, bgcol, dscan);
1956 } else {
1957 dup9 = 0;
1958 if (ch >= 0xb0 && ch <= 0xdf && (pThis->ar[0x10] & 0x04))
1959 dup9 = 1;
1960 if (pThis->fRenderVRAM)
1961 vga_draw_glyph9(d1, linesize, font_ptr, cheight, fgcol, bgcol, dup9);
1962 }
1963
1964 /* Underline. Typically turned off by setting it past cell height. */
1965 if (((cattr & 0x03) == 1) && (uline_pos < cheight))
1966 {
1967 int h;
1968
1969 d = d1 + (linesize * uline_pos << dscan);
1970 h = 1;
1971
1972 if (cw != 9) {
1973 if (pThis->fRenderVRAM)
1974 vga_draw_glyph8(d, linesize, cursor_glyph, h, fgcol, bgcol, dscan);
1975 } else {
1976 if (pThis->fRenderVRAM)
1977 vga_draw_glyph9(d, linesize, cursor_glyph, h, fgcol, bgcol, 1);
1978 }
1979 }
1980
1981 /* Cursor. */
1982 if (src == cursor_ptr &&
1983 !(pThis->cr[0x0a] & 0x20)) {
1984 int line_start, line_last, h;
1985
1986 /* draw the cursor if within the visible period */
1987 if (blink_on) {
1988 line_start = pThis->cr[0x0a] & 0x1f;
1989 line_last = pThis->cr[0x0b] & 0x1f;
1990 /* XXX: check that */
1991 if (line_last > cheight - 1)
1992 line_last = cheight - 1;
1993 if (line_last >= line_start && line_start < cheight) {
1994 h = line_last - line_start + 1;
1995 d = d1 + (linesize * line_start << dscan);
1996 if (cw != 9) {
1997 if (pThis->fRenderVRAM)
1998 vga_draw_glyph8(d, linesize, cursor_glyph, h, fgcol, bgcol, dscan);
1999 } else {
2000 if (pThis->fRenderVRAM)
2001 vga_draw_glyph9(d, linesize, cursor_glyph, h, fgcol, bgcol, 1);
2002 }
2003 }
2004 }
2005 }
2006 }
2007 d1 += x_incr;
2008 src += s_incr; /* Even in text mode, word/byte mode matters. */
2009 if (src > (pThisCC->pbVRam + addr_mask))
2010 src = pThisCC->pbVRam;
2011 ch_attr_ptr++;
2012 }
2013 if (cx_max != -1) {
2014 /* Keep track of the bounding rectangle for updates. */
2015 if (cy_start == -1)
2016 cy_start = cy;
2017 if (cx_min_upd > cx_min)
2018 cx_min_upd = cx_min;
2019 if (cx_max_upd < cx_max)
2020 cx_max_upd = cx_max;
2021 } else if (cy_start >= 0) {
2022 /* Flush updates to display. */
2023 pDrv->pfnUpdateRect(pDrv, cx_min_upd * cw, cy_start * cheight,
2024 (cx_max_upd - cx_min_upd + 1) * cw, (cy - cy_start) * cheight);
2025 cy_start = -1;
2026 cx_max_upd = -1;
2027 cx_min_upd = width;
2028 }
2029
2030 dest += linesize * cheight << dscan;
2031 s1 += line_offset;
2032
2033 /* Line compare works in text modes, too. */
2034 /** @todo r=michaln This is inaccurate; text should be rendered line by line
2035 * and line compare checked after every line. */
2036 if ((uint32_t)cy == (pThis->line_compare / cheight))
2037 s1 = pThisCC->pbVRam;
2038
2039 if (s1 > (pThisCC->pbVRam + addr_mask))
2040 s1 = s1 - (addr_mask + 1);
2041 }
2042 if (cy_start >= 0)
2043 /* Flush any remaining changes to display. */
2044 pDrv->pfnUpdateRect(pDrv, cx_min_upd * cw, cy_start * cheight,
2045 (cx_max_upd - cx_min_upd + 1) * cw, (cy - cy_start) * cheight);
2046 return VINF_SUCCESS;
2047}
2048
2049enum {
2050 VGA_DRAW_LINE2,
2051 VGA_DRAW_LINE2D2,
2052 VGA_DRAW_LINE4,
2053 VGA_DRAW_LINE4D2,
2054 VGA_DRAW_LINE8D2,
2055 VGA_DRAW_LINE8,
2056 VGA_DRAW_LINE15,
2057 VGA_DRAW_LINE16,
2058 VGA_DRAW_LINE24,
2059 VGA_DRAW_LINE32,
2060 VGA_DRAW_LINE_NB
2061};
2062
2063static vga_draw_line_func * const vga_draw_line_table[4 * VGA_DRAW_LINE_NB] = {
2064 vga_draw_line2_8,
2065 vga_draw_line2_16,
2066 vga_draw_line2_16,
2067 vga_draw_line2_32,
2068
2069 vga_draw_line2d2_8,
2070 vga_draw_line2d2_16,
2071 vga_draw_line2d2_16,
2072 vga_draw_line2d2_32,
2073
2074 vga_draw_line4_8,
2075 vga_draw_line4_16,
2076 vga_draw_line4_16,
2077 vga_draw_line4_32,
2078
2079 vga_draw_line4d2_8,
2080 vga_draw_line4d2_16,
2081 vga_draw_line4d2_16,
2082 vga_draw_line4d2_32,
2083
2084 vga_draw_line8d2_8,
2085 vga_draw_line8d2_16,
2086 vga_draw_line8d2_16,
2087 vga_draw_line8d2_32,
2088
2089 vga_draw_line8_8,
2090 vga_draw_line8_16,
2091 vga_draw_line8_16,
2092 vga_draw_line8_32,
2093
2094 vga_draw_line15_8,
2095 vga_draw_line15_15,
2096 vga_draw_line15_16,
2097 vga_draw_line15_32,
2098
2099 vga_draw_line16_8,
2100 vga_draw_line16_15,
2101 vga_draw_line16_16,
2102 vga_draw_line16_32,
2103
2104 vga_draw_line24_8,
2105 vga_draw_line24_15,
2106 vga_draw_line24_16,
2107 vga_draw_line24_32,
2108
2109 vga_draw_line32_8,
2110 vga_draw_line32_15,
2111 vga_draw_line32_16,
2112 vga_draw_line32_32,
2113};
2114
2115static int vgaR3GetBpp(PVGASTATE pThis)
2116{
2117 int ret;
2118#ifdef CONFIG_BOCHS_VBE
2119 if (pThis->vbe_regs[VBE_DISPI_INDEX_ENABLE] & VBE_DISPI_ENABLED) {
2120 ret = pThis->vbe_regs[VBE_DISPI_INDEX_BPP];
2121 } else
2122#endif
2123 {
2124 ret = 0;
2125 }
2126 return ret;
2127}
2128
2129static void vgaR3GetResolution(PVGASTATE pThis, int *pwidth, int *pheight)
2130{
2131 int width, height;
2132#ifdef CONFIG_BOCHS_VBE
2133 if (pThis->vbe_regs[VBE_DISPI_INDEX_ENABLE] & VBE_DISPI_ENABLED) {
2134 width = pThis->vbe_regs[VBE_DISPI_INDEX_XRES];
2135 height = RT_MIN(pThis->vbe_regs[VBE_DISPI_INDEX_YRES],
2136 pThis->vbe_regs[VBE_DISPI_INDEX_VIRT_HEIGHT]);
2137 } else
2138#endif
2139 {
2140 width = (pThis->cr[0x01] + 1) * 8;
2141 height = pThis->cr[0x12] |
2142 ((pThis->cr[0x07] & 0x02) << 7) |
2143 ((pThis->cr[0x07] & 0x40) << 3);
2144 height = (height + 1);
2145 }
2146 *pwidth = width;
2147 *pheight = height;
2148}
2149
2150
2151/**
2152 * Performs the display driver resizing when in graphics mode.
2153 *
2154 * This will recalc / update any status data depending on the driver
2155 * properties (bit depth mostly).
2156 *
2157 * @returns VINF_SUCCESS on success.
2158 * @returns VINF_VGA_RESIZE_IN_PROGRESS if the operation wasn't complete.
2159 * @param pThis Pointer to the shared VGA state.
2160 * @param pThisCC Pointer to the ring-3 VGA state.
2161 * @param cx The width.
2162 * @param cy The height.
2163 * @param pDrv The display connector.
2164 */
2165static int vgaR3ResizeGraphic(PVGASTATE pThis, PVGASTATER3 pThisCC, int cx, int cy, PDMIDISPLAYCONNECTOR *pDrv)
2166{
2167 const unsigned cBits = pThisCC->get_bpp(pThis);
2168
2169 int rc;
2170 AssertReturn(cx, VERR_INVALID_PARAMETER);
2171 AssertReturn(cy, VERR_INVALID_PARAMETER);
2172 AssertPtrReturn(pThis, VERR_INVALID_POINTER);
2173
2174 if (!pThis->line_offset)
2175 return VERR_INTERNAL_ERROR;
2176
2177#if 0 //def VBOX_WITH_VDMA
2178 /** @todo we get a second resize here when VBVA is on, while we actually should not */
2179 /* do not do pfnResize in case VBVA is on since all mode changes are performed over VBVA
2180 * we are checking for VDMA state here to ensure this code works only for WDDM driver,
2181 * although we should avoid calling pfnResize for XPDM as well, since pfnResize is actually an extra resize
2182 * event and generally only pfnVBVAxxx calls should be used with HGSMI + VBVA
2183 *
2184 * The reason for doing this for WDDM driver only now is to avoid regressions of the current code */
2185 PVBOXVDMAHOST pVdma = pThisCC->pVdma;
2186 if (pVdma && vboxVDMAIsEnabled(pVdma))
2187 rc = VINF_SUCCESS;
2188 else
2189#endif
2190 {
2191 /* Skip the resize if the values are not valid. */
2192 if (pThis->start_addr * 4 + pThis->line_offset * cy < pThis->vram_size)
2193 /* Take into account the programmed start address (in DWORDs) of the visible screen. */
2194 rc = pDrv->pfnResize(pDrv, cBits, pThisCC->pbVRam + pThis->start_addr * 4, pThis->line_offset, cx, cy);
2195 else
2196 {
2197 /* Change nothing in the VGA state. Lets hope the guest will eventually programm correct values. */
2198 return VERR_TRY_AGAIN;
2199 }
2200 }
2201
2202 /* last stuff */
2203 pThis->last_bpp = cBits;
2204 pThis->last_scr_width = cx;
2205 pThis->last_scr_height = cy;
2206 pThis->last_width = cx;
2207 pThis->last_height = cy;
2208
2209 if (rc == VINF_VGA_RESIZE_IN_PROGRESS)
2210 return rc;
2211 AssertRC(rc);
2212
2213 /* update palette */
2214 switch (pDrv->cBits)
2215 {
2216 case 32: pThisCC->rgb_to_pixel = rgb_to_pixel32_dup; break;
2217 case 16:
2218 default: pThisCC->rgb_to_pixel = rgb_to_pixel16_dup; break;
2219 case 15: pThisCC->rgb_to_pixel = rgb_to_pixel15_dup; break;
2220 case 8: pThisCC->rgb_to_pixel = rgb_to_pixel8_dup; break;
2221 }
2222 if (pThis->shift_control == 0)
2223 vgaR3UpdatePalette16(pThis, pThisCC);
2224 else if (pThis->shift_control == 1)
2225 vgaR3UpdatePalette16(pThis, pThisCC);
2226 return VINF_SUCCESS;
2227}
2228
2229# ifdef VBOX_WITH_VMSVGA
2230
2231# if 0 /* unused? */
2232int vgaR3UpdateDisplay(PVGASTATE pThis, PVGASTATER3 pThisCC, unsigned xStart, unsigned yStart, unsigned cx, unsigned cy, PDMIDISPLAYCONNECTOR *pDrv)
2233{
2234 uint32_t v;
2235 vga_draw_line_func *vga_draw_line;
2236
2237 if (!pThis->fRenderVRAM)
2238 {
2239 pDrv->pfnUpdateRect(pDrv, xStart, yStart, cx, cy);
2240 return VINF_SUCCESS;
2241 }
2242 /** @todo might crash if a blit follows a resolution change very quickly (seen this many times!) */
2243
2244 if ( pThis->svga.uWidth == VMSVGA_VAL_UNINITIALIZED
2245 || pThis->svga.uHeight == VMSVGA_VAL_UNINITIALIZED
2246 || pThis->svga.uBpp == VMSVGA_VAL_UNINITIALIZED)
2247 {
2248 /* Intermediate state; skip redraws. */
2249 AssertFailed();
2250 return VINF_SUCCESS;
2251 }
2252
2253 uint32_t cBits;
2254 switch (pThis->svga.uBpp) {
2255 default:
2256 case 0:
2257 case 8:
2258 AssertFailed();
2259 return VERR_NOT_IMPLEMENTED;
2260 case 15:
2261 v = VGA_DRAW_LINE15;
2262 cBits = 16;
2263 break;
2264 case 16:
2265 v = VGA_DRAW_LINE16;
2266 cBits = 16;
2267 break;
2268 case 24:
2269 v = VGA_DRAW_LINE24;
2270 cBits = 24;
2271 break;
2272 case 32:
2273 v = VGA_DRAW_LINE32;
2274 cBits = 32;
2275 break;
2276 }
2277 vga_draw_line = vga_draw_line_table[v * 4 + vgaR3GetDepthIndex(pDrv->cBits)];
2278
2279 uint32_t offSrc = (xStart * cBits) / 8 + pThis->svga.cbScanline * yStart;
2280 uint32_t offDst = (xStart * RT_ALIGN(pDrv->cBits, 8)) / 8 + pDrv->cbScanline * yStart;
2281
2282 uint8_t *pbDst = pDrv->pbData + offDst;
2283 uint8_t const *pbSrc = pThisCC->pbVRam + offSrc;
2284
2285 for (unsigned y = yStart; y < yStart + cy; y++)
2286 {
2287 vga_draw_line(pThis, pThisCC, pbDst, pbSrc, cx);
2288
2289 pbDst += pDrv->cbScanline;
2290 pbSrc += pThis->svga.cbScanline;
2291 }
2292 pDrv->pfnUpdateRect(pDrv, xStart, yStart, cx, cy);
2293
2294 return VINF_SUCCESS;
2295}
2296# endif
2297
2298/**
2299 * graphic modes
2300 */
2301static int vmsvgaR3DrawGraphic(PVGASTATE pThis, PVGASTATER3 pThisCC, bool fFullUpdate,
2302 bool fFailOnResize, bool reset_dirty, PDMIDISPLAYCONNECTOR *pDrv)
2303{
2304 RT_NOREF1(fFailOnResize);
2305
2306 uint32_t const cx = pThis->last_scr_width;
2307 uint32_t const cxDisplay = cx;
2308 uint32_t const cy = pThis->last_scr_height;
2309 uint32_t cBits = pThis->last_bpp;
2310
2311 if ( cx == VMSVGA_VAL_UNINITIALIZED
2312 || cx == 0
2313 || cy == VMSVGA_VAL_UNINITIALIZED
2314 || cy == 0
2315 || cBits == VMSVGA_VAL_UNINITIALIZED
2316 || cBits == 0)
2317 {
2318 /* Intermediate state; skip redraws. */
2319 return VINF_SUCCESS;
2320 }
2321
2322 unsigned v;
2323 switch (cBits)
2324 {
2325 case 8:
2326 /* Note! experimental, not sure if this really works... */
2327 /** @todo fFullUpdate |= vgaR3UpdatePalette256(pThis); - need fFullUpdate but not
2328 * copying anything to last_palette. */
2329 v = VGA_DRAW_LINE8;
2330 break;
2331 case 15:
2332 v = VGA_DRAW_LINE15;
2333 cBits = 16;
2334 break;
2335 case 16:
2336 v = VGA_DRAW_LINE16;
2337 break;
2338 case 24:
2339 v = VGA_DRAW_LINE24;
2340 break;
2341 case 32:
2342 v = VGA_DRAW_LINE32;
2343 break;
2344 default:
2345 case 0:
2346 AssertFailed();
2347 return VERR_NOT_IMPLEMENTED;
2348 }
2349 vga_draw_line_func *pfnVgaDrawLine = vga_draw_line_table[v * 4 + vgaR3GetDepthIndex(pDrv->cBits)];
2350
2351 Assert(!pThisCC->cursor_invalidate);
2352 Assert(!pThisCC->cursor_draw_line);
2353 //not used// if (pThisCC->cursor_invalidate)
2354 //not used// pThisCC->cursor_invalidate(pThis);
2355
2356 uint8_t *pbDst = pDrv->pbData;
2357 uint32_t cbDstScanline = pDrv->cbScanline;
2358 uint32_t offSrcStart = 0; /* always start at the beginning of the framebuffer */
2359 uint32_t cbScanline = (cx * cBits + 7) / 8; /* The visible width of a scanline. */
2360 uint32_t yUpdateRectTop = UINT32_MAX;
2361 uint32_t offPageMin = UINT32_MAX;
2362 int32_t offPageMax = -1;
2363 uint32_t y;
2364 for (y = 0; y < cy; y++)
2365 {
2366 uint32_t offSrcLine = offSrcStart + y * cbScanline;
2367 uint32_t offPage0 = offSrcLine & ~(uint32_t)GUEST_PAGE_OFFSET_MASK;
2368 uint32_t offPage1 = (offSrcLine + cbScanline - 1) & ~(uint32_t)GUEST_PAGE_OFFSET_MASK;
2369 /** @todo r=klaus this assumes that a line is fully covered by 3 pages,
2370 * irrespective of alignment. Not guaranteed for high res modes, i.e.
2371 * anything wider than 2050 pixels @32bpp. Need to check all pages
2372 * between the first and last one. */
2373 bool fUpdate = fFullUpdate || vgaR3IsDirty(pThis, offPage0) || vgaR3IsDirty(pThis, offPage1);
2374 if (offPage1 - offPage0 > GUEST_PAGE_SIZE)
2375 /* if wide line, can use another page */
2376 fUpdate |= vgaR3IsDirty(pThis, offPage0 + GUEST_PAGE_SIZE);
2377 /* explicit invalidation for the hardware cursor */
2378 fUpdate |= (pThis->invalidated_y_table[y >> 5] >> (y & 0x1f)) & 1;
2379 if (fUpdate)
2380 {
2381 if (yUpdateRectTop == UINT32_MAX)
2382 yUpdateRectTop = y;
2383 if (offPage0 < offPageMin)
2384 offPageMin = offPage0;
2385 if ((int32_t)offPage1 > offPageMax)
2386 offPageMax = offPage1;
2387 if (pThis->fRenderVRAM)
2388 pfnVgaDrawLine(pThis, pThisCC, pbDst, pThisCC->pbVRam + offSrcLine, cx);
2389 //not used// if (pThisCC->cursor_draw_line)
2390 //not used// pThisCC->cursor_draw_line(pThis, pbDst, y);
2391 }
2392 else if (yUpdateRectTop != UINT32_MAX)
2393 {
2394 /* flush to display */
2395 Log(("Flush to display (%d,%d)(%d,%d)\n", 0, yUpdateRectTop, cxDisplay, y - yUpdateRectTop));
2396 pDrv->pfnUpdateRect(pDrv, 0, yUpdateRectTop, cxDisplay, y - yUpdateRectTop);
2397 yUpdateRectTop = UINT32_MAX;
2398 }
2399 pbDst += cbDstScanline;
2400 }
2401 if (yUpdateRectTop != UINT32_MAX)
2402 {
2403 /* flush to display */
2404 Log(("Flush to display (%d,%d)(%d,%d)\n", 0, yUpdateRectTop, cxDisplay, y - yUpdateRectTop));
2405 pDrv->pfnUpdateRect(pDrv, 0, yUpdateRectTop, cxDisplay, y - yUpdateRectTop);
2406 }
2407
2408 /* reset modified pages */
2409 if (offPageMax != -1 && reset_dirty)
2410 vgaR3ResetDirty(pThis, offPageMin, offPageMax + GUEST_PAGE_SIZE);
2411 memset(pThis->invalidated_y_table, 0, ((cy + 31) >> 5) * 4);
2412
2413 return VINF_SUCCESS;
2414}
2415
2416# endif /* VBOX_WITH_VMSVGA */
2417
2418/**
2419 * graphic modes
2420 */
2421static int vgaR3DrawGraphic(PVGASTATE pThis, PVGASTATER3 pThisCC, bool full_update, bool fFailOnResize, bool reset_dirty,
2422 PDMIDISPLAYCONNECTOR *pDrv)
2423{
2424 int y1, y2, y, page_min, page_max, linesize, y_start, double_scan;
2425 int width, height, shift_control, line_offset, page0, page1, bwidth, bits;
2426 int disp_width, multi_run;
2427 uint8_t *d;
2428 uint32_t v, addr1, addr;
2429 vga_draw_line_func *pfnVgaDrawLine;
2430
2431 bool offsets_changed = vgaR3UpdateBasicParams(pThis, pThisCC);
2432
2433 full_update |= offsets_changed;
2434
2435 pThisCC->get_resolution(pThis, &width, &height);
2436 disp_width = width;
2437
2438 shift_control = (pThis->gr[0x05] >> 5) & 3;
2439 double_scan = (pThis->cr[0x09] >> 7);
2440 multi_run = double_scan;
2441 if (shift_control != pThis->shift_control ||
2442 double_scan != pThis->double_scan) {
2443 full_update = true;
2444 pThis->shift_control = shift_control;
2445 pThis->double_scan = double_scan;
2446 }
2447
2448 if (shift_control == 0) {
2449 full_update |= vgaR3UpdatePalette16(pThis, pThisCC);
2450 if (pThis->sr[0x01] & 8) {
2451 v = VGA_DRAW_LINE4D2;
2452 disp_width <<= 1;
2453 } else {
2454 v = VGA_DRAW_LINE4;
2455 }
2456 bits = 4;
2457 } else if (shift_control == 1) {
2458 full_update |= vgaR3UpdatePalette16(pThis, pThisCC);
2459 if (pThis->sr[0x01] & 8) {
2460 v = VGA_DRAW_LINE2D2;
2461 disp_width <<= 1;
2462 } else {
2463 v = VGA_DRAW_LINE2;
2464 }
2465 bits = 4;
2466 } else {
2467 switch(pThisCC->get_bpp(pThis)) {
2468 default:
2469 case 0:
2470 full_update |= vgaR3UpdatePalette256(pThis, pThisCC);
2471 v = VGA_DRAW_LINE8D2;
2472 bits = 4;
2473 break;
2474 case 8:
2475 full_update |= vgaR3UpdatePalette256(pThis, pThisCC);
2476 v = VGA_DRAW_LINE8;
2477 bits = 8;
2478 break;
2479 case 15:
2480 v = VGA_DRAW_LINE15;
2481 bits = 16;
2482 break;
2483 case 16:
2484 v = VGA_DRAW_LINE16;
2485 bits = 16;
2486 break;
2487 case 24:
2488 v = VGA_DRAW_LINE24;
2489 bits = 24;
2490 break;
2491 case 32:
2492 v = VGA_DRAW_LINE32;
2493 bits = 32;
2494 break;
2495 }
2496 }
2497 if ( disp_width != (int)pThis->last_width
2498 || height != (int)pThis->last_height
2499 || pThisCC->get_bpp(pThis) != (int)pThis->last_bpp
2500 || (offsets_changed && !pThis->fRenderVRAM))
2501 {
2502 if (fFailOnResize)
2503 {
2504 /* The caller does not want to call the pfnResize. */
2505 return VERR_TRY_AGAIN;
2506 }
2507 int rc = vgaR3ResizeGraphic(pThis, pThisCC, disp_width, height, pDrv);
2508 if (rc != VINF_SUCCESS) /* Return any rc, particularly VINF_VGA_RESIZE_IN_PROGRESS, to the caller. */
2509 return rc;
2510 full_update = true;
2511 }
2512
2513 if (pThis->fRenderVRAM)
2514 {
2515 /* Do not update the destination buffer if it is not big enough.
2516 * Can happen if the resize request was ignored by the driver.
2517 * Compare with 'disp_width', because it is what the framebuffer has been resized to.
2518 */
2519 if ( pDrv->cx != (uint32_t)disp_width
2520 || pDrv->cy != (uint32_t)height)
2521 {
2522 LogRel(("Framebuffer mismatch: vga %dx%d, drv %dx%d!!!\n",
2523 disp_width, height,
2524 pDrv->cx, pDrv->cy));
2525 return VINF_SUCCESS;
2526 }
2527 }
2528
2529 pfnVgaDrawLine = vga_draw_line_table[v * 4 + vgaR3GetDepthIndex(pDrv->cBits)];
2530
2531 if (pThisCC->cursor_invalidate)
2532 pThisCC->cursor_invalidate(pThis);
2533
2534 line_offset = pThis->line_offset;
2535#if 0
2536 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",
2537 width, height, v, line_offset, pThis->cr[9], pThis->cr[0x17], pThis->line_compare, pThis->sr[0x01]));
2538#endif
2539 addr1 = (pThis->start_addr * 4);
2540 bwidth = (width * bits + 7) / 8; /* The visible width of a scanline. */
2541 y_start = -1;
2542 page_min = 0x7fffffff;
2543 page_max = -1;
2544 d = pDrv->pbData;
2545 linesize = pDrv->cbScanline;
2546
2547 if (!(pThis->vbe_regs[VBE_DISPI_INDEX_ENABLE] & VBE_DISPI_ENABLED))
2548 pThis->vga_addr_mask = 0x3ffff;
2549 else
2550 pThis->vga_addr_mask = UINT32_MAX;
2551
2552 y1 = 0;
2553 y2 = pThis->cr[0x09] & 0x1F; /* starting row scan count */
2554 for(y = 0; y < height; y++) {
2555 addr = addr1;
2556 /* CGA/MDA compatibility. Note that these addresses are all
2557 * shifted left by two compared to VGA specs.
2558 */
2559 if (!(pThis->cr[0x17] & 1)) {
2560 addr = (addr & ~(1 << 15)) | ((y1 & 1) << 15);
2561 }
2562 if (!(pThis->cr[0x17] & 2)) {
2563 addr = (addr & ~(1 << 16)) | ((y1 & 2) << 15);
2564 }
2565 addr &= pThis->vga_addr_mask;
2566 page0 = addr & ~(uint32_t)GUEST_PAGE_OFFSET_MASK;
2567 page1 = (addr + bwidth - 1) & ~(uint32_t)GUEST_PAGE_OFFSET_MASK;
2568 /** @todo r=klaus this assumes that a line is fully covered by 3 pages,
2569 * irrespective of alignment. Not guaranteed for high res modes, i.e.
2570 * anything wider than 2050 pixels @32bpp. Need to check all pages
2571 * between the first and last one. */
2572 bool update = full_update || vgaR3IsDirty(pThis, page0) || vgaR3IsDirty(pThis, page1);
2573 if (page1 - page0 > GUEST_PAGE_SIZE) {
2574 /* if wide line, can use another page */
2575 update |= vgaR3IsDirty(pThis, page0 + GUEST_PAGE_SIZE);
2576 }
2577 /* explicit invalidation for the hardware cursor */
2578 update |= (pThis->invalidated_y_table[y >> 5] >> (y & 0x1f)) & 1;
2579 if (update) {
2580 if (y_start < 0)
2581 y_start = y;
2582 if (page0 < page_min)
2583 page_min = page0;
2584 if (page1 > page_max)
2585 page_max = page1;
2586 if (pThis->fRenderVRAM)
2587 pfnVgaDrawLine(pThis, pThisCC, d, pThisCC->pbVRam + addr, width);
2588 if (pThisCC->cursor_draw_line)
2589 pThisCC->cursor_draw_line(pThis, d, y);
2590 } else {
2591 if (y_start >= 0) {
2592 /* flush to display */
2593 pDrv->pfnUpdateRect(pDrv, 0, y_start, disp_width, y - y_start);
2594 y_start = -1;
2595 }
2596 }
2597 if (!multi_run) {
2598 y1++;
2599 multi_run = double_scan;
2600
2601 if (y2 == 0) {
2602 y2 = pThis->cr[0x09] & 0x1F;
2603 addr1 += line_offset;
2604 } else {
2605 --y2;
2606 }
2607 } else {
2608 multi_run--;
2609 }
2610 /* line compare acts on the displayed lines */
2611 if ((uint32_t)y == pThis->line_compare)
2612 addr1 = 0;
2613 d += linesize;
2614 }
2615 if (y_start >= 0) {
2616 /* flush to display */
2617 pDrv->pfnUpdateRect(pDrv, 0, y_start, disp_width, y - y_start);
2618 }
2619 /* reset modified pages */
2620 if (page_max != -1 && reset_dirty) {
2621 vgaR3ResetDirty(pThis, page_min, page_max + GUEST_PAGE_SIZE);
2622 }
2623 memset(pThis->invalidated_y_table, 0, ((height + 31) >> 5) * 4);
2624 return VINF_SUCCESS;
2625}
2626
2627/**
2628 * blanked modes
2629 */
2630static int vgaR3DrawBlank(PVGASTATE pThis, PVGASTATER3 pThisCC, bool full_update,
2631 bool fFailOnResize, bool reset_dirty, PDMIDISPLAYCONNECTOR *pDrv)
2632{
2633 int i, w, val;
2634 uint8_t *d;
2635 uint32_t cbScanline = pDrv->cbScanline;
2636 uint32_t page_min, page_max;
2637
2638 if (pThis->last_width != 0)
2639 {
2640 if (fFailOnResize)
2641 {
2642 /* The caller does not want to call the pfnResize. */
2643 return VERR_TRY_AGAIN;
2644 }
2645 pThis->last_width = 0;
2646 pThis->last_height = 0;
2647 /* For blanking signal width=0, height=0, bpp=0 and cbLine=0 here.
2648 * There is no screen content, which distinguishes it from text mode. */
2649 pDrv->pfnResize(pDrv, 0, NULL, 0, 0, 0);
2650 }
2651 /* reset modified pages, i.e. everything */
2652 if (reset_dirty && pThis->last_scr_height > 0)
2653 {
2654 page_min = (pThis->start_addr * 4) & ~(uint32_t)GUEST_PAGE_OFFSET_MASK;
2655 /* round up page_max by one page, as otherwise this can be -GUEST_PAGE_SIZE,
2656 * which causes assertion trouble in vgaR3ResetDirty. */
2657 page_max = (pThis->start_addr * 4 + pThis->line_offset * pThis->last_scr_height - 1 + GUEST_PAGE_SIZE)
2658 & ~(uint32_t)GUEST_PAGE_OFFSET_MASK;
2659 vgaR3ResetDirty(pThis, page_min, page_max + GUEST_PAGE_SIZE);
2660 }
2661 if (pDrv->pbData == pThisCC->pbVRam) /* Do not clear the VRAM itself. */
2662 return VINF_SUCCESS;
2663 if (!full_update)
2664 return VINF_SUCCESS;
2665 if (pThis->last_scr_width <= 0 || pThis->last_scr_height <= 0)
2666 return VINF_SUCCESS;
2667 if (pDrv->cBits == 8)
2668 val = pThisCC->rgb_to_pixel(0, 0, 0);
2669 else
2670 val = 0;
2671 w = pThis->last_scr_width * ((pDrv->cBits + 7) >> 3);
2672 d = pDrv->pbData;
2673 if (pThis->fRenderVRAM)
2674 {
2675 for(i = 0; i < (int)pThis->last_scr_height; i++) {
2676 memset(d, val, w);
2677 d += cbScanline;
2678 }
2679 }
2680 pDrv->pfnUpdateRect(pDrv, 0, 0, pThis->last_scr_width, pThis->last_scr_height);
2681 return VINF_SUCCESS;
2682}
2683
2684
2685#define GMODE_TEXT 0
2686#define GMODE_GRAPH 1
2687#define GMODE_BLANK 2
2688#ifdef VBOX_WITH_VMSVGA
2689#define GMODE_SVGA 3
2690#endif
2691
2692/**
2693 * Worker for vgaR3PortUpdateDisplay(), vgaR3UpdateDisplayAllInternal() and
2694 * vgaR3PortTakeScreenshot().
2695 */
2696static int vgaR3UpdateDisplay(PPDMDEVINS pDevIns, PVGASTATE pThis, PVGASTATER3 pThisCC, bool fUpdateAll,
2697 bool fFailOnResize, bool reset_dirty, PDMIDISPLAYCONNECTOR *pDrv, int32_t *pcur_graphic_mode)
2698{
2699 int rc = VINF_SUCCESS;
2700 int graphic_mode;
2701
2702 if (pDrv->cBits == 0) {
2703 /* nothing to do */
2704 } else {
2705 switch(pDrv->cBits) {
2706 case 8:
2707 pThisCC->rgb_to_pixel = rgb_to_pixel8_dup;
2708 break;
2709 case 15:
2710 pThisCC->rgb_to_pixel = rgb_to_pixel15_dup;
2711 break;
2712 default:
2713 case 16:
2714 pThisCC->rgb_to_pixel = rgb_to_pixel16_dup;
2715 break;
2716 case 32:
2717 pThisCC->rgb_to_pixel = rgb_to_pixel32_dup;
2718 break;
2719 }
2720
2721#ifdef VBOX_WITH_VMSVGA
2722 if (pThis->svga.fEnabled) {
2723 graphic_mode = GMODE_SVGA;
2724 }
2725 else
2726#endif
2727 if (!(pThis->ar_index & 0x20) || (pThis->sr[0x01] & 0x20)) {
2728 graphic_mode = GMODE_BLANK;
2729 } else {
2730 graphic_mode = pThis->gr[6] & 1 ? GMODE_GRAPH : GMODE_TEXT;
2731 }
2732 bool full_update = fUpdateAll || graphic_mode != *pcur_graphic_mode;
2733 if (full_update) {
2734 *pcur_graphic_mode = graphic_mode;
2735 }
2736 switch(graphic_mode) {
2737 case GMODE_TEXT:
2738 rc = vgaR3DrawText(pDevIns, pThis, pThisCC, full_update, fFailOnResize, reset_dirty, pDrv);
2739 break;
2740 case GMODE_GRAPH:
2741 rc = vgaR3DrawGraphic(pThis, pThisCC, full_update, fFailOnResize, reset_dirty, pDrv);
2742 break;
2743#ifdef VBOX_WITH_VMSVGA
2744 case GMODE_SVGA:
2745 rc = vmsvgaR3DrawGraphic(pThis, pThisCC, full_update, fFailOnResize, reset_dirty, pDrv);
2746 break;
2747#endif
2748 case GMODE_BLANK:
2749 default:
2750 rc = vgaR3DrawBlank(pThis, pThisCC, full_update, fFailOnResize, reset_dirty, pDrv);
2751 break;
2752 }
2753 }
2754 return rc;
2755}
2756
2757/**
2758 * Worker for vgaR3SaveExec().
2759 */
2760static void vga_save(PCPDMDEVHLPR3 pHlp, PSSMHANDLE pSSM, PVGASTATE pThis)
2761{
2762 int i;
2763
2764 pHlp->pfnSSMPutU32(pSSM, pThis->latch);
2765 pHlp->pfnSSMPutU8(pSSM, pThis->sr_index);
2766 pHlp->pfnSSMPutMem(pSSM, pThis->sr, 8);
2767 pHlp->pfnSSMPutU8(pSSM, pThis->gr_index);
2768 pHlp->pfnSSMPutMem(pSSM, pThis->gr, 16);
2769 pHlp->pfnSSMPutU8(pSSM, pThis->ar_index);
2770 pHlp->pfnSSMPutMem(pSSM, pThis->ar, 21);
2771 pHlp->pfnSSMPutU32(pSSM, pThis->ar_flip_flop);
2772 pHlp->pfnSSMPutU8(pSSM, pThis->cr_index);
2773 pHlp->pfnSSMPutMem(pSSM, pThis->cr, 256);
2774 pHlp->pfnSSMPutU8(pSSM, pThis->msr);
2775 pHlp->pfnSSMPutU8(pSSM, pThis->fcr);
2776 pHlp->pfnSSMPutU8(pSSM, pThis->st00);
2777 pHlp->pfnSSMPutU8(pSSM, pThis->st01);
2778
2779 pHlp->pfnSSMPutU8(pSSM, pThis->dac_state);
2780 pHlp->pfnSSMPutU8(pSSM, pThis->dac_sub_index);
2781 pHlp->pfnSSMPutU8(pSSM, pThis->dac_read_index);
2782 pHlp->pfnSSMPutU8(pSSM, pThis->dac_write_index);
2783 pHlp->pfnSSMPutMem(pSSM, pThis->dac_cache, 3);
2784 pHlp->pfnSSMPutMem(pSSM, pThis->palette, 768);
2785
2786 pHlp->pfnSSMPutU32(pSSM, pThis->bank_offset);
2787#ifdef CONFIG_BOCHS_VBE
2788 AssertCompile(RT_ELEMENTS(pThis->vbe_regs) < 256);
2789 pHlp->pfnSSMPutU8(pSSM, (uint8_t)RT_ELEMENTS(pThis->vbe_regs));
2790 pHlp->pfnSSMPutU16(pSSM, pThis->vbe_index);
2791 for(i = 0; i < (int)RT_ELEMENTS(pThis->vbe_regs); i++)
2792 pHlp->pfnSSMPutU16(pSSM, pThis->vbe_regs[i]);
2793 pHlp->pfnSSMPutU32(pSSM, pThis->vbe_start_addr);
2794 pHlp->pfnSSMPutU32(pSSM, pThis->vbe_line_offset);
2795#else
2796 pHlp->pfnSSMPutU8(pSSM, 0);
2797#endif
2798}
2799
2800
2801/**
2802 * Worker for vgaR3LoadExec().
2803 */
2804static int vga_load(PCPDMDEVHLPR3 pHlp, PSSMHANDLE pSSM, PVGASTATE pThis, int version_id)
2805{
2806 int is_vbe, i;
2807 uint32_t u32Dummy;
2808 uint8_t u8;
2809
2810 pHlp->pfnSSMGetU32(pSSM, &pThis->latch);
2811 pHlp->pfnSSMGetU8(pSSM, &pThis->sr_index);
2812 pHlp->pfnSSMGetMem(pSSM, pThis->sr, 8);
2813 pHlp->pfnSSMGetU8(pSSM, &pThis->gr_index);
2814 pHlp->pfnSSMGetMem(pSSM, pThis->gr, 16);
2815 pHlp->pfnSSMGetU8(pSSM, &pThis->ar_index);
2816 pHlp->pfnSSMGetMem(pSSM, pThis->ar, 21);
2817 pHlp->pfnSSMGetS32(pSSM, &pThis->ar_flip_flop);
2818 pHlp->pfnSSMGetU8(pSSM, &pThis->cr_index);
2819 pHlp->pfnSSMGetMem(pSSM, pThis->cr, 256);
2820 pHlp->pfnSSMGetU8(pSSM, &pThis->msr);
2821 pHlp->pfnSSMGetU8(pSSM, &pThis->fcr);
2822 pHlp->pfnSSMGetU8(pSSM, &pThis->st00);
2823 pHlp->pfnSSMGetU8(pSSM, &pThis->st01);
2824
2825 pHlp->pfnSSMGetU8(pSSM, &pThis->dac_state);
2826 pHlp->pfnSSMGetU8(pSSM, &pThis->dac_sub_index);
2827 pHlp->pfnSSMGetU8(pSSM, &pThis->dac_read_index);
2828 pHlp->pfnSSMGetU8(pSSM, &pThis->dac_write_index);
2829 pHlp->pfnSSMGetMem(pSSM, pThis->dac_cache, 3);
2830 pHlp->pfnSSMGetMem(pSSM, pThis->palette, 768);
2831
2832 pHlp->pfnSSMGetS32(pSSM, &pThis->bank_offset);
2833 pHlp->pfnSSMGetU8(pSSM, &u8);
2834 is_vbe = !!u8;
2835#ifdef CONFIG_BOCHS_VBE
2836 if (!is_vbe)
2837 {
2838 Log(("vga_load: !is_vbe !!\n"));
2839 return VERR_SSM_DATA_UNIT_FORMAT_CHANGED;
2840 }
2841
2842 if (u8 == 1)
2843 u8 = VBE_DISPI_INDEX_NB_SAVED; /* Used to save so many registers. */
2844 if (u8 > RT_ELEMENTS(pThis->vbe_regs))
2845 {
2846 Log(("vga_load: saved %d, expected %d!!\n", u8, RT_ELEMENTS(pThis->vbe_regs)));
2847 return VERR_SSM_DATA_UNIT_FORMAT_CHANGED;
2848 }
2849
2850 pHlp->pfnSSMGetU16(pSSM, &pThis->vbe_index);
2851 for(i = 0; i < (int)u8; i++)
2852 pHlp->pfnSSMGetU16(pSSM, &pThis->vbe_regs[i]);
2853 if (version_id <= VGA_SAVEDSTATE_VERSION_INV_VHEIGHT)
2854 recalculate_data(pThis); /* <- re-calculate the pThis->vbe_regs[VBE_DISPI_INDEX_VIRT_HEIGHT] since it might be invalid */
2855 pHlp->pfnSSMGetU32(pSSM, &pThis->vbe_start_addr);
2856 pHlp->pfnSSMGetU32(pSSM, &pThis->vbe_line_offset);
2857 if (version_id < 2)
2858 pHlp->pfnSSMGetU32(pSSM, &u32Dummy);
2859 pThis->vbe_bank_max = (pThis->vram_size >> 16) - 1;
2860#else
2861 if (is_vbe)
2862 {
2863 Log(("vga_load: is_vbe !!\n"));
2864 return VERR_SSM_DATA_UNIT_FORMAT_CHANGED;
2865 }
2866#endif
2867
2868 /* force refresh */
2869 pThis->graphic_mode = -1;
2870 return 0;
2871}
2872
2873
2874/**
2875 * Worker for vgaR3Construct().
2876 */
2877static void vgaR3InitExpand(void)
2878{
2879 int i, j, v, b;
2880
2881 for(i = 0;i < 256; i++) {
2882 v = 0;
2883 for(j = 0; j < 8; j++) {
2884 v |= ((i >> j) & 1) << (j * 4);
2885 }
2886 expand4[i] = v;
2887
2888 v = 0;
2889 for(j = 0; j < 4; j++) {
2890 v |= ((i >> (2 * j)) & 3) << (j * 4);
2891 }
2892 expand2[i] = v;
2893 }
2894 for(i = 0; i < 16; i++) {
2895 v = 0;
2896 for(j = 0; j < 4; j++) {
2897 b = ((i >> j) & 1);
2898 v |= b << (2 * j);
2899 v |= b << (2 * j + 1);
2900 }
2901 expand4to8[i] = v;
2902 }
2903}
2904
2905#endif /* IN_RING3 */
2906
2907
2908
2909/* -=-=-=-=-=- all contexts -=-=-=-=-=- */
2910
2911#define VGA_IOPORT_WRITE_PLACEHOLDER(a_uPort, a_cPorts) do {\
2912 PVGASTATE pThis = PDMDEVINS_2_DATA(pDevIns, PVGASTATE); \
2913 Assert(PDMDevHlpCritSectIsOwner(pDevIns, pDevIns->CTX_SUFF(pCritSectRo))); \
2914 AssertCompile(RT_IS_POWER_OF_TWO(a_cPorts)); \
2915 Assert((unsigned)offPort - (unsigned)(a_uPort) < (unsigned)(a_cPorts)); \
2916 NOREF(pvUser); \
2917 if (cb == 1) \
2918 vga_ioport_write(pDevIns, pThis, offPort, u32); \
2919 else if (cb == 2) \
2920 { \
2921 vga_ioport_write(pDevIns, pThis, offPort, u32 & 0xff); \
2922 vga_ioport_write(pDevIns, pThis, offPort + 1, u32 >> 8); \
2923 } \
2924 return VINF_SUCCESS; \
2925 } while (0)
2926
2927#define VGA_IOPORT_READ_PLACEHOLDER(a_uPort, a_cPorts) do {\
2928 PVGASTATE pThis = PDMDEVINS_2_DATA(pDevIns, PVGASTATE); \
2929 Assert(PDMDevHlpCritSectIsOwner(pDevIns, pDevIns->CTX_SUFF(pCritSectRo))); \
2930 AssertCompile(RT_IS_POWER_OF_TWO(a_cPorts)); \
2931 Assert((unsigned)offPort - (unsigned)(a_uPort) < (unsigned)(a_cPorts)); \
2932 NOREF(pvUser); \
2933 if (cb == 1) \
2934 *pu32 = vga_ioport_read(pDevIns, pThis, offPort); \
2935 else if (cb == 2) \
2936 { \
2937 uint32_t u32 = vga_ioport_read(pDevIns, pThis, offPort); \
2938 u32 |= vga_ioport_read(pDevIns, pThis, offPort + 1) << 8; \
2939 *pu32 = u32; \
2940 } \
2941 else \
2942 return VERR_IOM_IOPORT_UNUSED; \
2943 return VINF_SUCCESS; \
2944 } while (0)
2945
2946/**
2947 * @callback_method_impl{FNIOMIOPORTNEWOUT,0x3c0-0x3c1 Attribute Controller.}
2948 */
2949static DECLCALLBACK(VBOXSTRICTRC) vgaIoPortArWrite(PPDMDEVINS pDevIns, void *pvUser, RTIOPORT offPort, uint32_t u32, unsigned cb)
2950{
2951 VGA_IOPORT_WRITE_PLACEHOLDER(0x3c0, 2);
2952}
2953
2954/**
2955 * @callback_method_impl{FNIOMIOPORTNEWIN,0x3c0-0x3c1 Attribute Controller.}
2956 */
2957static DECLCALLBACK(VBOXSTRICTRC) vgaIoPortArRead(PPDMDEVINS pDevIns, void *pvUser, RTIOPORT offPort, uint32_t *pu32, unsigned cb)
2958{
2959 VGA_IOPORT_READ_PLACEHOLDER(0x3c0, 2);
2960}
2961
2962
2963/**
2964 * @callback_method_impl{FNIOMIOPORTNEWOUT,0x3c2 Miscellaneous Register.}
2965 */
2966static DECLCALLBACK(VBOXSTRICTRC) vgaIoPortMsrWrite(PPDMDEVINS pDevIns, void *pvUser, RTIOPORT offPort, uint32_t u32, unsigned cb)
2967{
2968 VGA_IOPORT_WRITE_PLACEHOLDER(0x3c2, 1);
2969}
2970
2971/**
2972 * @callback_method_impl{FNIOMIOPORTNEWIN,0x3c2 Status register 0.}
2973 */
2974static DECLCALLBACK(VBOXSTRICTRC) vgaIoPortSt00Read(PPDMDEVINS pDevIns, void *pvUser, RTIOPORT offPort, uint32_t *pu32, unsigned cb)
2975{
2976 VGA_IOPORT_READ_PLACEHOLDER(0x3c2, 1);
2977}
2978
2979
2980/**
2981 * @callback_method_impl{FNIOMIOPORTNEWOUT,0x3c3 Unused.}
2982 */
2983static DECLCALLBACK(VBOXSTRICTRC) vgaIoPortUnusedWrite(PPDMDEVINS pDevIns, void *pvUser, RTIOPORT offPort, uint32_t u32, unsigned cb)
2984{
2985 VGA_IOPORT_WRITE_PLACEHOLDER(0x3c3, 1);
2986}
2987
2988/**
2989 * @callback_method_impl{FNIOMIOPORTNEWIN,0x3c3 Unused.}
2990 */
2991static DECLCALLBACK(VBOXSTRICTRC) vgaIoPortUnusedRead(PPDMDEVINS pDevIns, void *pvUser, RTIOPORT offPort, uint32_t *pu32, unsigned cb)
2992{
2993 VGA_IOPORT_READ_PLACEHOLDER(0x3c3, 1);
2994}
2995
2996
2997/**
2998 * @callback_method_impl{FNIOMIOPORTNEWOUT,0x3c4-0x3c5 Sequencer.}
2999 */
3000static DECLCALLBACK(VBOXSTRICTRC) vgaIoPortSrWrite(PPDMDEVINS pDevIns, void *pvUser, RTIOPORT offPort, uint32_t u32, unsigned cb)
3001{
3002 VGA_IOPORT_WRITE_PLACEHOLDER(0x3c4, 2);
3003}
3004
3005/**
3006 * @callback_method_impl{FNIOMIOPORTNEWIN,0x3c4-0x3c5 Sequencer.}
3007 */
3008static DECLCALLBACK(VBOXSTRICTRC) vgaIoPortSrRead(PPDMDEVINS pDevIns, void *pvUser, RTIOPORT offPort, uint32_t *pu32, unsigned cb)
3009{
3010 VGA_IOPORT_READ_PLACEHOLDER(0x3c4, 2);
3011}
3012
3013
3014/**
3015 * @callback_method_impl{FNIOMIOPORTNEWOUT,0x3c6-0x3c9 DAC.}
3016 */
3017static DECLCALLBACK(VBOXSTRICTRC) vgaIoPortDacWrite(PPDMDEVINS pDevIns, void *pvUser, RTIOPORT offPort, uint32_t u32, unsigned cb)
3018{
3019 VGA_IOPORT_WRITE_PLACEHOLDER(0x3c6, 4);
3020}
3021
3022/**
3023 * @callback_method_impl{FNIOMIOPORTNEWIN,0x3c6-0x3c9 DAC.}
3024 */
3025static DECLCALLBACK(VBOXSTRICTRC) vgaIoPortDacRead(PPDMDEVINS pDevIns, void *pvUser, RTIOPORT offPort, uint32_t *pu32, unsigned cb)
3026{
3027 VGA_IOPORT_READ_PLACEHOLDER(0x3c6, 4);
3028}
3029
3030
3031/**
3032 * @callback_method_impl{FNIOMIOPORTNEWOUT,0x3ca-0x3cd Graphics Position?}
3033 */
3034static DECLCALLBACK(VBOXSTRICTRC) vgaIoPortPosWrite(PPDMDEVINS pDevIns, void *pvUser, RTIOPORT offPort, uint32_t u32, unsigned cb)
3035{
3036 VGA_IOPORT_WRITE_PLACEHOLDER(0x3ca, 4);
3037}
3038
3039/**
3040 * @callback_method_impl{FNIOMIOPORTNEWIN,0x3ca-0x3cd Graphics Position?}
3041 */
3042static DECLCALLBACK(VBOXSTRICTRC) vgaIoPortPosRead(PPDMDEVINS pDevIns, void *pvUser, RTIOPORT offPort, uint32_t *pu32, unsigned cb)
3043{
3044 VGA_IOPORT_READ_PLACEHOLDER(0x3ca, 4);
3045}
3046
3047
3048/**
3049 * @callback_method_impl{FNIOMIOPORTNEWOUT,0x3ce-0x3cf Graphics Controller.}
3050 */
3051static DECLCALLBACK(VBOXSTRICTRC) vgaIoPortGrWrite(PPDMDEVINS pDevIns, void *pvUser, RTIOPORT offPort, uint32_t u32, unsigned cb)
3052{
3053 VGA_IOPORT_WRITE_PLACEHOLDER(0x3ce, 2);
3054}
3055
3056/**
3057 * @callback_method_impl{FNIOMIOPORTNEWIN,0x3ca-0x3cf Graphics Controller.}
3058 */
3059static DECLCALLBACK(VBOXSTRICTRC) vgaIoPortGrRead(PPDMDEVINS pDevIns, void *pvUser, RTIOPORT offPort, uint32_t *pu32, unsigned cb)
3060{
3061 VGA_IOPORT_READ_PLACEHOLDER(0x3ce, 2);
3062}
3063
3064
3065/**
3066 * @callback_method_impl{FNIOMIOPORTNEWOUT,0x3b4-0x3b5 MDA CRT control.}
3067 */
3068static DECLCALLBACK(VBOXSTRICTRC) vgaIoPortMdaCrtWrite(PPDMDEVINS pDevIns, void *pvUser, RTIOPORT offPort, uint32_t u32, unsigned cb)
3069{
3070 /** @todo do vga_ioport_invalid here */
3071 VGA_IOPORT_WRITE_PLACEHOLDER(0x3b4, 2);
3072}
3073
3074/**
3075 * @callback_method_impl{FNIOMIOPORTNEWIN,0x3b4-0x3b5 MDA CRT control.}
3076 */
3077static DECLCALLBACK(VBOXSTRICTRC) vgaIoPortMdaCrtRead(PPDMDEVINS pDevIns, void *pvUser, RTIOPORT offPort, uint32_t *pu32, unsigned cb)
3078{
3079 /** @todo do vga_ioport_invalid here */
3080 VGA_IOPORT_READ_PLACEHOLDER(0x3b4, 2);
3081}
3082
3083
3084/**
3085 * @callback_method_impl{FNIOMIOPORTNEWOUT,0x3ba MDA feature/status.}
3086 */
3087static DECLCALLBACK(VBOXSTRICTRC) vgaIoPortMdaFcrWrite(PPDMDEVINS pDevIns, void *pvUser, RTIOPORT offPort, uint32_t u32, unsigned cb)
3088{
3089 /** @todo do vga_ioport_invalid here */
3090 VGA_IOPORT_WRITE_PLACEHOLDER(0x3ba, 1);
3091}
3092
3093/**
3094 * @callback_method_impl{FNIOMIOPORTNEWIN,0x3ba MDA feature/status.}
3095 */
3096static DECLCALLBACK(VBOXSTRICTRC) vgaIoPortMdaStRead(PPDMDEVINS pDevIns, void *pvUser, RTIOPORT offPort, uint32_t *pu32, unsigned cb)
3097{
3098 /** @todo do vga_ioport_invalid here */
3099 VGA_IOPORT_READ_PLACEHOLDER(0x3ba, 1);
3100}
3101
3102
3103/**
3104 * @callback_method_impl{FNIOMIOPORTNEWOUT,0x3d4-0x3d5 CGA CRT control.}
3105 */
3106static DECLCALLBACK(VBOXSTRICTRC) vgaIoPortCgaCrtWrite(PPDMDEVINS pDevIns, void *pvUser, RTIOPORT offPort, uint32_t u32, unsigned cb)
3107{
3108 /** @todo do vga_ioport_invalid here */
3109 VGA_IOPORT_WRITE_PLACEHOLDER(0x3d4, 2);
3110}
3111
3112/**
3113 * @callback_method_impl{FNIOMIOPORTNEWIN,0x3d4-0x3d5 CGA CRT control.}
3114 */
3115static DECLCALLBACK(VBOXSTRICTRC) vgaIoPortCgaCrtRead(PPDMDEVINS pDevIns, void *pvUser, RTIOPORT offPort, uint32_t *pu32, unsigned cb)
3116{
3117 /** @todo do vga_ioport_invalid here */
3118 VGA_IOPORT_READ_PLACEHOLDER(0x3d4, 2);
3119}
3120
3121
3122/**
3123 * @callback_method_impl{FNIOMIOPORTNEWOUT,0x3da CGA feature/status.}
3124 */
3125static DECLCALLBACK(VBOXSTRICTRC) vgaIoPortCgaFcrWrite(PPDMDEVINS pDevIns, void *pvUser, RTIOPORT offPort, uint32_t u32, unsigned cb)
3126{
3127 /** @todo do vga_ioport_invalid here */
3128 VGA_IOPORT_WRITE_PLACEHOLDER(0x3da, 1);
3129}
3130
3131/**
3132 * @callback_method_impl{FNIOMIOPORTNEWIN,0x3da CGA feature/status.}
3133 */
3134static DECLCALLBACK(VBOXSTRICTRC) vgaIoPortCgaStRead(PPDMDEVINS pDevIns, void *pvUser, RTIOPORT offPort, uint32_t *pu32, unsigned cb)
3135{
3136 /** @todo do vga_ioport_invalid here */
3137 VGA_IOPORT_READ_PLACEHOLDER(0x3da, 1);
3138}
3139
3140
3141/**
3142 * @callback_method_impl{FNIOMIOPORTNEWOUT,VBE Data Port OUT handler (0x1ce).}
3143 */
3144static DECLCALLBACK(VBOXSTRICTRC)
3145vgaIoPortWriteVbeData(PPDMDEVINS pDevIns, void *pvUser, RTIOPORT offPort, uint32_t u32, unsigned cb)
3146{
3147 PVGASTATE pThis = PDMDEVINS_2_DATA(pDevIns, PVGASTATE);
3148 PVGASTATECC pThisCC = PDMDEVINS_2_DATA_CC(pDevIns, PVGASTATECC);
3149 Assert(PDMDevHlpCritSectIsOwner(pDevIns, pDevIns->CTX_SUFF(pCritSectRo)));
3150
3151 NOREF(pvUser);
3152
3153#ifndef IN_RING3
3154 /*
3155 * This has to be done on the host in order to execute the connector callbacks.
3156 */
3157 if ( pThis->vbe_index == VBE_DISPI_INDEX_ENABLE
3158 || pThis->vbe_index == VBE_DISPI_INDEX_VBOX_VIDEO)
3159 {
3160 Log(("vgaIoPortWriteVbeData: VBE_DISPI_INDEX_ENABLE - Switching to host...\n"));
3161 return VINF_IOM_R3_IOPORT_WRITE;
3162 }
3163#endif
3164#ifdef VBE_BYTEWISE_IO
3165 if (cb == 1)
3166 {
3167 if (!pThis->fWriteVBEData)
3168 {
3169 if ( (pThis->vbe_index == VBE_DISPI_INDEX_ENABLE)
3170 && (u32 & VBE_DISPI_ENABLED))
3171 {
3172 pThis->fWriteVBEData = false;
3173 return vbe_ioport_write_data(pDevIns, pThis, pThisCC, offPort, u32 & 0xFF);
3174 }
3175
3176 pThis->cbWriteVBEData = u32 & 0xFF;
3177 pThis->fWriteVBEData = true;
3178 return VINF_SUCCESS;
3179 }
3180
3181 u32 = (pThis->cbWriteVBEData << 8) | (u32 & 0xFF);
3182 pThis->fWriteVBEData = false;
3183 cb = 2;
3184 }
3185#endif
3186 if (cb == 2 || cb == 4)
3187 return vbe_ioport_write_data(pDevIns, pThis, pThisCC, offPort, u32);
3188 AssertMsgFailed(("vgaIoPortWriteVbeData: offPort=%#x cb=%d u32=%#x\n", offPort, cb, u32));
3189
3190 return VINF_SUCCESS;
3191}
3192
3193
3194/**
3195 * @callback_method_impl{FNIOMIOPORTNEWOUT,VBE Index Port OUT handler (0x1ce).}
3196 */
3197static DECLCALLBACK(VBOXSTRICTRC)
3198vgaIoPortWriteVbeIndex(PPDMDEVINS pDevIns, void *pvUser, RTIOPORT offPort, uint32_t u32, unsigned cb)
3199{
3200 PVGASTATE pThis = PDMDEVINS_2_DATA(pDevIns, PVGASTATE); NOREF(pvUser);
3201 Assert(PDMDevHlpCritSectIsOwner(pDevIns, pDevIns->CTX_SUFF(pCritSectRo)));
3202
3203#ifdef VBE_BYTEWISE_IO
3204 if (cb == 1)
3205 {
3206 if (!pThis->fWriteVBEIndex)
3207 {
3208 pThis->cbWriteVBEIndex = u32 & 0x00FF;
3209 pThis->fWriteVBEIndex = true;
3210 return VINF_SUCCESS;
3211 }
3212 pThis->fWriteVBEIndex = false;
3213 vbe_ioport_write_index(pThis, offPort, (pThis->cbWriteVBEIndex << 8) | (u32 & 0x00FF));
3214 return VINF_SUCCESS;
3215 }
3216#endif
3217
3218 if (cb == 2)
3219 vbe_ioport_write_index(pThis, offPort, u32);
3220 else
3221 ASSERT_GUEST_MSG_FAILED(("vgaIoPortWriteVbeIndex: offPort=%#x cb=%d u32=%#x\n", offPort, cb, u32));
3222 return VINF_SUCCESS;
3223}
3224
3225
3226/**
3227 * @callback_method_impl{FNIOMIOPORTNEWOUT,VBE Data Port IN handler (0x1cf).}
3228 */
3229static DECLCALLBACK(VBOXSTRICTRC)
3230vgaIoPortReadVbeData(PPDMDEVINS pDevIns, void *pvUser, RTIOPORT offPort, uint32_t *pu32, unsigned cb)
3231{
3232 PVGASTATE pThis = PDMDEVINS_2_DATA(pDevIns, PVGASTATE); NOREF(pvUser);
3233 Assert(PDMDevHlpCritSectIsOwner(pDevIns, pDevIns->CTX_SUFF(pCritSectRo)));
3234
3235#ifdef VBE_BYTEWISE_IO
3236 if (cb == 1)
3237 {
3238 if (!pThis->fReadVBEData)
3239 {
3240 *pu32 = (vbe_ioport_read_data(pThis, offPort) >> 8) & 0xFF;
3241 pThis->fReadVBEData = true;
3242 return VINF_SUCCESS;
3243 }
3244 *pu32 = vbe_ioport_read_data(pThis, offPort) & 0xFF;
3245 pThis->fReadVBEData = false;
3246 return VINF_SUCCESS;
3247 }
3248#endif
3249 if (cb == 2)
3250 {
3251 *pu32 = vbe_ioport_read_data(pThis, offPort);
3252 return VINF_SUCCESS;
3253 }
3254 if (cb == 4)
3255 {
3256 if (pThis->vbe_regs[VBE_DISPI_INDEX_ID] == VBE_DISPI_ID_CFG)
3257 *pu32 = vbe_ioport_read_data(pThis, offPort); /* New interface. */
3258 else
3259 *pu32 = pThis->vram_size; /* Quick hack for getting the vram size. */
3260 return VINF_SUCCESS;
3261 }
3262 AssertMsgFailed(("vgaIoPortReadVbeData: offPort=%#x cb=%d\n", offPort, cb));
3263 return VERR_IOM_IOPORT_UNUSED;
3264}
3265
3266
3267/**
3268 * @callback_method_impl{FNIOMIOPORTNEWOUT,VBE Index Port IN handler (0x1cf).}
3269 */
3270static DECLCALLBACK(VBOXSTRICTRC)
3271vgaIoPortReadVbeIndex(PPDMDEVINS pDevIns, void *pvUser, RTIOPORT offPort, uint32_t *pu32, unsigned cb)
3272{
3273 NOREF(pvUser);
3274 PVGASTATE pThis = PDMDEVINS_2_DATA(pDevIns, PVGASTATE);
3275 Assert(PDMDevHlpCritSectIsOwner(pDevIns, pDevIns->CTX_SUFF(pCritSectRo)));
3276
3277#ifdef VBE_BYTEWISE_IO
3278 if (cb == 1)
3279 {
3280 if (!pThis->fReadVBEIndex)
3281 {
3282 *pu32 = (vbe_ioport_read_index(pThis, offPort) >> 8) & 0xFF;
3283 pThis->fReadVBEIndex = true;
3284 return VINF_SUCCESS;
3285 }
3286 *pu32 = vbe_ioport_read_index(pThis, offPort) & 0xFF;
3287 pThis->fReadVBEIndex = false;
3288 return VINF_SUCCESS;
3289 }
3290#endif
3291 if (cb == 2)
3292 {
3293 *pu32 = vbe_ioport_read_index(pThis, offPort);
3294 return VINF_SUCCESS;
3295 }
3296 AssertMsgFailed(("vgaIoPortReadVbeIndex: offPort=%#x cb=%d\n", offPort, cb));
3297 return VERR_IOM_IOPORT_UNUSED;
3298}
3299
3300#if defined(VBOX_WITH_HGSMI) && defined(IN_RING3)
3301
3302/**
3303 * @callback_method_impl{FNIOMIOPORTNEWOUT,HGSMI OUT handler.}
3304 */
3305static DECLCALLBACK(VBOXSTRICTRC)
3306vgaR3IOPortHgsmiWrite(PPDMDEVINS pDevIns, void *pvUser, RTIOPORT offPort, uint32_t u32, unsigned cb)
3307{
3308 PVGASTATE pThis = PDMDEVINS_2_DATA(pDevIns, PVGASTATE);
3309 PVGASTATECC pThisCC = PDMDEVINS_2_DATA_CC(pDevIns, PVGASTATECC);
3310 Assert(PDMDevHlpCritSectIsOwner(pDevIns, pDevIns->CTX_SUFF(pCritSectRo)));
3311 LogFlowFunc(("offPort=0x%x u32=0x%x cb=%u\n", offPort, u32, cb));
3312
3313 NOREF(pvUser);
3314
3315 if (cb == 4)
3316 {
3317 switch (offPort)
3318 {
3319 case VGA_PORT_HGSMI_HOST: /* Host */
3320 {
3321# if defined(VBOX_WITH_VIDEOHWACCEL) || defined(VBOX_WITH_VDMA) || defined(VBOX_WITH_WDDM)
3322 if (u32 == HGSMIOFFSET_VOID)
3323 {
3324 int const rcLock = PDMDevHlpCritSectEnter(pDevIns, &pThis->CritSectIRQ, VERR_SEM_BUSY);
3325 PDM_CRITSECT_RELEASE_ASSERT_RC_DEV(pDevIns, &pThis->CritSectIRQ, rcLock);
3326
3327 if (pThis->fu32PendingGuestFlags == 0)
3328 {
3329 PDMDevHlpPCISetIrqNoWait(pDevIns, 0, PDM_IRQ_LEVEL_LOW);
3330 HGSMIClearHostGuestFlags(pThisCC->pHGSMI,
3331 HGSMIHOSTFLAGS_IRQ
3332 | HGSMIHOSTFLAGS_VSYNC
3333 | HGSMIHOSTFLAGS_HOTPLUG
3334 | HGSMIHOSTFLAGS_CURSOR_CAPABILITIES);
3335 }
3336 else
3337 {
3338 HGSMISetHostGuestFlags(pThisCC->pHGSMI, HGSMIHOSTFLAGS_IRQ | pThis->fu32PendingGuestFlags);
3339 pThis->fu32PendingGuestFlags = 0;
3340 /* Keep the IRQ unchanged. */
3341 }
3342
3343 PDMDevHlpCritSectLeave(pDevIns, &pThis->CritSectIRQ);
3344 }
3345 else
3346# endif
3347 {
3348 HGSMIHostWrite(pThisCC->pHGSMI, u32);
3349 }
3350 break;
3351 }
3352
3353 case VGA_PORT_HGSMI_GUEST: /* Guest */
3354 HGSMIGuestWrite(pThisCC->pHGSMI, u32);
3355 break;
3356
3357 default:
3358# ifdef DEBUG_sunlover
3359 AssertMsgFailed(("vgaR3IOPortHgsmiWrite: offPort=%#x cb=%d u32=%#x\n", offPort, cb, u32));
3360# endif
3361 break;
3362 }
3363 }
3364 else
3365 {
3366 /** @todo r=bird: According to Ralf Brown, one and two byte accesses to the
3367 * 0x3b0-0x3b1 and 0x3b2-0x3b3 I/O port pairs should work the same as
3368 * 0x3b4-0x3b5 (MDA CRT control). */
3369 Log(("vgaR3IOPortHgsmiWrite: offPort=%#x cb=%d u32=%#x - possible valid MDA CRT access\n", offPort, cb, u32));
3370# ifdef DEBUG_sunlover
3371 AssertMsgFailed(("vgaR3IOPortHgsmiWrite: offPort=%#x cb=%d u32=%#x\n", offPort, cb, u32));
3372# endif
3373 STAM_REL_COUNTER_INC(&pThis->StatHgsmiMdaCgaAccesses);
3374 }
3375
3376 return VINF_SUCCESS;
3377}
3378
3379
3380/**
3381 * @callback_method_impl{FNIOMIOPORTNEWOUT,HGSMI IN handler.}
3382 */
3383static DECLCALLBACK(VBOXSTRICTRC)
3384vgaR3IOPortHgmsiRead(PPDMDEVINS pDevIns, void *pvUser, RTIOPORT offPort, uint32_t *pu32, unsigned cb)
3385{
3386 PVGASTATE pThis = PDMDEVINS_2_DATA(pDevIns, PVGASTATE);
3387 PVGASTATECC pThisCC = PDMDEVINS_2_DATA_CC(pDevIns, PVGASTATECC);
3388 Assert(PDMDevHlpCritSectIsOwner(pDevIns, pDevIns->CTX_SUFF(pCritSectRo)));
3389 LogFlowFunc(("offPort=0x%x cb=%d\n", offPort, cb));
3390
3391 NOREF(pvUser);
3392
3393 VBOXSTRICTRC rc = VINF_SUCCESS;
3394 if (cb == 4)
3395 {
3396 switch (offPort)
3397 {
3398 case VGA_PORT_HGSMI_HOST: /* Host */
3399 *pu32 = HGSMIHostRead(pThisCC->pHGSMI);
3400 break;
3401 case VGA_PORT_HGSMI_GUEST: /* Guest */
3402 *pu32 = HGSMIGuestRead(pThisCC->pHGSMI);
3403 break;
3404 default:
3405 rc = VERR_IOM_IOPORT_UNUSED;
3406 break;
3407 }
3408 }
3409 else
3410 {
3411 /** @todo r=bird: According to Ralf Brown, one and two byte accesses to the
3412 * 0x3b0-0x3b1 and 0x3b2-0x3b3 I/O port pairs should work the same as
3413 * 0x3b4-0x3b5 (MDA CRT control). */
3414 Log(("vgaR3IOPortHgmsiRead: offPort=%#x cb=%d - possible valid MDA CRT access\n", offPort, cb));
3415 STAM_REL_COUNTER_INC(&pThis->StatHgsmiMdaCgaAccesses);
3416 rc = VERR_IOM_IOPORT_UNUSED;
3417 }
3418
3419 return rc;
3420}
3421
3422#endif /* VBOX_WITH_HGSMI && IN_RING3*/
3423
3424
3425
3426
3427/* -=-=-=-=-=- All Contexts -=-=-=-=-=- */
3428
3429/**
3430 * @internal. For use inside VGAGCMemoryFillWrite only.
3431 * Macro for apply logical operation and bit mask.
3432 */
3433#define APPLY_LOGICAL_AND_MASK(pThis, val, bit_mask) \
3434 /* apply logical operation */ \
3435 switch (pThis->gr[3] >> 3)\
3436 { \
3437 case 0: \
3438 default:\
3439 /* nothing to do */ \
3440 break; \
3441 case 1: \
3442 /* and */ \
3443 val &= pThis->latch; \
3444 break; \
3445 case 2: \
3446 /* or */ \
3447 val |= pThis->latch; \
3448 break; \
3449 case 3: \
3450 /* xor */ \
3451 val ^= pThis->latch; \
3452 break; \
3453 } \
3454 /* apply bit mask */ \
3455 val = (val & bit_mask) | (pThis->latch & ~bit_mask)
3456
3457/**
3458 * Legacy VGA memory (0xa0000 - 0xbffff) write hook, to be called from IOM and from the inside of VGADeviceGC.cpp.
3459 * This is the advanced version of vga_mem_writeb function.
3460 *
3461 * @returns VBox status code.
3462 * @param pThis The shared VGA instance data.
3463 * @param pThisCC The VGA instance data for the current context.
3464 * @param pvUser User argument - ignored.
3465 * @param GCPhysAddr Physical address of memory to write.
3466 * @param u32Item Data to write, up to 4 bytes.
3467 * @param cbItem Size of data Item, only 1/2/4 bytes is allowed for now.
3468 * @param cItems Number of data items to write.
3469 */
3470static int vgaInternalMMIOFill(PVGASTATE pThis, PVGASTATECC pThisCC, void *pvUser, RTGCPHYS GCPhysAddr,
3471 uint32_t u32Item, unsigned cbItem, unsigned cItems)
3472{
3473 uint32_t b;
3474 uint32_t write_mask, bit_mask, set_mask;
3475 uint32_t aVal[4];
3476 unsigned i;
3477 NOREF(pvUser);
3478
3479 for (i = 0; i < cbItem; i++)
3480 {
3481 aVal[i] = u32Item & 0xff;
3482 u32Item >>= 8;
3483 }
3484
3485 /* convert to VGA memory offset */
3486 /// @todo add check for the end of region
3487 GCPhysAddr &= 0x1ffff;
3488 switch((pThis->gr[6] >> 2) & 3) {
3489 case 0:
3490 break;
3491 case 1:
3492 if (GCPhysAddr >= 0x10000)
3493 return VINF_SUCCESS;
3494 GCPhysAddr += pThis->bank_offset;
3495 break;
3496 case 2:
3497 GCPhysAddr -= 0x10000;
3498 if (GCPhysAddr >= 0x8000)
3499 return VINF_SUCCESS;
3500 break;
3501 default:
3502 case 3:
3503 GCPhysAddr -= 0x18000;
3504 if (GCPhysAddr >= 0x8000)
3505 return VINF_SUCCESS;
3506 break;
3507 }
3508
3509 if (pThis->sr[4] & 0x08) {
3510 /* chain 4 mode : simplest access */
3511 VERIFY_VRAM_WRITE_OFF_RETURN(pThis, GCPhysAddr + cItems * cbItem - 1);
3512
3513 while (cItems-- > 0)
3514 for (i = 0; i < cbItem; i++)
3515 {
3516 if (pThis->sr[2] & (1 << (GCPhysAddr & 3)))
3517 {
3518 pThisCC->pbVRam[GCPhysAddr] = aVal[i];
3519 vgaR3MarkDirty(pThis, GCPhysAddr);
3520 }
3521 GCPhysAddr++;
3522 }
3523 } else if (pThis->gr[5] & 0x10) {
3524 /* odd/even mode (aka text mode mapping) */
3525 VERIFY_VRAM_WRITE_OFF_RETURN(pThis, (GCPhysAddr + cItems * cbItem) * 4 - 1);
3526 while (cItems-- > 0)
3527 for (i = 0; i < cbItem; i++)
3528 {
3529 unsigned plane = GCPhysAddr & 1;
3530 if (pThis->sr[2] & (1 << plane)) {
3531 RTGCPHYS PhysAddr2 = ((GCPhysAddr & ~1) * 4) | plane;
3532 pThisCC->pbVRam[PhysAddr2] = aVal[i];
3533 vgaR3MarkDirty(pThis, PhysAddr2);
3534 }
3535 GCPhysAddr++;
3536 }
3537 } else {
3538 /* standard VGA latched access */
3539 VERIFY_VRAM_WRITE_OFF_RETURN(pThis, (GCPhysAddr + cItems * cbItem) * 4 - 1);
3540
3541 switch(pThis->gr[5] & 3) {
3542 default:
3543 case 0:
3544 /* rotate */
3545 b = pThis->gr[3] & 7;
3546 bit_mask = pThis->gr[8];
3547 bit_mask |= bit_mask << 8;
3548 bit_mask |= bit_mask << 16;
3549 set_mask = mask16[pThis->gr[1]];
3550
3551 for (i = 0; i < cbItem; i++)
3552 {
3553 aVal[i] = ((aVal[i] >> b) | (aVal[i] << (8 - b))) & 0xff;
3554 aVal[i] |= aVal[i] << 8;
3555 aVal[i] |= aVal[i] << 16;
3556
3557 /* apply set/reset mask */
3558 aVal[i] = (aVal[i] & ~set_mask) | (mask16[pThis->gr[0]] & set_mask);
3559
3560 APPLY_LOGICAL_AND_MASK(pThis, aVal[i], bit_mask);
3561 }
3562 break;
3563 case 1:
3564 for (i = 0; i < cbItem; i++)
3565 aVal[i] = pThis->latch;
3566 break;
3567 case 2:
3568 bit_mask = pThis->gr[8];
3569 bit_mask |= bit_mask << 8;
3570 bit_mask |= bit_mask << 16;
3571 for (i = 0; i < cbItem; i++)
3572 {
3573 aVal[i] = mask16[aVal[i] & 0x0f];
3574
3575 APPLY_LOGICAL_AND_MASK(pThis, aVal[i], bit_mask);
3576 }
3577 break;
3578 case 3:
3579 /* rotate */
3580 b = pThis->gr[3] & 7;
3581
3582 for (i = 0; i < cbItem; i++)
3583 {
3584 aVal[i] = (aVal[i] >> b) | (aVal[i] << (8 - b));
3585 bit_mask = pThis->gr[8] & aVal[i];
3586 bit_mask |= bit_mask << 8;
3587 bit_mask |= bit_mask << 16;
3588 aVal[i] = mask16[pThis->gr[0]];
3589
3590 APPLY_LOGICAL_AND_MASK(pThis, aVal[i], bit_mask);
3591 }
3592 break;
3593 }
3594
3595 /* mask data according to sr[2] */
3596 write_mask = mask16[pThis->sr[2]];
3597
3598 /* actually write data */
3599 if (cbItem == 1)
3600 {
3601 /* The most frequently case is 1 byte I/O. */
3602 while (cItems-- > 0)
3603 {
3604 ((uint32_t *)pThisCC->pbVRam)[GCPhysAddr] = (((uint32_t *)pThisCC->pbVRam)[GCPhysAddr] & ~write_mask) | (aVal[0] & write_mask);
3605 vgaR3MarkDirty(pThis, GCPhysAddr * 4);
3606 GCPhysAddr++;
3607 }
3608 }
3609 else if (cbItem == 2)
3610 {
3611 /* The second case is 2 bytes I/O. */
3612 while (cItems-- > 0)
3613 {
3614 ((uint32_t *)pThisCC->pbVRam)[GCPhysAddr] = (((uint32_t *)pThisCC->pbVRam)[GCPhysAddr] & ~write_mask) | (aVal[0] & write_mask);
3615 vgaR3MarkDirty(pThis, GCPhysAddr * 4);
3616 GCPhysAddr++;
3617
3618 ((uint32_t *)pThisCC->pbVRam)[GCPhysAddr] = (((uint32_t *)pThisCC->pbVRam)[GCPhysAddr] & ~write_mask) | (aVal[1] & write_mask);
3619 vgaR3MarkDirty(pThis, GCPhysAddr * 4);
3620 GCPhysAddr++;
3621 }
3622 }
3623 else
3624 {
3625 /* And the rest is 4 bytes. */
3626 Assert(cbItem == 4);
3627 while (cItems-- > 0)
3628 for (i = 0; i < cbItem; i++)
3629 {
3630 ((uint32_t *)pThisCC->pbVRam)[GCPhysAddr] = (((uint32_t *)pThisCC->pbVRam)[GCPhysAddr] & ~write_mask) | (aVal[i] & write_mask);
3631 vgaR3MarkDirty(pThis, GCPhysAddr * 4);
3632 GCPhysAddr++;
3633 }
3634 }
3635 }
3636 return VINF_SUCCESS;
3637}
3638
3639#undef APPLY_LOGICAL_AND_MASK
3640
3641/**
3642 * @callback_method_impl{FNIOMMMIONEWFILL,
3643 * Legacy VGA memory (0xa0000 - 0xbffff) write hook\, to be called from IOM and
3644 * from the inside of VGADeviceGC.cpp. This is the advanced version of
3645 * vga_mem_writeb function.}
3646 */
3647static DECLCALLBACK(VBOXSTRICTRC)
3648vgaMmioFill(PPDMDEVINS pDevIns, void *pvUser, RTGCPHYS off, uint32_t u32Item, unsigned cbItem, unsigned cItems)
3649{
3650 PVGASTATE pThis = PDMDEVINS_2_DATA(pDevIns, PVGASTATE);
3651 PVGASTATECC pThisCC = PDMDEVINS_2_DATA_CC(pDevIns, PVGASTATECC);
3652 Assert(PDMDevHlpCritSectIsOwner(pDevIns, pDevIns->CTX_SUFF(pCritSectRo)));
3653
3654 return vgaInternalMMIOFill(pThis, pThisCC, pvUser, off, u32Item, cbItem, cItems);
3655}
3656
3657
3658/**
3659 * @callback_method_impl{FNIOMMMIONEWREAD,
3660 * Legacy VGA memory (0xa0000 - 0xbffff) read hook\, to be called from IOM.}
3661 *
3662 * @note The @a off is an absolute address in the 0xa0000 - 0xbffff range, not
3663 * an offset.
3664 */
3665static DECLCALLBACK(VBOXSTRICTRC) vgaMmioRead(PPDMDEVINS pDevIns, void *pvUser, RTGCPHYS off, void *pv, unsigned cb)
3666{
3667 PVGASTATE pThis = PDMDEVINS_2_DATA(pDevIns, PVGASTATE);
3668 PVGASTATECC pThisCC = PDMDEVINS_2_DATA_CC(pDevIns, PVGASTATECC);
3669 STAM_PROFILE_START(&pThis->CTX_MID_Z(Stat,MemoryRead), a);
3670 Assert(PDMDevHlpCritSectIsOwner(pDevIns, pDevIns->CTX_SUFF(pCritSectRo)));
3671 NOREF(pvUser);
3672
3673 int rc = VINF_SUCCESS;
3674 switch (cb)
3675 {
3676 case 1:
3677 *(uint8_t *)pv = vga_mem_readb(pDevIns, pThis, pThisCC, off, &rc);
3678 break;
3679 case 2:
3680/** @todo This and the wider accesses maybe misbehave when accessing bytes
3681 * crossing the 512KB VRAM boundrary if the access is handled in
3682 * ring-0 and operating in latched mode. */
3683 *(uint16_t *)pv = vga_mem_readb(pDevIns, pThis, pThisCC, off, &rc)
3684 | (vga_mem_readb(pDevIns, pThis, pThisCC, off + 1, &rc) << 8);
3685 break;
3686 case 4:
3687 *(uint32_t *)pv = vga_mem_readb(pDevIns, pThis, pThisCC, off, &rc)
3688 | (vga_mem_readb(pDevIns, pThis, pThisCC, off + 1, &rc) << 8)
3689 | (vga_mem_readb(pDevIns, pThis, pThisCC, off + 2, &rc) << 16)
3690 | (vga_mem_readb(pDevIns, pThis, pThisCC, off + 3, &rc) << 24);
3691 break;
3692
3693 case 8:
3694 *(uint64_t *)pv = (uint64_t)vga_mem_readb(pDevIns, pThis, pThisCC, off, &rc)
3695 | ((uint64_t)vga_mem_readb(pDevIns, pThis, pThisCC, off + 1, &rc) << 8)
3696 | ((uint64_t)vga_mem_readb(pDevIns, pThis, pThisCC, off + 2, &rc) << 16)
3697 | ((uint64_t)vga_mem_readb(pDevIns, pThis, pThisCC, off + 3, &rc) << 24)
3698 | ((uint64_t)vga_mem_readb(pDevIns, pThis, pThisCC, off + 4, &rc) << 32)
3699 | ((uint64_t)vga_mem_readb(pDevIns, pThis, pThisCC, off + 5, &rc) << 40)
3700 | ((uint64_t)vga_mem_readb(pDevIns, pThis, pThisCC, off + 6, &rc) << 48)
3701 | ((uint64_t)vga_mem_readb(pDevIns, pThis, pThisCC, off + 7, &rc) << 56);
3702 break;
3703
3704 default:
3705 {
3706 uint8_t *pbData = (uint8_t *)pv;
3707 while (cb-- > 0)
3708 {
3709 *pbData++ = vga_mem_readb(pDevIns, pThis, pThisCC, off++, &rc);
3710 if (RT_UNLIKELY(rc != VINF_SUCCESS))
3711 break;
3712 }
3713 }
3714 }
3715
3716 STAM_PROFILE_STOP(&pThis->CTX_MID_Z(Stat,MemoryRead), a);
3717 return rc;
3718}
3719
3720/**
3721 * @callback_method_impl{FNIOMMMIONEWWRITE,
3722 * Legacy VGA memory (0xa0000 - 0xbffff) write hook\, to be called from IOM.}
3723 *
3724 * @note The @a off is an absolute address in the 0xa0000 - 0xbffff range, not
3725 * an offset.
3726 */
3727static DECLCALLBACK(VBOXSTRICTRC) vgaMmioWrite(PPDMDEVINS pDevIns, void *pvUser, RTGCPHYS off, void const *pv, unsigned cb)
3728{
3729 PVGASTATE pThis = PDMDEVINS_2_DATA(pDevIns, PVGASTATE);
3730 PVGASTATECC pThisCC = PDMDEVINS_2_DATA_CC(pDevIns, PVGASTATECC);
3731 uint8_t const *pbSrc = (uint8_t const *)pv;
3732 NOREF(pvUser);
3733 STAM_PROFILE_START(&pThis->CTX_MID_Z(Stat,MemoryWrite), a);
3734 Assert(PDMDevHlpCritSectIsOwner(pDevIns, pDevIns->CTX_SUFF(pCritSectRo)));
3735
3736 VBOXSTRICTRC rc;
3737 switch (cb)
3738 {
3739 case 1:
3740 rc = vga_mem_writeb(pDevIns, pThis, pThisCC, off, *pbSrc);
3741 break;
3742#if 1
3743 case 2:
3744 rc = vga_mem_writeb(pDevIns, pThis, pThisCC, off + 0, pbSrc[0]);
3745 if (RT_LIKELY(rc == VINF_SUCCESS))
3746 rc = vga_mem_writeb(pDevIns, pThis, pThisCC, off + 1, pbSrc[1]);
3747 break;
3748 case 4:
3749 rc = vga_mem_writeb(pDevIns, pThis, pThisCC, off + 0, pbSrc[0]);
3750 if (RT_LIKELY(rc == VINF_SUCCESS))
3751 rc = vga_mem_writeb(pDevIns, pThis, pThisCC, off + 1, pbSrc[1]);
3752 if (RT_LIKELY(rc == VINF_SUCCESS))
3753 rc = vga_mem_writeb(pDevIns, pThis, pThisCC, off + 2, pbSrc[2]);
3754 if (RT_LIKELY(rc == VINF_SUCCESS))
3755 rc = vga_mem_writeb(pDevIns, pThis, pThisCC, off + 3, pbSrc[3]);
3756 break;
3757 case 8:
3758 rc = vga_mem_writeb(pDevIns, pThis, pThisCC, off + 0, pbSrc[0]);
3759 if (RT_LIKELY(rc == VINF_SUCCESS))
3760 rc = vga_mem_writeb(pDevIns, pThis, pThisCC, off + 1, pbSrc[1]);
3761 if (RT_LIKELY(rc == VINF_SUCCESS))
3762 rc = vga_mem_writeb(pDevIns, pThis, pThisCC, off + 2, pbSrc[2]);
3763 if (RT_LIKELY(rc == VINF_SUCCESS))
3764 rc = vga_mem_writeb(pDevIns, pThis, pThisCC, off + 3, pbSrc[3]);
3765 if (RT_LIKELY(rc == VINF_SUCCESS))
3766 rc = vga_mem_writeb(pDevIns, pThis, pThisCC, off + 4, pbSrc[4]);
3767 if (RT_LIKELY(rc == VINF_SUCCESS))
3768 rc = vga_mem_writeb(pDevIns, pThis, pThisCC, off + 5, pbSrc[5]);
3769 if (RT_LIKELY(rc == VINF_SUCCESS))
3770 rc = vga_mem_writeb(pDevIns, pThis, pThisCC, off + 6, pbSrc[6]);
3771 if (RT_LIKELY(rc == VINF_SUCCESS))
3772 rc = vga_mem_writeb(pDevIns, pThis, pThisCC, off + 7, pbSrc[7]);
3773 break;
3774#else
3775 case 2:
3776 rc = vgaMmioFill(pDevIns, off, *(uint16_t *)pv, 2, 1);
3777 break;
3778 case 4:
3779 rc = vgaMmioFill(pDevIns, off, *(uint32_t *)pv, 4, 1);
3780 break;
3781 case 8:
3782 rc = vgaMmioFill(pDevIns, off, *(uint64_t *)pv, 8, 1);
3783 break;
3784#endif
3785 default:
3786 rc = VINF_SUCCESS;
3787 while (cb-- > 0 && rc == VINF_SUCCESS)
3788 rc = vga_mem_writeb(pDevIns, pThis, pThisCC, off++, *pbSrc++);
3789 break;
3790
3791 }
3792 STAM_PROFILE_STOP(&pThis->CTX_MID_Z(Stat,MemoryWrite), a);
3793 return rc;
3794}
3795
3796
3797/* -=-=-=-=-=- All rings: VGA BIOS I/Os -=-=-=-=-=- */
3798
3799/**
3800 * @callback_method_impl{FNIOMIOPORTNEWIN,
3801 * Port I/O Handler for VGA BIOS IN operations.}
3802 */
3803static DECLCALLBACK(VBOXSTRICTRC) vgaIoPortReadBios(PPDMDEVINS pDevIns, void *pvUser, RTIOPORT offPort, uint32_t *pu32, unsigned cb)
3804{
3805 RT_NOREF(pDevIns, pvUser, offPort, pu32, cb);
3806 return VERR_IOM_IOPORT_UNUSED;
3807}
3808
3809/**
3810 * @callback_method_impl{FNIOMIOPORTNEWOUT,
3811 * Port I/O Handler for VGA BIOS IN operations.}
3812 */
3813static DECLCALLBACK(VBOXSTRICTRC) vgaIoPortWriteBios(PPDMDEVINS pDevIns, void *pvUser, RTIOPORT offPort, uint32_t u32, unsigned cb)
3814{
3815 RT_NOREF2(pDevIns, pvUser);
3816 Assert(PDMDevHlpCritSectIsOwner(pDevIns, pDevIns->CTX_SUFF(pCritSectRo)));
3817 Assert(offPort == 0); RT_NOREF(offPort);
3818
3819 /*
3820 * VGA BIOS char printing.
3821 */
3822 if (cb == 1)
3823 {
3824#if 0
3825 switch (u32)
3826 {
3827 case '\r': Log(("vgabios: <return>\n")); break;
3828 case '\n': Log(("vgabios: <newline>\n")); break;
3829 case '\t': Log(("vgabios: <tab>\n")); break;
3830 default:
3831 Log(("vgabios: %c\n", u32));
3832 }
3833#else
3834 static int s_fLastWasNotNewline = 0; /* We are only called in a single-threaded way */
3835 if (s_fLastWasNotNewline == 0)
3836 Log(("vgabios: "));
3837 if (u32 != '\r') /* return - is only sent in conjunction with '\n' */
3838 Log(("%c", u32));
3839 if (u32 == '\n')
3840 s_fLastWasNotNewline = 0;
3841 else
3842 s_fLastWasNotNewline = 1;
3843#endif
3844 return VINF_SUCCESS;
3845 }
3846
3847 /* not in use. */
3848 return VERR_IOM_IOPORT_UNUSED;
3849}
3850
3851
3852/* -=-=-=-=-=- Ring 3 -=-=-=-=-=- */
3853
3854#ifdef IN_RING3
3855
3856/**
3857 * @callback_method_impl{FNIOMIOPORTNEWOUT,
3858 * Port I/O Handler for VBE Extra OUT operations.}
3859 */
3860static DECLCALLBACK(VBOXSTRICTRC)
3861vbeR3IOPortWriteVbeExtra(PPDMDEVINS pDevIns, void *pvUser, RTIOPORT offPort, uint32_t u32, unsigned cb)
3862{
3863 PVGASTATECC pThisCC = PDMDEVINS_2_DATA_CC(pDevIns, PVGASTATECC);
3864 Assert(PDMDevHlpCritSectIsOwner(pDevIns, pDevIns->CTX_SUFF(pCritSectRo)));
3865 RT_NOREF(offPort, pvUser);
3866
3867 if (cb == 2)
3868 {
3869 Log(("vbeR3IOPortWriteVbeExtra: addr=%#RX32\n", u32));
3870 pThisCC->u16VBEExtraAddress = u32;
3871 }
3872 else
3873 Log(("vbeR3IOPortWriteVbeExtra: Ignoring invalid cb=%d writes to the VBE Extra port!!!\n", cb));
3874
3875 return VINF_SUCCESS;
3876}
3877
3878
3879/**
3880 * @callback_method_impl{FNIOMIOPORTNEWIN,
3881 * Port I/O Handler for VBE Extra IN operations.}
3882 */
3883static DECLCALLBACK(VBOXSTRICTRC)
3884vbeR3IoPortReadVbeExtra(PPDMDEVINS pDevIns, void *pvUser, RTIOPORT offPort, uint32_t *pu32, unsigned cb)
3885{
3886 PVGASTATE pThis = PDMDEVINS_2_DATA(pDevIns, PVGASTATE);
3887 PVGASTATECC pThisCC = PDMDEVINS_2_DATA_CC(pDevIns, PVGASTATECC);
3888 Assert(PDMDevHlpCritSectIsOwner(pDevIns, pDevIns->CTX_SUFF(pCritSectRo)));
3889 RT_NOREF(offPort, pvUser);
3890
3891 int rc = VINF_SUCCESS;
3892 if (pThisCC->u16VBEExtraAddress == 0xffff)
3893 {
3894 Log(("vbeR3IoPortReadVbeExtra: Requested number of 64k video banks\n"));
3895 *pu32 = pThis->vram_size / _64K;
3896 }
3897 else if ( pThisCC->u16VBEExtraAddress >= pThisCC->cbVBEExtraData
3898 || pThisCC->u16VBEExtraAddress + cb > pThisCC->cbVBEExtraData)
3899 {
3900 *pu32 = 0;
3901 Log(("vbeR3IoPortReadVbeExtra: Requested address is out of VBE data!!! Address=%#x(%d) cbVBEExtraData=%#x(%d)\n",
3902 pThisCC->u16VBEExtraAddress, pThisCC->u16VBEExtraAddress, pThisCC->cbVBEExtraData, pThisCC->cbVBEExtraData));
3903 }
3904 else
3905 {
3906 RT_UNTRUSTED_VALIDATED_FENCE();
3907 if (cb == 1)
3908 {
3909 *pu32 = pThisCC->pbVBEExtraData[pThisCC->u16VBEExtraAddress] & 0xFF;
3910
3911 Log(("vbeR3IoPortReadVbeExtra: cb=%#x %.*Rhxs\n", cb, cb, pu32));
3912 }
3913 else if (cb == 2)
3914 {
3915 *pu32 = pThisCC->pbVBEExtraData[pThisCC->u16VBEExtraAddress]
3916 | (uint32_t)pThisCC->pbVBEExtraData[pThisCC->u16VBEExtraAddress + 1] << 8;
3917
3918 Log(("vbeR3IoPortReadVbeExtra: cb=%#x %.*Rhxs\n", cb, cb, pu32));
3919 }
3920 else
3921 {
3922 Log(("vbeR3IoPortReadVbeExtra: Invalid cb=%d read from the VBE Extra port!!!\n", cb));
3923 rc = VERR_IOM_IOPORT_UNUSED;
3924 }
3925 }
3926
3927 return rc;
3928}
3929
3930
3931/**
3932 * Parse the logo bitmap data at init time.
3933 *
3934 * @returns VBox status code.
3935 *
3936 * @param pThisCC The VGA instance data for ring-3.
3937 */
3938static int vbeR3ParseBitmap(PVGASTATECC pThisCC)
3939{
3940 /*
3941 * Get bitmap header data
3942 */
3943 PCLOGOHDR pLogoHdr = (PCLOGOHDR)pThisCC->pbLogo;
3944 PBMPFILEHDR pFileHdr = (PBMPFILEHDR)(pThisCC->pbLogo + sizeof(LOGOHDR));
3945 PBMPWIN3XINFOHDR pCoreHdr = (PBMPWIN3XINFOHDR)(pThisCC->pbLogo + sizeof(LOGOHDR) + sizeof(BMPFILEHDR));
3946
3947 if (pFileHdr->uType == BMP_HDR_MAGIC)
3948 {
3949 switch (pCoreHdr->cbSize)
3950 {
3951 case BMP_HDR_SIZE_OS21:
3952 {
3953 PBMPOS2COREHDR pOs2Hdr = (PBMPOS2COREHDR)pCoreHdr;
3954 pThisCC->cxLogo = pOs2Hdr->uWidth;
3955 pThisCC->cyLogo = pOs2Hdr->uHeight;
3956 pThisCC->cLogoPlanes = pOs2Hdr->cPlanes;
3957 pThisCC->cLogoBits = pOs2Hdr->cBits;
3958 pThisCC->LogoCompression = BMP_COMPRESSION_TYPE_NONE;
3959 pThisCC->cLogoUsedColors = 0;
3960 break;
3961 }
3962
3963 case BMP_HDR_SIZE_OS22:
3964 {
3965 PBMPOS2COREHDR2 pOs22Hdr = (PBMPOS2COREHDR2)pCoreHdr;
3966 pThisCC->cxLogo = pOs22Hdr->uWidth;
3967 pThisCC->cyLogo = pOs22Hdr->uHeight;
3968 pThisCC->cLogoPlanes = pOs22Hdr->cPlanes;
3969 pThisCC->cLogoBits = pOs22Hdr->cBits;
3970 pThisCC->LogoCompression = pOs22Hdr->enmCompression;
3971 pThisCC->cLogoUsedColors = pOs22Hdr->cClrUsed;
3972 break;
3973 }
3974
3975 case BMP_HDR_SIZE_WIN3X:
3976 pThisCC->cxLogo = pCoreHdr->uWidth;
3977 pThisCC->cyLogo = pCoreHdr->uHeight;
3978 pThisCC->cLogoPlanes = pCoreHdr->cPlanes;
3979 pThisCC->cLogoBits = pCoreHdr->cBits;
3980 pThisCC->LogoCompression = pCoreHdr->enmCompression;
3981 pThisCC->cLogoUsedColors = pCoreHdr->cClrUsed;
3982 break;
3983
3984 default:
3985 AssertLogRelMsgFailedReturn(("Unsupported bitmap header size %u.\n", pCoreHdr->cbSize),
3986 VERR_INVALID_PARAMETER);
3987 break;
3988 }
3989
3990 AssertLogRelMsgReturn(pThisCC->cxLogo <= LOGO_MAX_WIDTH && pThisCC->cyLogo <= LOGO_MAX_HEIGHT,
3991 ("Bitmap %ux%u is too big.\n", pThisCC->cxLogo, pThisCC->cyLogo),
3992 VERR_INVALID_PARAMETER);
3993
3994 AssertLogRelMsgReturn(pThisCC->cLogoPlanes == 1,
3995 ("Bitmap planes %u != 1.\n", pThisCC->cLogoPlanes),
3996 VERR_INVALID_PARAMETER);
3997
3998 AssertLogRelMsgReturn(pThisCC->cLogoBits == 4 || pThisCC->cLogoBits == 8 || pThisCC->cLogoBits == 24,
3999 ("Unsupported %u depth.\n", pThisCC->cLogoBits),
4000 VERR_INVALID_PARAMETER);
4001
4002 AssertLogRelMsgReturn(pThisCC->cLogoUsedColors <= 256,
4003 ("Unsupported %u colors.\n", pThisCC->cLogoUsedColors),
4004 VERR_INVALID_PARAMETER);
4005
4006 AssertLogRelMsgReturn(pThisCC->LogoCompression == BMP_COMPRESSION_TYPE_NONE,
4007 ("Unsupported %u compression.\n", pThisCC->LogoCompression),
4008 VERR_INVALID_PARAMETER);
4009
4010 AssertLogRelMsgReturn(pLogoHdr->cbLogo > pFileHdr->offBits,
4011 ("Wrong bitmap data offset %u, cbLogo=%u.\n", pFileHdr->offBits, pLogoHdr->cbLogo),
4012 VERR_INVALID_PARAMETER);
4013
4014 uint32_t const cbFileData = pLogoHdr->cbLogo - pFileHdr->offBits;
4015 uint32_t cbImageData = (uint32_t)pThisCC->cxLogo * pThisCC->cyLogo * pThisCC->cLogoPlanes;
4016 if (pThisCC->cLogoBits == 4)
4017 cbImageData /= 2;
4018 else if (pThisCC->cLogoBits == 24)
4019 cbImageData *= 3;
4020 AssertLogRelMsgReturn(cbImageData <= cbFileData,
4021 ("Wrong BMP header data %u (cbLogo=%u offBits=%u)\n", cbImageData, pFileHdr->offBits, pLogoHdr->cbLogo),
4022 VERR_INVALID_PARAMETER);
4023
4024 AssertLogRelMsgReturn(pLogoHdr->cbLogo == pFileHdr->cbFileSize,
4025 ("Wrong bitmap file size %u, cbLogo=%u.\n", pFileHdr->cbFileSize, pLogoHdr->cbLogo),
4026 VERR_INVALID_PARAMETER);
4027
4028 /*
4029 * Read bitmap palette
4030 */
4031 if (!pThisCC->cLogoUsedColors)
4032 pThisCC->cLogoPalEntries = 1 << (pThisCC->cLogoPlanes * pThisCC->cLogoBits);
4033 else
4034 pThisCC->cLogoPalEntries = pThisCC->cLogoUsedColors;
4035
4036 if (pThisCC->cLogoPalEntries)
4037 {
4038 const uint8_t *pbPal = pThisCC->pbLogo + sizeof(LOGOHDR) + sizeof(BMPFILEHDR) + pCoreHdr->cbSize; /* ASSUMES Size location (safe) */
4039
4040 for (uint16_t i = 0; i < pThisCC->cLogoPalEntries; i++)
4041 {
4042 uint16_t j;
4043 uint32_t u32Pal = 0;
4044
4045 for (j = 0; j < 3; j++)
4046 {
4047 uint8_t b = *pbPal++;
4048 u32Pal <<= 8;
4049 u32Pal |= b;
4050 }
4051
4052 pbPal++; /* skip unused byte */
4053 pThisCC->au32LogoPalette[i] = u32Pal;
4054 }
4055 }
4056
4057 /*
4058 * Bitmap data offset
4059 */
4060 pThisCC->pbLogoBitmap = pThisCC->pbLogo + sizeof(LOGOHDR) + pFileHdr->offBits;
4061 }
4062 else
4063 AssertLogRelMsgFailedReturn(("Not a BMP file.\n"), VERR_INVALID_PARAMETER);
4064
4065 return VINF_SUCCESS;
4066}
4067
4068
4069/**
4070 * Show logo bitmap data.
4071 *
4072 * @param cBits Logo depth.
4073 * @param xLogo Logo X position.
4074 * @param yLogo Logo Y position.
4075 * @param cxLogo Logo width.
4076 * @param cyLogo Logo height.
4077 * @param fInverse True if the bitmask is black on white (only for 1bpp)
4078 * @param iStep Fade in/fade out step.
4079 * @param pu32Palette Palette data.
4080 * @param pbSrc Source buffer.
4081 * @param pbDst Destination buffer.
4082 */
4083static void vbeR3ShowBitmap(uint16_t cBits, uint16_t xLogo, uint16_t yLogo, uint16_t cxLogo, uint16_t cyLogo,
4084 bool fInverse, uint8_t iStep, const uint32_t *pu32Palette, const uint8_t *pbSrc, uint8_t *pbDst)
4085{
4086 uint16_t i;
4087 size_t cbPadBytes = 0;
4088 size_t cbLineDst = LOGO_MAX_WIDTH * 4;
4089 uint16_t cyLeft = cyLogo;
4090
4091 pbDst += xLogo * 4 + yLogo * cbLineDst;
4092
4093 switch (cBits)
4094 {
4095 case 1:
4096 pbDst += cyLogo * cbLineDst;
4097 cbPadBytes = 0;
4098 break;
4099
4100 case 4:
4101 if (((cxLogo % 8) == 0) || ((cxLogo % 8) > 6))
4102 cbPadBytes = 0;
4103 else if ((cxLogo % 8) <= 2)
4104 cbPadBytes = 3;
4105 else if ((cxLogo % 8) <= 4)
4106 cbPadBytes = 2;
4107 else
4108 cbPadBytes = 1;
4109 break;
4110
4111 case 8:
4112 cbPadBytes = ((cxLogo % 4) == 0) ? 0 : (4 - (cxLogo % 4));
4113 break;
4114
4115 case 24:
4116 cbPadBytes = cxLogo % 4;
4117 break;
4118 }
4119
4120 uint8_t j = 0, c = 0;
4121
4122 while (cyLeft-- > 0)
4123 {
4124 uint8_t *pbTmpDst = pbDst;
4125
4126 if (cBits != 1)
4127 j = 0;
4128
4129 for (i = 0; i < cxLogo; i++)
4130 {
4131 switch (cBits)
4132 {
4133 case 1:
4134 {
4135 if (!j)
4136 c = *pbSrc++;
4137
4138 if (c & 1)
4139 {
4140 if (fInverse)
4141 {
4142 *pbTmpDst++ = 0;
4143 *pbTmpDst++ = 0;
4144 *pbTmpDst++ = 0;
4145 pbTmpDst++;
4146 }
4147 else
4148 {
4149 uint8_t pix = 0xFF * iStep / LOGO_SHOW_STEPS;
4150 *pbTmpDst++ = pix;
4151 *pbTmpDst++ = pix;
4152 *pbTmpDst++ = pix;
4153 pbTmpDst++;
4154 }
4155 }
4156 else
4157 pbTmpDst += 4;
4158 c >>= 1;
4159 j = (j + 1) % 8;
4160 break;
4161 }
4162
4163 case 4:
4164 {
4165 if (!j)
4166 c = *pbSrc++;
4167
4168 uint8_t pix = (c >> 4) & 0xF;
4169 c <<= 4;
4170
4171 uint32_t u32Pal = pu32Palette[pix];
4172
4173 pix = (u32Pal >> 16) & 0xFF;
4174 *pbTmpDst++ = pix * iStep / LOGO_SHOW_STEPS;
4175 pix = (u32Pal >> 8) & 0xFF;
4176 *pbTmpDst++ = pix * iStep / LOGO_SHOW_STEPS;
4177 pix = u32Pal & 0xFF;
4178 *pbTmpDst++ = pix * iStep / LOGO_SHOW_STEPS;
4179 pbTmpDst++;
4180
4181 j = (j + 1) % 2;
4182 break;
4183 }
4184
4185 case 8:
4186 {
4187 uint32_t u32Pal = pu32Palette[*pbSrc++];
4188
4189 uint8_t pix = (u32Pal >> 16) & 0xFF;
4190 *pbTmpDst++ = pix * iStep / LOGO_SHOW_STEPS;
4191 pix = (u32Pal >> 8) & 0xFF;
4192 *pbTmpDst++ = pix * iStep / LOGO_SHOW_STEPS;
4193 pix = u32Pal & 0xFF;
4194 *pbTmpDst++ = pix * iStep / LOGO_SHOW_STEPS;
4195 pbTmpDst++;
4196 break;
4197 }
4198
4199 case 24:
4200 *pbTmpDst++ = *pbSrc++ * iStep / LOGO_SHOW_STEPS;
4201 *pbTmpDst++ = *pbSrc++ * iStep / LOGO_SHOW_STEPS;
4202 *pbTmpDst++ = *pbSrc++ * iStep / LOGO_SHOW_STEPS;
4203 pbTmpDst++;
4204 break;
4205 }
4206 }
4207
4208 pbDst -= cbLineDst;
4209 pbSrc += cbPadBytes;
4210 }
4211}
4212
4213
4214/**
4215 * @callback_method_impl{FNIOMIOPORTNEWOUT,
4216 * Port I/O Handler for BIOS Logo OUT operations.}
4217 */
4218static DECLCALLBACK(VBOXSTRICTRC)
4219vbeR3IoPortWriteCmdLogo(PPDMDEVINS pDevIns, void *pvUser, RTIOPORT offPort, uint32_t u32, unsigned cb)
4220{
4221 PVGASTATE pThis = PDMDEVINS_2_DATA(pDevIns, PVGASTATE);
4222 PVGASTATECC pThisCC = PDMDEVINS_2_DATA_CC(pDevIns, PVGASTATECC);
4223 RT_NOREF(pvUser, offPort);
4224
4225 Log(("vbeR3IoPortWriteCmdLogo: cb=%d u32=%#04x(%#04d) (byte)\n", cb, u32, u32));
4226
4227 if (cb == 2)
4228 {
4229 /* Get the logo command */
4230 switch (u32 & 0xFF00)
4231 {
4232 case LOGO_CMD_SET_OFFSET:
4233 pThisCC->offLogoData = u32 & 0xFF;
4234 break;
4235
4236 case LOGO_CMD_SHOW_BMP:
4237 {
4238 uint8_t iStep = u32 & 0xFF;
4239 const uint8_t *pbSrc = pThisCC->pbLogoBitmap;
4240 uint8_t *pbDst;
4241 PCLOGOHDR pLogoHdr = (PCLOGOHDR)pThisCC->pbLogo;
4242 uint32_t offDirty = 0;
4243 uint16_t xLogo = (LOGO_MAX_WIDTH - pThisCC->cxLogo) / 2;
4244 uint16_t yLogo = LOGO_MAX_HEIGHT - (LOGO_MAX_HEIGHT - pThisCC->cyLogo) / 2;
4245
4246 /* Check VRAM size */
4247 if (pThis->vram_size < LOGO_MAX_SIZE)
4248 break;
4249
4250 if (pThis->vram_size >= LOGO_MAX_SIZE * 2)
4251 pbDst = pThisCC->pbVRam + LOGO_MAX_SIZE;
4252 else
4253 pbDst = pThisCC->pbVRam;
4254
4255 /* Clear screen - except on power on... */
4256 if (!pThisCC->fLogoClearScreen)
4257 {
4258 /* Clear vram */
4259 uint32_t *pu32Dst = (uint32_t *)pbDst;
4260 for (int i = 0; i < LOGO_MAX_WIDTH; i++)
4261 for (int j = 0; j < LOGO_MAX_HEIGHT; j++)
4262 *pu32Dst++ = 0;
4263 pThisCC->fLogoClearScreen = true;
4264 }
4265
4266 /* Show the bitmap. */
4267 vbeR3ShowBitmap(pThisCC->cLogoBits, xLogo, yLogo,
4268 pThisCC->cxLogo, pThisCC->cyLogo,
4269 false, iStep, &pThisCC->au32LogoPalette[0],
4270 pbSrc, pbDst);
4271
4272 /* Show the 'Press F12...' text. */
4273 if (pLogoHdr->fu8ShowBootMenu == 2)
4274 vbeR3ShowBitmap(1, LOGO_F12TEXT_X, LOGO_F12TEXT_Y,
4275 LOGO_F12TEXT_WIDTH, LOGO_F12TEXT_HEIGHT,
4276 pThisCC->fBootMenuInverse, iStep, &pThisCC->au32LogoPalette[0],
4277 &g_abLogoF12BootText[0], pbDst);
4278
4279 /* Blit the offscreen buffer. */
4280 if (pThis->vram_size >= LOGO_MAX_SIZE * 2)
4281 {
4282 uint32_t *pu32TmpDst = (uint32_t *)pThisCC->pbVRam;
4283 uint32_t *pu32TmpSrc = (uint32_t *)(pThisCC->pbVRam + LOGO_MAX_SIZE);
4284 for (int i = 0; i < LOGO_MAX_WIDTH; i++)
4285 {
4286 for (int j = 0; j < LOGO_MAX_HEIGHT; j++)
4287 *pu32TmpDst++ = *pu32TmpSrc++;
4288 }
4289 }
4290
4291 /* Set the dirty flags. */
4292 while (offDirty <= LOGO_MAX_SIZE)
4293 {
4294 vgaR3MarkDirty(pThis, offDirty);
4295 offDirty += GUEST_PAGE_SIZE;
4296 }
4297 break;
4298 }
4299
4300 default:
4301 Log(("vbeR3IoPortWriteCmdLogo: invalid command %d\n", u32));
4302 pThisCC->LogoCommand = LOGO_CMD_NOP;
4303 break;
4304 }
4305
4306 return VINF_SUCCESS;
4307 }
4308
4309 Log(("vbeR3IoPortWriteCmdLogo: Ignoring invalid cb=%d writes to the VBE Extra port!!!\n", cb));
4310 return VINF_SUCCESS;
4311}
4312
4313
4314/**
4315 * @callback_method_impl{FNIOMIOPORTIN,
4316 * Port I/O Handler for BIOS Logo IN operations.}
4317 */
4318static DECLCALLBACK(VBOXSTRICTRC)
4319vbeR3IoPortReadCmdLogo(PPDMDEVINS pDevIns, void *pvUser, RTIOPORT offPort, uint32_t *pu32, unsigned cb)
4320{
4321 PVGASTATECC pThisCC = PDMDEVINS_2_DATA_CC(pDevIns, PVGASTATECC);
4322 RT_NOREF(pvUser, offPort);
4323
4324 if (pThisCC->offLogoData + cb > pThisCC->cbLogo)
4325 {
4326 Log(("vbeR3IoPortReadCmdLogo: Requested address is out of Logo data!!! offLogoData=%#x(%d) cbLogo=%#x(%d)\n",
4327 pThisCC->offLogoData, pThisCC->offLogoData, pThisCC->cbLogo, pThisCC->cbLogo));
4328 return VINF_SUCCESS;
4329 }
4330 RT_UNTRUSTED_VALIDATED_FENCE();
4331
4332 PCRTUINT64U p = (PCRTUINT64U)&pThisCC->pbLogo[pThisCC->offLogoData];
4333 switch (cb)
4334 {
4335 case 1: *pu32 = p->au8[0]; break;
4336 case 2: *pu32 = p->au16[0]; break;
4337 case 4: *pu32 = p->au32[0]; break;
4338 //case 8: *pu32 = p->au64[0]; break;
4339 default: AssertFailed(); break;
4340 }
4341 Log(("vbeR3IoPortReadCmdLogo: LogoOffset=%#x(%d) cb=%#x %.*Rhxs\n", pThisCC->offLogoData, pThisCC->offLogoData, cb, cb, pu32));
4342
4343 pThisCC->LogoCommand = LOGO_CMD_NOP;
4344 pThisCC->offLogoData += cb;
4345
4346 return VINF_SUCCESS;
4347}
4348
4349
4350/* -=-=-=-=-=- Ring 3: Debug Info Handlers -=-=-=-=-=- */
4351
4352/**
4353 * @callback_method_impl{FNDBGFHANDLERDEV,
4354 * Dumps several interesting bits of the VGA state that are difficult to
4355 * decode from the registers.}
4356 */
4357static DECLCALLBACK(void) vgaR3InfoState(PPDMDEVINS pDevIns, PCDBGFINFOHLP pHlp, const char *pszArgs)
4358{
4359 PVGASTATE pThis = PDMDEVINS_2_DATA(pDevIns, PVGASTATE);
4360 int is_graph, double_scan;
4361 int w, h, char_height, char_dots;
4362 int val, vfreq_hz, hfreq_hz;
4363 vga_retrace_s *r = &pThis->retrace_state;
4364 const char *clocks[] = { "25.175 MHz", "28.322 MHz", "External", "Reserved?!" };
4365 const char *mem_map[] = { "A000-BFFF", "A000-AFFF", "B000-B7FF", "B800-BFFF" };
4366 NOREF(pszArgs);
4367
4368 is_graph = pThis->gr[6] & 1;
4369 char_dots = (pThis->sr[0x01] & 1) ? 8 : 9;
4370 double_scan = pThis->cr[9] >> 7;
4371 pHlp->pfnPrintf(pHlp, "decoding memory at %s\n", mem_map[(pThis->gr[6] >> 2) & 3]);
4372 pHlp->pfnPrintf(pHlp, "Misc status reg. MSR:%02X\n", pThis->msr);
4373 pHlp->pfnPrintf(pHlp, "pixel clock: %s\n", clocks[(pThis->msr >> 2) & 3]);
4374 pHlp->pfnPrintf(pHlp, "double scanning %s\n", double_scan ? "on" : "off");
4375 pHlp->pfnPrintf(pHlp, "double clocking %s\n", pThis->sr[1] & 0x08 ? "on" : "off");
4376 val = pThis->cr[0] + 5;
4377 pHlp->pfnPrintf(pHlp, "htotal: %d px (%d cclk)\n", val * char_dots, val);
4378 val = pThis->cr[6] + ((pThis->cr[7] & 1) << 8) + ((pThis->cr[7] & 0x20) << 4) + 2;
4379 pHlp->pfnPrintf(pHlp, "vtotal: %d px\n", val);
4380 val = pThis->cr[1] + 1;
4381 w = val * char_dots;
4382 pHlp->pfnPrintf(pHlp, "hdisp : %d px (%d cclk)\n", w, val);
4383 val = pThis->cr[0x12] + ((pThis->cr[7] & 2) << 7) + ((pThis->cr[7] & 0x40) << 4) + 1;
4384 h = val;
4385 pHlp->pfnPrintf(pHlp, "vdisp : %d px\n", val);
4386 val = ((pThis->cr[9] & 0x40) << 3) + ((pThis->cr[7] & 0x10) << 4) + pThis->cr[0x18];
4387 pHlp->pfnPrintf(pHlp, "split : %d ln\n", val);
4388 val = (pThis->cr[0xc] << 8) + pThis->cr[0xd];
4389 pHlp->pfnPrintf(pHlp, "start : %#x\n", val);
4390 if (!is_graph)
4391 {
4392 uint8_t ch_stride;
4393
4394 ch_stride = pThis->cr[0x17] & 0x40 ? 4 : 8;
4395 val = (pThis->cr[9] & 0x1f) + 1;
4396 char_height = val;
4397 pHlp->pfnPrintf(pHlp, "char height %d\n", val);
4398 pHlp->pfnPrintf(pHlp, "text mode %dx%d\n", w / char_dots, h / (char_height << double_scan));
4399
4400 uint32_t cbLine;
4401 uint32_t offStart;
4402 uint32_t offCursr;
4403 uint32_t uLineCompareIgn;
4404 vgaR3GetOffsets(pThis, &cbLine, &offStart, &uLineCompareIgn);
4405 if (!cbLine)
4406 cbLine = 80 * ch_stride;
4407 offStart *= ch_stride;
4408 offCursr = ((pThis->cr[0x0e] << 8) | pThis->cr[0x0f]) * ch_stride;
4409 pHlp->pfnPrintf(pHlp, "cbLine: %#x\n", cbLine);
4410 pHlp->pfnPrintf(pHlp, "offStart: %#x (line %#x)\n", offStart, offStart / cbLine);
4411 pHlp->pfnPrintf(pHlp, "offCursr: %#x\n", offCursr);
4412 }
4413 if (pThis->fRealRetrace)
4414 {
4415 val = r->hb_start;
4416 pHlp->pfnPrintf(pHlp, "hblank start: %d px (%d cclk)\n", val * char_dots, val);
4417 val = r->hb_end;
4418 pHlp->pfnPrintf(pHlp, "hblank end : %d px (%d cclk)\n", val * char_dots, val);
4419 pHlp->pfnPrintf(pHlp, "vblank start: %d px, end: %d px\n", r->vb_start, r->vb_end);
4420 pHlp->pfnPrintf(pHlp, "vsync start : %d px, end: %d px\n", r->vs_start, r->vs_end);
4421 pHlp->pfnPrintf(pHlp, "cclks per frame: %d\n", r->frame_cclks);
4422 pHlp->pfnPrintf(pHlp, "cclk time (ns) : %d\n", r->cclk_ns);
4423 if (r->frame_ns && r->h_total_ns) /* Careful in case state is temporarily invalid. */
4424 {
4425 vfreq_hz = 1000000000 / r->frame_ns;
4426 hfreq_hz = 1000000000 / r->h_total_ns;
4427 pHlp->pfnPrintf(pHlp, "vfreq: %d Hz, hfreq: %d.%03d kHz\n",
4428 vfreq_hz, hfreq_hz / 1000, hfreq_hz % 1000);
4429 }
4430 }
4431 pHlp->pfnPrintf(pHlp, "display refresh interval: %u ms\n", pThis->cMilliesRefreshInterval);
4432
4433# ifdef VBOX_WITH_VMSVGA
4434 if (pThis->svga.fEnabled)
4435 pHlp->pfnPrintf(pHlp, pThis->svga.f3DEnabled ? "VMSVGA 3D enabled: %ux%ux%u\n" : "VMSVGA enabled: %ux%ux%u",
4436 pThis->svga.uWidth, pThis->svga.uHeight, pThis->svga.uBpp);
4437# endif
4438}
4439
4440
4441/**
4442 * Prints a separator line.
4443 *
4444 * @param pHlp Callback functions for doing output.
4445 * @param cCols The number of columns.
4446 * @param pszTitle The title text, NULL if none.
4447 */
4448static void vgaR3InfoTextPrintSeparatorLine(PCDBGFINFOHLP pHlp, size_t cCols, const char *pszTitle)
4449{
4450 if (pszTitle)
4451 {
4452 size_t cchTitle = strlen(pszTitle);
4453 if (cchTitle + 6 >= cCols)
4454 {
4455 pHlp->pfnPrintf(pHlp, "-- %s --", pszTitle);
4456 cCols = 0;
4457 }
4458 else
4459 {
4460 size_t cchLeft = (cCols - cchTitle - 2) / 2;
4461 cCols -= cchLeft + cchTitle + 2;
4462 while (cchLeft-- > 0)
4463 pHlp->pfnPrintf(pHlp, "-");
4464 pHlp->pfnPrintf(pHlp, " %s ", pszTitle);
4465 }
4466 }
4467
4468 while (cCols-- > 0)
4469 pHlp->pfnPrintf(pHlp, "-");
4470 pHlp->pfnPrintf(pHlp, "\n");
4471}
4472
4473
4474/**
4475 * Worker for vgaR3InfoText.
4476 *
4477 * @param pThis The shared VGA state.
4478 * @param pThisCC The VGA state for ring-3.
4479 * @param pHlp Callback functions for doing output.
4480 * @param offStart Where to start dumping (relative to the VRAM).
4481 * @param cbLine The source line length (aka line_offset).
4482 * @param cCols The number of columns on the screen.
4483 * @param cRows The number of rows to dump.
4484 * @param iScrBegin The row at which the current screen output starts.
4485 * @param iScrEnd The row at which the current screen output end
4486 * (exclusive).
4487 */
4488static void vgaR3InfoTextWorker(PVGASTATE pThis, PVGASTATER3 pThisCC, PCDBGFINFOHLP pHlp,
4489 uint32_t offStart, uint32_t cbLine,
4490 uint32_t cCols, uint32_t cRows,
4491 uint32_t iScrBegin, uint32_t iScrEnd)
4492{
4493 /* Title, */
4494 char szTitle[32];
4495 if (iScrBegin || iScrEnd < cRows)
4496 RTStrPrintf(szTitle, sizeof(szTitle), "%ux%u (+%u before, +%u after)",
4497 cCols, iScrEnd - iScrBegin, iScrBegin, cRows - iScrEnd);
4498 else
4499 RTStrPrintf(szTitle, sizeof(szTitle), "%ux%u", cCols, iScrEnd - iScrBegin);
4500
4501 /* Do the dumping. */
4502 uint8_t const *pbSrcOuter = pThisCC->pbVRam + offStart;
4503 uint8_t const cStride = pThis->cr[0x17] & 0x40 ? 4 : 8;
4504 uint32_t iRow;
4505 for (iRow = 0; iRow < cRows; iRow++, pbSrcOuter += cbLine)
4506 {
4507 if ((uintptr_t)(pbSrcOuter + cbLine - pThisCC->pbVRam) > pThis->vram_size) {
4508 pHlp->pfnPrintf(pHlp, "The last %u row/rows is/are outside the VRAM.\n", cRows - iRow);
4509 break;
4510 }
4511
4512 if (iRow == 0)
4513 vgaR3InfoTextPrintSeparatorLine(pHlp, cCols, szTitle);
4514 else if (iRow == iScrBegin)
4515 vgaR3InfoTextPrintSeparatorLine(pHlp, cCols, "screen start");
4516 else if (iRow == iScrEnd)
4517 vgaR3InfoTextPrintSeparatorLine(pHlp, cCols, "screen end");
4518
4519 uint8_t const *pbSrc = pbSrcOuter;
4520 for (uint32_t iCol = 0; iCol < cCols; ++iCol)
4521 {
4522 if (RT_C_IS_PRINT(*pbSrc))
4523 pHlp->pfnPrintf(pHlp, "%c", *pbSrc);
4524 else
4525 pHlp->pfnPrintf(pHlp, ".");
4526 pbSrc += cStride; /* chars are spaced 8 or sometimes 4 bytes apart */
4527 }
4528 pHlp->pfnPrintf(pHlp, "\n");
4529 }
4530
4531 /* Final separator. */
4532 vgaR3InfoTextPrintSeparatorLine(pHlp, cCols, NULL);
4533}
4534
4535
4536/**
4537 * @callback_method_impl{FNDBGFHANDLERDEV,
4538 * Dumps VGA memory formatted as ASCII text\, no attributes. Only looks at
4539 * the first page.}
4540 */
4541static DECLCALLBACK(void) vgaR3InfoText(PPDMDEVINS pDevIns, PCDBGFINFOHLP pHlp, const char *pszArgs)
4542{
4543 PVGASTATE pThis = PDMDEVINS_2_DATA(pDevIns, PVGASTATE);
4544 PVGASTATECC pThisCC = PDMDEVINS_2_DATA_CC(pDevIns, PVGASTATECC);
4545
4546 /*
4547 * Parse args.
4548 */
4549 bool fAll = true;
4550 if (pszArgs && *pszArgs)
4551 {
4552 if (!strcmp(pszArgs, "all"))
4553 fAll = true;
4554 else if (!strcmp(pszArgs, "scr") || !strcmp(pszArgs, "screen"))
4555 fAll = false;
4556 else
4557 {
4558 pHlp->pfnPrintf(pHlp, "Invalid argument: '%s'\n", pszArgs);
4559 return;
4560 }
4561 }
4562
4563 /*
4564 * Check that we're in text mode and that the VRAM is accessible.
4565 */
4566 if (!(pThis->gr[6] & 1))
4567 {
4568 uint8_t *pbSrc = pThisCC->pbVRam;
4569 if (pbSrc)
4570 {
4571 /*
4572 * Figure out the display size and where the text is.
4573 *
4574 * Note! We're cutting quite a few corners here and this code could
4575 * do with some brushing up. Dumping from the start of the
4576 * frame buffer is done intentionally so that we're more
4577 * likely to obtain the full scrollback of a linux panic.
4578 * windbg> .printf "------ start -----\n"; .for (r $t0 = 0; @$t0 < 25; r $t0 = @$t0 + 1) { .for (r $t1 = 0; @$t1 < 80; r $t1 = @$t1 + 1) { .printf "%c", by( (@$t0 * 80 + @$t1) * 8 + 100f0000) }; .printf "\n" }; .printf "------ end -----\n";
4579 */
4580 uint32_t cbLine;
4581 uint32_t offStart;
4582 uint32_t uLineCompareIgn;
4583 vgaR3GetOffsets(pThis, &cbLine, &offStart, &uLineCompareIgn);
4584 if (!cbLine)
4585 cbLine = 80 * 8;
4586 offStart *= 8;
4587
4588 uint32_t uVDisp = pThis->cr[0x12] + ((pThis->cr[7] & 2) << 7) + ((pThis->cr[7] & 0x40) << 4) + 1;
4589 uint32_t uCharHeight = (pThis->cr[9] & 0x1f) + 1;
4590 uint32_t uDblScan = pThis->cr[9] >> 7;
4591 uint32_t cScrRows = uVDisp / (uCharHeight << uDblScan);
4592 if (cScrRows < 25)
4593 cScrRows = 25;
4594 uint32_t iScrBegin = offStart / cbLine;
4595 uint32_t cRows = iScrBegin + cScrRows;
4596 uint32_t cCols = cbLine / 8;
4597
4598 if (fAll)
4599 vgaR3InfoTextWorker(pThis, pThisCC, pHlp, offStart - iScrBegin * cbLine, cbLine,
4600 cCols, cRows, iScrBegin, iScrBegin + cScrRows);
4601 else
4602 vgaR3InfoTextWorker(pThis, pThisCC, pHlp, offStart, cbLine, cCols, cScrRows, 0, cScrRows);
4603 }
4604 else
4605 pHlp->pfnPrintf(pHlp, "VGA memory not available!\n");
4606 }
4607 else
4608 pHlp->pfnPrintf(pHlp, "Not in text mode!\n");
4609}
4610
4611
4612/**
4613 * @callback_method_impl{FNDBGFHANDLERDEV, Dumps VGA Sequencer registers.}
4614 */
4615static DECLCALLBACK(void) vgaR3InfoSR(PPDMDEVINS pDevIns, PCDBGFINFOHLP pHlp, const char *pszArgs)
4616{
4617 PVGASTATE pThis = PDMDEVINS_2_DATA(pDevIns, PVGASTATE);
4618 NOREF(pszArgs);
4619
4620 pHlp->pfnPrintf(pHlp, "VGA Sequencer (3C5): SR index 3C4:%02X\n", pThis->sr_index);
4621 Assert(sizeof(pThis->sr) >= 8);
4622 for (unsigned i = 0; i < 8; ++i)
4623 pHlp->pfnPrintf(pHlp, " SR%02X:%02X", i, pThis->sr[i]);
4624 pHlp->pfnPrintf(pHlp, "\n");
4625}
4626
4627
4628/**
4629 * @callback_method_impl{FNDBGFHANDLERDEV, Dumps VGA CRTC registers.}
4630 */
4631static DECLCALLBACK(void) vgaR3InfoCR(PPDMDEVINS pDevIns, PCDBGFINFOHLP pHlp, const char *pszArgs)
4632{
4633 PVGASTATE pThis = PDMDEVINS_2_DATA(pDevIns, PVGASTATE);
4634 unsigned i;
4635 NOREF(pszArgs);
4636
4637 pHlp->pfnPrintf(pHlp, "VGA CRTC (3D5): CRTC index 3D4:%02X\n", pThis->cr_index);
4638 Assert(sizeof(pThis->cr) >= 24);
4639 for (i = 0; i < 10; ++i)
4640 pHlp->pfnPrintf(pHlp, " CR%02X:%02X", i, pThis->cr[i]);
4641 pHlp->pfnPrintf(pHlp, "\n");
4642 for (i = 10; i < 20; ++i)
4643 pHlp->pfnPrintf(pHlp, " CR%02X:%02X", i, pThis->cr[i]);
4644 pHlp->pfnPrintf(pHlp, "\n");
4645 for (i = 20; i < 25; ++i)
4646 pHlp->pfnPrintf(pHlp, " CR%02X:%02X", i, pThis->cr[i]);
4647 pHlp->pfnPrintf(pHlp, "\n");
4648}
4649
4650
4651/**
4652 * @callback_method_impl{FNDBGFHANDLERDEV,
4653 * Dumps VGA Graphics Controller registers.}
4654 */
4655static DECLCALLBACK(void) vgaR3InfoGR(PPDMDEVINS pDevIns, PCDBGFINFOHLP pHlp, const char *pszArgs)
4656{
4657 PVGASTATE pThis = PDMDEVINS_2_DATA(pDevIns, PVGASTATE);
4658 NOREF(pszArgs);
4659
4660 pHlp->pfnPrintf(pHlp, "VGA Graphics Controller (3CF): GR index 3CE:%02X\n", pThis->gr_index);
4661 Assert(sizeof(pThis->gr) >= 9);
4662 for (unsigned i = 0; i < 9; ++i)
4663 pHlp->pfnPrintf(pHlp, " GR%02X:%02X", i, pThis->gr[i]);
4664 pHlp->pfnPrintf(pHlp, "\n");
4665}
4666
4667
4668/**
4669 * @callback_method_impl{FNDBGFHANDLERDEV,
4670 * Dumps VGA Attribute Controller registers.}
4671 */
4672static DECLCALLBACK(void) vgaR3InfoAR(PPDMDEVINS pDevIns, PCDBGFINFOHLP pHlp, const char *pszArgs)
4673{
4674 PVGASTATE pThis = PDMDEVINS_2_DATA(pDevIns, PVGASTATE);
4675 unsigned i;
4676 NOREF(pszArgs);
4677
4678 pHlp->pfnPrintf(pHlp, "VGA Attribute Controller (3C0): index reg %02X, flip-flop: %d (%s)\n",
4679 pThis->ar_index, pThis->ar_flip_flop, pThis->ar_flip_flop ? "data" : "index" );
4680 Assert(sizeof(pThis->ar) >= 0x14);
4681 pHlp->pfnPrintf(pHlp, " Palette:");
4682 for (i = 0; i < 0x10; ++i)
4683 pHlp->pfnPrintf(pHlp, " %02X", pThis->ar[i]);
4684 pHlp->pfnPrintf(pHlp, "\n");
4685 for (i = 0x10; i <= 0x14; ++i)
4686 pHlp->pfnPrintf(pHlp, " AR%02X:%02X", i, pThis->ar[i]);
4687 pHlp->pfnPrintf(pHlp, "\n");
4688}
4689
4690
4691/**
4692 * @callback_method_impl{FNDBGFHANDLERDEV, Dumps VGA DAC registers.}
4693 */
4694static DECLCALLBACK(void) vgaR3InfoDAC(PPDMDEVINS pDevIns, PCDBGFINFOHLP pHlp, const char *pszArgs)
4695{
4696 PVGASTATE pThis = PDMDEVINS_2_DATA(pDevIns, PVGASTATE);
4697 NOREF(pszArgs);
4698
4699 pHlp->pfnPrintf(pHlp, "VGA DAC contents:\n");
4700 for (unsigned i = 0; i < 0x100; ++i)
4701 pHlp->pfnPrintf(pHlp, " %02X: %02X %02X %02X\n",
4702 i, pThis->palette[i*3+0], pThis->palette[i*3+1], pThis->palette[i*3+2]);
4703}
4704
4705
4706/**
4707 * @callback_method_impl{FNDBGFHANDLERDEV, Dumps VBE registers.}
4708 */
4709static DECLCALLBACK(void) vgaR3InfoVBE(PPDMDEVINS pDevIns, PCDBGFINFOHLP pHlp, const char *pszArgs)
4710{
4711 PVGASTATE pThis = PDMDEVINS_2_DATA(pDevIns, PVGASTATE);
4712 NOREF(pszArgs);
4713
4714 pHlp->pfnPrintf(pHlp, "LFB at %RGp\n", pThis->GCPhysVRAM);
4715 if (!(pThis->vbe_regs[VBE_DISPI_INDEX_ENABLE] & VBE_DISPI_ENABLED))
4716 pHlp->pfnPrintf(pHlp, "VBE disabled\n");
4717 else
4718 {
4719 pHlp->pfnPrintf(pHlp, "VBE state (chip ID 0x%04x):\n", pThis->vbe_regs[VBE_DISPI_INDEX_ID]);
4720 pHlp->pfnPrintf(pHlp, " Display resolution: %d x %d @ %dbpp\n",
4721 pThis->vbe_regs[VBE_DISPI_INDEX_XRES], pThis->vbe_regs[VBE_DISPI_INDEX_YRES],
4722 pThis->vbe_regs[VBE_DISPI_INDEX_BPP]);
4723 pHlp->pfnPrintf(pHlp, " Virtual resolution: %d x %d\n",
4724 pThis->vbe_regs[VBE_DISPI_INDEX_VIRT_WIDTH], pThis->vbe_regs[VBE_DISPI_INDEX_VIRT_HEIGHT]);
4725 pHlp->pfnPrintf(pHlp, " Display start addr: %d, %d\n",
4726 pThis->vbe_regs[VBE_DISPI_INDEX_X_OFFSET], pThis->vbe_regs[VBE_DISPI_INDEX_Y_OFFSET]);
4727 pHlp->pfnPrintf(pHlp, " Linear scanline pitch: 0x%04x\n", pThis->vbe_line_offset);
4728 pHlp->pfnPrintf(pHlp, " Linear display start : 0x%04x\n", pThis->vbe_start_addr);
4729 pHlp->pfnPrintf(pHlp, " Selected bank: 0x%04x\n", pThis->vbe_regs[VBE_DISPI_INDEX_BANK]);
4730 pHlp->pfnPrintf(pHlp, " DAC: %d-bit\n", pThis->vbe_regs[VBE_DISPI_INDEX_ENABLE] & VBE_DISPI_8BIT_DAC ? 8 : 6);
4731 }
4732}
4733
4734
4735/**
4736 * @callback_method_impl{FNDBGFHANDLERDEV,
4737 * Dumps register state relevant to 16-color planar graphics modes (GR/SR)
4738 * in human-readable form.}
4739 */
4740static DECLCALLBACK(void) vgaR3InfoPlanar(PPDMDEVINS pDevIns, PCDBGFINFOHLP pHlp, const char *pszArgs)
4741{
4742 PVGASTATE pThis = PDMDEVINS_2_DATA(pDevIns, PVGASTATE);
4743 NOREF(pszArgs);
4744
4745 unsigned val1 = (pThis->gr[5] >> 3) & 1;
4746 unsigned val2 = pThis->gr[5] & 3;
4747 pHlp->pfnPrintf(pHlp, "read mode : %u write mode: %u\n", val1, val2);
4748 val1 = pThis->gr[0];
4749 val2 = pThis->gr[1];
4750 pHlp->pfnPrintf(pHlp, "set/reset data: %02X S/R enable: %02X\n", val1, val2);
4751 val1 = pThis->gr[2];
4752 val2 = pThis->gr[4] & 3;
4753 pHlp->pfnPrintf(pHlp, "color compare : %02X read map : %u\n", val1, val2);
4754 val1 = pThis->gr[3] & 7;
4755 val2 = (pThis->gr[3] >> 3) & 3;
4756 pHlp->pfnPrintf(pHlp, "rotate : %u function : %u\n", val1, val2);
4757 val1 = pThis->gr[7];
4758 val2 = pThis->gr[8];
4759 pHlp->pfnPrintf(pHlp, "don't care : %02X bit mask : %02X\n", val1, val2);
4760 val1 = pThis->sr[2];
4761 val2 = pThis->sr[4] & 8;
4762 pHlp->pfnPrintf(pHlp, "seq plane mask: %02X chain-4 : %s\n", val1, val2 ? "on" : "off");
4763}
4764
4765
4766/* -=-=-=-=-=- Ring 3: IBase -=-=-=-=-=- */
4767
4768/**
4769 * @interface_method_impl{PDMIBASE,pfnQueryInterface}
4770 */
4771static DECLCALLBACK(void *) vgaR3PortQueryInterface(PPDMIBASE pInterface, const char *pszIID)
4772{
4773 PVGASTATECC pThisCC = RT_FROM_MEMBER(pInterface, VGASTATECC, IBase);
4774 PDMIBASE_RETURN_INTERFACE(pszIID, PDMIBASE, &pThisCC->IBase);
4775 PDMIBASE_RETURN_INTERFACE(pszIID, PDMIDISPLAYPORT, &pThisCC->IPort);
4776# if defined(VBOX_WITH_HGSMI) && defined(VBOX_WITH_VIDEOHWACCEL)
4777 PDMIBASE_RETURN_INTERFACE(pszIID, PDMIDISPLAYVBVACALLBACKS, &pThisCC->IVBVACallbacks);
4778# endif
4779 PDMIBASE_RETURN_INTERFACE(pszIID, PDMILEDPORTS, &pThisCC->ILeds);
4780 return NULL;
4781}
4782
4783
4784/* -=-=-=-=-=- Ring 3: ILe