VirtualBox

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

Last change on this file since 521 was 521, checked in by vboxsync, 18 years ago

cosmetical fixes

  • Property svn:eol-style set to native
File size: 156.9 KB
Line 
1#ifdef VBOX
2/** @file
3 *
4 * VBox VGA/VESA device
5 */
6
7/*
8 * Copyright (C) 2006 InnoTek Systemberatung GmbH
9 *
10 * This file is part of VirtualBox Open Source Edition (OSE), as
11 * available from http://www.virtualbox.org. This file is free software;
12 * you can redistribute it and/or modify it under the terms of the GNU
13 * General Public License as published by the Free Software Foundation,
14 * in version 2 as it comes in the "COPYING" file of the VirtualBox OSE
15 * distribution. VirtualBox OSE is distributed in the hope that it will
16 * be useful, but WITHOUT ANY WARRANTY of any kind.
17 *
18 * If you received this file as part of a commercial VirtualBox
19 * distribution, then only the terms of your commercial VirtualBox
20 * license agreement apply instead of the previous paragraph.
21 *
22 * --------------------------------------------------------------------
23 *
24 * This code is based on:
25 *
26 * QEMU VGA Emulator.
27 *
28 * Copyright (c) 2003 Fabrice Bellard
29 *
30 * Permission is hereby granted, free of charge, to any person obtaining a copy
31 * of this software and associated documentation files (the "Software"), to deal
32 * in the Software without restriction, including without limitation the rights
33 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
34 * copies of the Software, and to permit persons to whom the Software is
35 * furnished to do so, subject to the following conditions:
36 *
37 * The above copyright notice and this permission notice shall be included in
38 * all copies or substantial portions of the Software.
39 *
40 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
41 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
42 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
43 * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
44 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
45 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
46 * THE SOFTWARE.
47 */
48
49/*******************************************************************************
50* Defined Constants And Macros *
51*******************************************************************************/
52/** The default amount of VRAM. */
53#define VGA_VRAM_DEFAULT (_4M)
54/** The maximum amount of VRAM. */
55#define VGA_VRAM_MAX (128 * _1M)
56/** The minimum amount of VRAM. */
57#define VGA_VRAM_MIN (_1M)
58
59/** The size of the VGA GC mapping.
60 * This is supposed to be all the VGA memory accessible to the guest.
61 * The initial value was 256KB but NTAllInOne.iso appears to access more
62 * thus the limit was upped to 512KB.
63 *
64 * @todo Someone with some VGA knowhow should make a better guess at this value.
65 */
66#define VGA_MAPPING_SIZE _512K
67
68/** Converts a vga adaptor state pointer to a device instance pointer. */
69#define VGASTATE2DEVINS(pVgaState) ((pVgaState)->CTXSUFF(pDevIns))
70
71/** Use VBE bytewise I/O */
72#define VBE_BYTEWISE_IO
73
74/** Use VBE new dynamic mode list.
75 * If this is not defined, no checks are carried out to see if the modes all
76 * fit into the framebuffer! See the VRAM_SIZE_FIX define. */
77#define VBE_NEW_DYN_LIST
78
79/** Check that the video modes fit into virtual video memory.
80 * Only works when VBE_NEW_DYN_LIST is defined! */
81#define VRAM_SIZE_FIX
82
83/** Some fixes to ensure that logical scan-line lengths are not overwritten. */
84#define KEEP_SCAN_LINE_LENGTH
85
86
87/*******************************************************************************
88* Header Files *
89*******************************************************************************/
90#define LOG_GROUP LOG_GROUP_DEV_VGA
91#include <VBox/pdm.h>
92#include <VBox/stam.h>
93#include <VBox/pgm.h>
94#include <VBox/mm.h>
95#include <VBox/err.h>
96
97#include <VBox/log.h>
98#include <iprt/assert.h>
99#include <iprt/asm.h>
100#include <iprt/string.h>
101
102#include <VBox/VBoxGuest.h>
103
104#if defined(VBE_NEW_DYN_LIST) && defined(IN_RING3) && !defined(VBOX_DEVICE_STRUCT_TESTCASE)
105# include "DevVGAModes.h"
106# include <stdio.h> /* sscan */
107#endif
108
109#include "vl_vbox.h"
110#include "DevVGA.h"
111#include "Builtins.h"
112#include "Builtins2.h"
113
114
115#ifndef VBOX_DEVICE_STRUCT_TESTCASE
116/*******************************************************************************
117* Internal Functions *
118*******************************************************************************/
119__BEGIN_DECLS
120
121PDMBOTHCBDECL(int) vgaIOPortWrite(PPDMDEVINS pDevIns, void *pvUser, RTIOPORT Port, uint32_t u32, unsigned cb);
122PDMBOTHCBDECL(int) vgaIOPortRead(PPDMDEVINS pDevIns, void *pvUser, RTIOPORT Port, uint32_t *pu32, unsigned cb);
123PDMBOTHCBDECL(int) vgaIOPortWriteVBEIndex(PPDMDEVINS pDevIns, void *pvUser, RTIOPORT Port, uint32_t u32, unsigned cb);
124PDMBOTHCBDECL(int) vgaIOPortWriteVBEData(PPDMDEVINS pDevIns, void *pvUser, RTIOPORT Port, uint32_t u32, unsigned cb);
125PDMBOTHCBDECL(int) vgaIOPortReadVBEIndex(PPDMDEVINS pDevIns, void *pvUser, RTIOPORT Port, uint32_t *pu32, unsigned cb);
126PDMBOTHCBDECL(int) vgaIOPortReadVBEData(PPDMDEVINS pDevIns, void *pvUser, RTIOPORT Port, uint32_t *pu32, unsigned cb);
127PDMBOTHCBDECL(int) vgaMMIOFill(PPDMDEVINS pDevIns, void *pvUser, RTGCPHYS GCPhysAddr, uint32_t u32Item, unsigned cbItem, unsigned cItems);
128PDMBOTHCBDECL(int) vgaMMIORead(PPDMDEVINS pDevIns, void *pvUser, RTGCPHYS GCPhysAddr, void *pv, unsigned cb);
129PDMBOTHCBDECL(int) vgaMMIOWrite(PPDMDEVINS pDevIns, void *pvUser, RTGCPHYS GCPhysAddr, void *pv, unsigned cb);
130#ifdef IN_GC
131PDMBOTHCBDECL(int) vgaGCLFBAccessHandler(PVM pVM, RTGCUINT uErrorCode, PCPUMCTXCORE pRegFrame, RTGCPTR pvFault, RTGCPHYS GCPhysFault, void *pvUser);
132#endif
133#ifdef IN_RING0
134PDMBOTHCBDECL(int) vgaR0LFBAccessHandler(PVM pVM, RTGCUINT uErrorCode, PCPUMCTXCORE pRegFrame, RTGCPTR pvFault, RTGCPHYS GCPhysFault, void *pvUser);
135#endif
136#ifdef IN_RING3
137# ifdef VBE_NEW_DYN_LIST
138PDMBOTHCBDECL(int) vbeIOPortReadVBEExtra(PPDMDEVINS pDevIns, void *pvUser, RTIOPORT Port, uint32_t *pu32, unsigned cb);
139PDMBOTHCBDECL(int) vbeIOPortWriteVBEExtra(PPDMDEVINS pDevIns, void *pvUser, RTIOPORT Port, uint32_t u32, unsigned cb);
140# endif
141#endif /* IN_RING3 */
142
143
144__END_DECLS
145
146
147/**
148 * Set a VRAM page dirty.
149 *
150 * @param pData VGA instance data.
151 * @param offVRAM The VRAM offset of the page to set.
152 */
153DECLINLINE(void) vga_set_dirty(VGAState *pData, RTGCPHYS offVRAM)
154{
155 Assert(offVRAM < pData->vram_size);
156 ASMBitSet(&pData->au32DirtyBitmap[0], offVRAM >> PAGE_SHIFT);
157 pData->fHaveDirtyBits = true;
158}
159
160/**
161 * Tests if a VRAM page is dirty.
162 *
163 * @returns true if dirty.
164 * @returns false if clean.
165 * @param pData VGA instance data.
166 * @param offVRAM The VRAM offset of the page to check.
167 */
168DECLINLINE(bool) vga_is_dirty(VGAState *pData, RTGCPHYS offVRAM)
169{
170 Assert(offVRAM < pData->vram_size);
171 return ASMBitTest(&pData->au32DirtyBitmap[0], offVRAM >> PAGE_SHIFT);
172}
173
174/**
175 * Reset dirty flags in a give range.
176 *
177 * @param pData VGA instance data.
178 * @param offVRAMStart Offset into the VRAM buffer of the first page.
179 * @param offVRAMEnd Offset into the VRAM buffer of the last page - exclusive.
180 */
181DECLINLINE(void) vga_reset_dirty(VGAState *pData, RTGCPHYS offVRAMStart, RTGCPHYS offVRAMEnd)
182{
183 Assert(offVRAMStart < pData->vram_size);
184 Assert(offVRAMEnd < pData->vram_size);
185 Assert(offVRAMStart < offVRAMEnd);
186 ASMBitClearRange(&pData->au32DirtyBitmap[0], offVRAMStart >> PAGE_SHIFT, offVRAMEnd >> PAGE_SHIFT);
187}
188
189#endif /* !VBOX_DEVICE_STRUCT_TESTCASE */
190#endif /* VBOX */
191#ifndef VBOX_DEVICE_STRUCT_TESTCASE
192
193#ifndef VBOX
194#include "vl.h"
195#include "vga_int.h"
196#endif /* !VBOX */
197
198#ifdef LOG_ENABLED
199//#define DEBUG_VGA
200//#define DEBUG_VGA_MEM
201//#define DEBUG_VGA_REG
202
203#define DEBUG_BOCHS_VBE
204
205#endif
206
207/* force some bits to zero */
208#ifdef VBOX
209static
210#endif /* VBOX */
211const uint8_t sr_mask[8] = {
212 (uint8_t)~0xfc,
213 (uint8_t)~0xc2,
214 (uint8_t)~0xf0,
215 (uint8_t)~0xc0,
216 (uint8_t)~0xf1,
217 (uint8_t)~0xff,
218 (uint8_t)~0xff,
219 (uint8_t)~0x00,
220};
221
222#ifdef VBOX
223static
224#endif /* VBOX */
225const uint8_t gr_mask[16] = {
226 (uint8_t)~0xf0, /* 0x00 */
227 (uint8_t)~0xf0, /* 0x01 */
228 (uint8_t)~0xf0, /* 0x02 */
229 (uint8_t)~0xe0, /* 0x03 */
230 (uint8_t)~0xfc, /* 0x04 */
231 (uint8_t)~0x84, /* 0x05 */
232 (uint8_t)~0xf0, /* 0x06 */
233 (uint8_t)~0xf0, /* 0x07 */
234 (uint8_t)~0x00, /* 0x08 */
235 (uint8_t)~0xff, /* 0x09 */
236 (uint8_t)~0xff, /* 0x0a */
237 (uint8_t)~0xff, /* 0x0b */
238 (uint8_t)~0xff, /* 0x0c */
239 (uint8_t)~0xff, /* 0x0d */
240 (uint8_t)~0xff, /* 0x0e */
241 (uint8_t)~0xff, /* 0x0f */
242};
243
244#define cbswap_32(__x) \
245((uint32_t)( \
246 (((uint32_t)(__x) & (uint32_t)0x000000ffUL) << 24) | \
247 (((uint32_t)(__x) & (uint32_t)0x0000ff00UL) << 8) | \
248 (((uint32_t)(__x) & (uint32_t)0x00ff0000UL) >> 8) | \
249 (((uint32_t)(__x) & (uint32_t)0xff000000UL) >> 24) ))
250
251#ifdef WORDS_BIGENDIAN
252#define PAT(x) cbswap_32(x)
253#else
254#define PAT(x) (x)
255#endif
256
257#ifdef WORDS_BIGENDIAN
258#define BIG 1
259#else
260#define BIG 0
261#endif
262
263#ifdef WORDS_BIGENDIAN
264#define GET_PLANE(data, p) (((data) >> (24 - (p) * 8)) & 0xff)
265#else
266#define GET_PLANE(data, p) (((data) >> ((p) * 8)) & 0xff)
267#endif
268
269static const uint32_t mask16[16] = {
270 PAT(0x00000000),
271 PAT(0x000000ff),
272 PAT(0x0000ff00),
273 PAT(0x0000ffff),
274 PAT(0x00ff0000),
275 PAT(0x00ff00ff),
276 PAT(0x00ffff00),
277 PAT(0x00ffffff),
278 PAT(0xff000000),
279 PAT(0xff0000ff),
280 PAT(0xff00ff00),
281 PAT(0xff00ffff),
282 PAT(0xffff0000),
283 PAT(0xffff00ff),
284 PAT(0xffffff00),
285 PAT(0xffffffff),
286};
287
288#undef PAT
289
290#ifdef WORDS_BIGENDIAN
291#define PAT(x) (x)
292#else
293#define PAT(x) cbswap_32(x)
294#endif
295
296static const uint32_t dmask16[16] = {
297 PAT(0x00000000),
298 PAT(0x000000ff),
299 PAT(0x0000ff00),
300 PAT(0x0000ffff),
301 PAT(0x00ff0000),
302 PAT(0x00ff00ff),
303 PAT(0x00ffff00),
304 PAT(0x00ffffff),
305 PAT(0xff000000),
306 PAT(0xff0000ff),
307 PAT(0xff00ff00),
308 PAT(0xff00ffff),
309 PAT(0xffff0000),
310 PAT(0xffff00ff),
311 PAT(0xffffff00),
312 PAT(0xffffffff),
313};
314
315static const uint32_t dmask4[4] = {
316 PAT(0x00000000),
317 PAT(0x0000ffff),
318 PAT(0xffff0000),
319 PAT(0xffffffff),
320};
321
322#if defined(VBOX) && defined(IN_RING3)
323static uint32_t expand4[256];
324static uint16_t expand2[256];
325static uint8_t expand4to8[16];
326#endif /* VBOX && IN_RING3 */
327
328#ifndef VBOX
329VGAState *vga_state;
330int vga_io_memory;
331#endif /* !VBOX */
332
333static uint32_t vga_ioport_read(void *opaque, uint32_t addr)
334{
335 VGAState *s = (VGAState*)opaque;
336 int val, index;
337
338 /* check port range access depending on color/monochrome mode */
339 if ((addr >= 0x3b0 && addr <= 0x3bf && (s->msr & MSR_COLOR_EMULATION)) ||
340 (addr >= 0x3d0 && addr <= 0x3df && !(s->msr & MSR_COLOR_EMULATION))) {
341 val = 0xff;
342 } else {
343 switch(addr) {
344 case 0x3c0:
345 if (s->ar_flip_flop == 0) {
346 val = s->ar_index;
347 } else {
348 val = 0;
349 }
350 break;
351 case 0x3c1:
352 index = s->ar_index & 0x1f;
353 if (index < 21)
354 val = s->ar[index];
355 else
356 val = 0;
357 break;
358 case 0x3c2:
359 val = s->st00;
360 break;
361 case 0x3c4:
362 val = s->sr_index;
363 break;
364 case 0x3c5:
365 val = s->sr[s->sr_index];
366#ifdef DEBUG_VGA_REG
367 Log(("vga: read SR%x = 0x%02x\n", s->sr_index, val));
368#endif
369 break;
370 case 0x3c7:
371 val = s->dac_state;
372 break;
373 case 0x3c8:
374 val = s->dac_write_index;
375 break;
376 case 0x3c9:
377 val = s->palette[s->dac_read_index * 3 + s->dac_sub_index];
378 if (++s->dac_sub_index == 3) {
379 s->dac_sub_index = 0;
380 s->dac_read_index++;
381 }
382 break;
383 case 0x3ca:
384 val = s->fcr;
385 break;
386 case 0x3cc:
387 val = s->msr;
388 break;
389 case 0x3ce:
390 val = s->gr_index;
391 break;
392 case 0x3cf:
393 val = s->gr[s->gr_index];
394#ifdef DEBUG_VGA_REG
395 Log(("vga: read GR%x = 0x%02x\n", s->gr_index, val));
396#endif
397 break;
398 case 0x3b4:
399 case 0x3d4:
400 val = s->cr_index;
401 break;
402 case 0x3b5:
403 case 0x3d5:
404 val = s->cr[s->cr_index];
405#ifdef DEBUG_VGA_REG
406 Log(("vga: read CR%x = 0x%02x\n", s->cr_index, val));
407#endif
408 break;
409 case 0x3ba:
410 case 0x3da:
411 /* just toggle to fool polling */
412 s->st01 ^= ST01_V_RETRACE | ST01_DISP_ENABLE;
413 val = s->st01;
414 s->ar_flip_flop = 0;
415 break;
416 default:
417 val = 0x00;
418 break;
419 }
420 }
421#if defined(DEBUG_VGA)
422 Log(("VGA: read addr=0x%04x data=0x%02x\n", addr, val));
423#endif
424 return val;
425}
426
427static void vga_ioport_write(void *opaque, uint32_t addr, uint32_t val)
428{
429 VGAState *s = (VGAState*)opaque;
430 int index;
431
432 /* check port range access depending on color/monochrome mode */
433 if ((addr >= 0x3b0 && addr <= 0x3bf && (s->msr & MSR_COLOR_EMULATION)) ||
434 (addr >= 0x3d0 && addr <= 0x3df && !(s->msr & MSR_COLOR_EMULATION)))
435 return;
436
437#ifdef DEBUG_VGA
438 Log(("VGA: write addr=0x%04x data=0x%02x\n", addr, val));
439#endif
440
441 switch(addr) {
442 case 0x3c0:
443 if (s->ar_flip_flop == 0) {
444 val &= 0x3f;
445 s->ar_index = val;
446 } else {
447 index = s->ar_index & 0x1f;
448 switch(index) {
449#ifndef VBOX
450 case 0x00 ... 0x0f:
451#else /* VBOX */
452 case 0x00: case 0x01: case 0x02: case 0x03: case 0x04: case 0x05: case 0x06: case 0x07:
453 case 0x08: case 0x09: case 0x0a: case 0x0b: case 0x0c: case 0x0d: case 0x0e: case 0x0f:
454#endif /* VBOX */
455 s->ar[index] = val & 0x3f;
456 break;
457 case 0x10:
458 s->ar[index] = val & ~0x10;
459 break;
460 case 0x11:
461 s->ar[index] = val;
462 break;
463 case 0x12:
464 s->ar[index] = val & ~0xc0;
465 break;
466 case 0x13:
467 s->ar[index] = val & ~0xf0;
468 break;
469 case 0x14:
470 s->ar[index] = val & ~0xf0;
471 break;
472 default:
473 break;
474 }
475 }
476 s->ar_flip_flop ^= 1;
477 break;
478 case 0x3c2:
479 s->msr = val & ~0x10;
480 break;
481 case 0x3c4:
482 s->sr_index = val & 7;
483 break;
484 case 0x3c5:
485#ifdef DEBUG_VGA_REG
486 Log(("vga: write SR%x = 0x%02x\n", s->sr_index, val));
487#endif
488 s->sr[s->sr_index] = val & sr_mask[s->sr_index];
489 break;
490 case 0x3c7:
491 s->dac_read_index = val;
492 s->dac_sub_index = 0;
493 s->dac_state = 3;
494 break;
495 case 0x3c8:
496 s->dac_write_index = val;
497 s->dac_sub_index = 0;
498 s->dac_state = 0;
499 break;
500 case 0x3c9:
501 s->dac_cache[s->dac_sub_index] = val;
502 if (++s->dac_sub_index == 3) {
503 memcpy(&s->palette[s->dac_write_index * 3], s->dac_cache, 3);
504 s->dac_sub_index = 0;
505 s->dac_write_index++;
506 }
507 break;
508 case 0x3ce:
509 s->gr_index = val & 0x0f;
510 break;
511 case 0x3cf:
512#ifdef DEBUG_VGA_REG
513 Log(("vga: write GR%x = 0x%02x\n", s->gr_index, val));
514#endif
515 s->gr[s->gr_index] = val & gr_mask[s->gr_index];
516 break;
517 case 0x3b4:
518 case 0x3d4:
519 s->cr_index = val;
520 break;
521 case 0x3b5:
522 case 0x3d5:
523#ifdef DEBUG_VGA_REG
524 Log(("vga: write CR%x = 0x%02x\n", s->cr_index, val));
525#endif
526 /* handle CR0-7 protection */
527 if ((s->cr[0x11] & 0x80) && s->cr_index <= 7) {
528 /* can always write bit 4 of CR7 */
529 if (s->cr_index == 7)
530 s->cr[7] = (s->cr[7] & ~0x10) | (val & 0x10);
531 return;
532 }
533 switch(s->cr_index) {
534 case 0x01: /* horizontal display end */
535 case 0x07:
536 case 0x09:
537 case 0x0c:
538 case 0x0d:
539 case 0x12: /* veritcal display end */
540 s->cr[s->cr_index] = val;
541 break;
542
543 default:
544 s->cr[s->cr_index] = val;
545 break;
546 }
547 break;
548 case 0x3ba:
549 case 0x3da:
550 s->fcr = val & 0x10;
551 break;
552 }
553}
554
555#ifdef CONFIG_BOCHS_VBE
556static uint32_t vbe_ioport_read_index(void *opaque, uint32_t addr)
557{
558 VGAState *s = (VGAState*)opaque;
559 uint32_t val;
560 val = s->vbe_index;
561 return val;
562}
563
564static uint32_t vbe_ioport_read_data(void *opaque, uint32_t addr)
565{
566 VGAState *s = (VGAState*)opaque;
567 uint32_t val;
568
569 if (s->vbe_index <= VBE_DISPI_INDEX_NB) {
570 if (s->vbe_regs[VBE_DISPI_INDEX_ENABLE] & VBE_DISPI_GETCAPS) {
571 switch(s->vbe_index) {
572 /* XXX: do not hardcode ? */
573 case VBE_DISPI_INDEX_XRES:
574 val = VBE_DISPI_MAX_XRES;
575 break;
576 case VBE_DISPI_INDEX_YRES:
577 val = VBE_DISPI_MAX_YRES;
578 break;
579 case VBE_DISPI_INDEX_BPP:
580 val = VBE_DISPI_MAX_BPP;
581 break;
582 default:
583 val = s->vbe_regs[s->vbe_index];
584 break;
585 }
586 } else {
587 val = s->vbe_regs[s->vbe_index];
588 }
589 } else {
590 val = 0;
591 }
592#ifdef DEBUG_BOCHS_VBE
593 Log(("VBE: read index=0x%x val=0x%x\n", s->vbe_index, val));
594#endif
595 return val;
596}
597
598static void vbe_ioport_write_index(void *opaque, uint32_t addr, uint32_t val)
599{
600 VGAState *s = (VGAState*)opaque;
601 s->vbe_index = val;
602}
603
604static void vbe_ioport_write_data(void *opaque, uint32_t addr, uint32_t val)
605{
606 VGAState *s = (VGAState*)opaque;
607
608 if (s->vbe_index <= VBE_DISPI_INDEX_NB) {
609#ifdef DEBUG_BOCHS_VBE
610 Log(("VBE: write index=0x%x val=0x%x\n", s->vbe_index, val));
611#endif
612 switch(s->vbe_index) {
613 case VBE_DISPI_INDEX_ID:
614 if (val == VBE_DISPI_ID0 ||
615 val == VBE_DISPI_ID1 ||
616 val == VBE_DISPI_ID2) {
617 s->vbe_regs[s->vbe_index] = val;
618 }
619 break;
620 case VBE_DISPI_INDEX_XRES:
621 if ((val <= VBE_DISPI_MAX_XRES) && ((val & 7) == 0)) {
622 s->vbe_regs[s->vbe_index] = val;
623#ifdef KEEP_SCAN_LINE_LENGTH
624 if (s->vbe_regs[VBE_DISPI_INDEX_BPP] == 4)
625 s->vbe_line_offset = val >> 1;
626 else
627 s->vbe_line_offset = val * ((s->vbe_regs[VBE_DISPI_INDEX_BPP] + 7) >> 3);
628 /* XXX: support weird bochs semantics ? */
629 s->vbe_regs[VBE_DISPI_INDEX_VIRT_WIDTH] = s->vbe_line_offset;
630 s->vbe_regs[VBE_DISPI_INDEX_X_OFFSET] = 0;
631 s->vbe_regs[VBE_DISPI_INDEX_Y_OFFSET] = 0;
632 s->vbe_start_addr = 0;
633#endif /* KEEP_SCAN_LINE_LENGTH defined */
634 }
635 break;
636 case VBE_DISPI_INDEX_YRES:
637 if (val <= VBE_DISPI_MAX_YRES) {
638 s->vbe_regs[s->vbe_index] = val;
639#ifdef KEEP_SCAN_LINE_LENGTH
640 s->vbe_regs[VBE_DISPI_INDEX_VIRT_HEIGHT] = val;
641 s->vbe_regs[VBE_DISPI_INDEX_X_OFFSET] = 0;
642 s->vbe_regs[VBE_DISPI_INDEX_Y_OFFSET] = 0;
643 s->vbe_start_addr = 0;
644#endif /* KEEP_SCAN_LINE_LENGTH defined */
645 }
646 break;
647 case VBE_DISPI_INDEX_BPP:
648 if (val == 0)
649 val = 8;
650 if (val == 4 || val == 8 || val == 15 ||
651 val == 16 || val == 24 || val == 32) {
652 s->vbe_regs[s->vbe_index] = val;
653#ifdef KEEP_SCAN_LINE_LENGTH
654 if (val == 4)
655 s->vbe_line_offset = s->vbe_regs[VBE_DISPI_INDEX_XRES] >> 1;
656 else
657 s->vbe_line_offset = s->vbe_regs[VBE_DISPI_INDEX_XRES] * ((val + 7) >> 3);
658 /* XXX: support weird bochs semantics ? */
659 s->vbe_regs[VBE_DISPI_INDEX_VIRT_WIDTH] = s->vbe_line_offset;
660 s->vbe_regs[VBE_DISPI_INDEX_X_OFFSET] = 0;
661 s->vbe_regs[VBE_DISPI_INDEX_Y_OFFSET] = 0;
662 s->vbe_start_addr = 0;
663#endif /* KEEP_SCAN_LINE_LENGTH defined */
664 }
665 break;
666 case VBE_DISPI_INDEX_BANK:
667 if (s->vbe_regs[VBE_DISPI_INDEX_BPP] == 4) {
668 val &= (s->vbe_bank_mask >> 2);
669 } else {
670 val &= s->vbe_bank_mask;
671 }
672 val &= s->vbe_bank_mask;
673 s->vbe_regs[s->vbe_index] = val;
674 s->bank_offset = (val << 16);
675 break;
676 case VBE_DISPI_INDEX_ENABLE:
677 if (val & VBE_DISPI_ENABLED) {
678 int h, shift_control;
679#ifdef VBOX
680 /* Check the values before we screw up with a resolution which is too big or small. */
681 size_t cb = s->vbe_regs[VBE_DISPI_INDEX_XRES];
682 if (s->vbe_regs[VBE_DISPI_INDEX_BPP] == 4)
683 cb = s->vbe_regs[VBE_DISPI_INDEX_XRES] >> 1;
684 else
685 cb = s->vbe_regs[VBE_DISPI_INDEX_XRES] * ((s->vbe_regs[VBE_DISPI_INDEX_BPP] + 7) >> 3);
686 cb *= s->vbe_regs[VBE_DISPI_INDEX_YRES];
687#ifndef KEEP_SCAN_LINE_LENGTH
688 if ( !s->vbe_regs[VBE_DISPI_INDEX_XRES]
689 || !s->vbe_regs[VBE_DISPI_INDEX_YRES]
690 || cb > s->vram_size)
691 {
692 AssertMsgFailed(("XRES=%d YRES=%d cb=%d vram_size=%d\n",
693 s->vbe_regs[VBE_DISPI_INDEX_XRES], s->vbe_regs[VBE_DISPI_INDEX_YRES], cb, s->vram_size));
694 return;
695 }
696#else /* KEEP_SCAN_LINE_LENGTH defined */
697 if ( !s->vbe_regs[VBE_DISPI_INDEX_VIRT_WIDTH]
698 || !s->vbe_regs[VBE_DISPI_INDEX_YRES]
699 || cb > s->vram_size)
700 {
701 AssertMsgFailed(("VIRT WIDTH=%d YRES=%d cb=%d vram_size=%d\n",
702 s->vbe_regs[VBE_DISPI_INDEX_VIRT_WIDTH], s->vbe_regs[VBE_DISPI_INDEX_YRES], cb, s->vram_size));
703 return;
704 }
705#endif /* KEEP_SCAN_LINE_LENGTH defined */
706#endif /* VBOX */
707
708#ifndef KEEP_SCAN_LINE_LENGTH
709 s->vbe_regs[VBE_DISPI_INDEX_VIRT_WIDTH] =
710 s->vbe_regs[VBE_DISPI_INDEX_XRES];
711 s->vbe_regs[VBE_DISPI_INDEX_VIRT_HEIGHT] =
712 s->vbe_regs[VBE_DISPI_INDEX_YRES];
713 s->vbe_regs[VBE_DISPI_INDEX_X_OFFSET] = 0;
714 s->vbe_regs[VBE_DISPI_INDEX_Y_OFFSET] = 0;
715
716 if (s->vbe_regs[VBE_DISPI_INDEX_BPP] == 4)
717 s->vbe_line_offset = s->vbe_regs[VBE_DISPI_INDEX_XRES] >> 1;
718 else
719 s->vbe_line_offset = s->vbe_regs[VBE_DISPI_INDEX_XRES] *
720 ((s->vbe_regs[VBE_DISPI_INDEX_BPP] + 7) >> 3);
721 s->vbe_start_addr = 0;
722#endif /* KEEP_SCAN_LINE_LENGTH not defined */
723
724 /* clear the screen (should be done in BIOS) */
725 if (!(val & VBE_DISPI_NOCLEARMEM)) {
726#ifndef VBOX
727 memset(s->vram_ptr, 0,
728 s->vbe_regs[VBE_DISPI_INDEX_YRES] * s->vbe_line_offset);
729#else /* VBOX */
730 memset(CTXSUFF(s->vram_ptr), 0,
731 s->vbe_regs[VBE_DISPI_INDEX_YRES] * s->vbe_line_offset);
732#endif /* VBOX */
733 }
734
735 /* we initialize the VGA graphic mode (should be done
736 in BIOS) */
737 s->gr[0x06] = (s->gr[0x06] & ~0x0c) | 0x05; /* graphic mode + memory map 1 */
738 s->cr[0x17] |= 3; /* no CGA modes */
739 s->cr[0x13] = s->vbe_line_offset >> 3;
740 /* width */
741 s->cr[0x01] = (s->vbe_regs[VBE_DISPI_INDEX_XRES] >> 3) - 1;
742 /* height (only meaningful if < 1024) */
743 h = s->vbe_regs[VBE_DISPI_INDEX_YRES] - 1;
744 s->cr[0x12] = h;
745 s->cr[0x07] = (s->cr[0x07] & ~0x42) |
746 ((h >> 7) & 0x02) | ((h >> 3) & 0x40);
747 /* line compare to 1023 */
748 s->cr[0x18] = 0xff;
749 s->cr[0x07] |= 0x10;
750 s->cr[0x09] |= 0x40;
751
752 if (s->vbe_regs[VBE_DISPI_INDEX_BPP] == 4) {
753 shift_control = 0;
754 s->sr[0x01] &= ~8; /* no double line */
755 } else {
756 shift_control = 2;
757 s->sr[4] |= 0x08; /* set chain 4 mode */
758 s->sr[2] |= 0x0f; /* activate all planes */
759 }
760 s->gr[0x05] = (s->gr[0x05] & ~0x60) | (shift_control << 5);
761 s->cr[0x09] &= ~0x9f; /* no double scan */
762 } else {
763 /* XXX: the bios should do that */
764#ifdef VBOX
765 /* sunlover 21.12.2006
766 * Here is probably more to reset. When this was executed in GC
767 * then the *update* functions could not detect a mode change.
768 * Or may be these update function should take the s->vbe_regs[s->vbe_index]
769 * into account when detecting a mode change.
770 *
771 * The 'mode reset not detected' problem is now fixed by executing the
772 * VBE_DISPI_INDEX_ENABLE case always in RING3 in order to call the
773 * LFBChange callback.
774 */
775#endif /* VBOX */
776 s->bank_offset = 0;
777 }
778 s->vbe_regs[s->vbe_index] = val;
779#ifdef VBOX
780#ifdef IN_RING3
781 /*
782 * LFB video mode is either disabled or changed. This notification
783 * is used by the display to disable VBVA.
784 */
785 s->pDrv->pfnLFBModeChange(s->pDrv, (val & VBE_DISPI_ENABLED) != 0);
786#endif /* IN_RING3 */
787#endif /* VBOX */
788 break;
789 case VBE_DISPI_INDEX_VIRT_WIDTH:
790 {
791 int w, h, line_offset;
792
793 if (val < s->vbe_regs[VBE_DISPI_INDEX_XRES])
794 return;
795 w = val;
796 if (s->vbe_regs[VBE_DISPI_INDEX_BPP] == 4)
797 line_offset = w >> 1;
798 else
799 line_offset = w * ((s->vbe_regs[VBE_DISPI_INDEX_BPP] + 7) >> 3);
800 h = s->vram_size / line_offset;
801 /* XXX: support weird bochs semantics ? */
802 if (h < s->vbe_regs[VBE_DISPI_INDEX_YRES])
803 return;
804 s->vbe_regs[VBE_DISPI_INDEX_VIRT_WIDTH] = w;
805 s->vbe_regs[VBE_DISPI_INDEX_VIRT_HEIGHT] = h;
806 s->vbe_line_offset = line_offset;
807 }
808 break;
809 case VBE_DISPI_INDEX_X_OFFSET:
810 case VBE_DISPI_INDEX_Y_OFFSET:
811 {
812 int x;
813 s->vbe_regs[s->vbe_index] = val;
814 s->vbe_start_addr = s->vbe_line_offset * s->vbe_regs[VBE_DISPI_INDEX_Y_OFFSET];
815 x = s->vbe_regs[VBE_DISPI_INDEX_X_OFFSET];
816 if (s->vbe_regs[VBE_DISPI_INDEX_BPP] == 4)
817 s->vbe_start_addr += x >> 1;
818 else
819 s->vbe_start_addr += x * ((s->vbe_regs[VBE_DISPI_INDEX_BPP] + 7) >> 3);
820 s->vbe_start_addr >>= 2;
821 }
822 break;
823 default:
824 break;
825 }
826 }
827}
828#endif
829
830/* called for accesses between 0xa0000 and 0xc0000 */
831#ifdef VBOX
832static
833#endif /* VBOX */
834uint32_t vga_mem_readb(void *opaque, target_phys_addr_t addr)
835{
836 VGAState *s = (VGAState*)opaque;
837 int memory_map_mode, plane;
838 uint32_t ret;
839
840 /* convert to VGA memory offset */
841 memory_map_mode = (s->gr[6] >> 2) & 3;
842 addr &= 0x1ffff;
843 switch(memory_map_mode) {
844 case 0:
845 break;
846 case 1:
847 if (addr >= 0x10000)
848 return 0xff;
849 addr += s->bank_offset;
850 break;
851 case 2:
852 addr -= 0x10000;
853 if (addr >= 0x8000)
854 return 0xff;
855 break;
856 default:
857 case 3:
858 addr -= 0x18000;
859 if (addr >= 0x8000)
860 return 0xff;
861 break;
862 }
863
864#ifdef IN_GC
865 if (addr >= VGA_MAPPING_SIZE)
866 return VINF_IOM_HC_MMIO_WRITE;
867#endif
868
869 if (s->sr[4] & 0x08) {
870 /* chain 4 mode : simplest access */
871#ifndef VBOX
872 ret = s->vram_ptr[addr];
873#else /* VBOX */
874 ret = s->CTXSUFF(vram_ptr)[addr];
875#endif /* VBOX */
876 } else if (s->gr[5] & 0x10) {
877 /* odd/even mode (aka text mode mapping) */
878 plane = (s->gr[4] & 2) | (addr & 1);
879#ifndef VBOX
880 ret = s->vram_ptr[((addr & ~1) << 1) | plane];
881#else /* VBOX */
882 ret = s->CTXSUFF(vram_ptr)[((addr & ~1) << 1) | plane];
883#endif /* VBOX */
884 } else {
885 /* standard VGA latched access */
886#ifndef VBOX
887 s->latch = ((uint32_t *)s->vram_ptr)[addr];
888#else /* VBOX && IN_GC */
889 s->latch = ((uint32_t *)s->CTXSUFF(vram_ptr))[addr];
890#endif /* VBOX && IN_GC */
891
892 if (!(s->gr[5] & 0x08)) {
893 /* read mode 0 */
894 plane = s->gr[4];
895 ret = GET_PLANE(s->latch, plane);
896 } else {
897 /* read mode 1 */
898 ret = (s->latch ^ mask16[s->gr[2]]) & mask16[s->gr[7]];
899 ret |= ret >> 16;
900 ret |= ret >> 8;
901 ret = (~ret) & 0xff;
902 }
903 }
904 return ret;
905}
906
907#ifndef VBOX
908static uint32_t vga_mem_readw(void *opaque, target_phys_addr_t addr)
909{
910 uint32_t v;
911#ifdef TARGET_WORDS_BIGENDIAN
912 v = vga_mem_readb(opaque, addr) << 8;
913 v |= vga_mem_readb(opaque, addr + 1);
914#else
915 v = vga_mem_readb(opaque, addr);
916 v |= vga_mem_readb(opaque, addr + 1) << 8;
917#endif
918 return v;
919}
920
921static uint32_t vga_mem_readl(void *opaque, target_phys_addr_t addr)
922{
923 uint32_t v;
924#ifdef TARGET_WORDS_BIGENDIAN
925 v = vga_mem_readb(opaque, addr) << 24;
926 v |= vga_mem_readb(opaque, addr + 1) << 16;
927 v |= vga_mem_readb(opaque, addr + 2) << 8;
928 v |= vga_mem_readb(opaque, addr + 3);
929#else
930 v = vga_mem_readb(opaque, addr);
931 v |= vga_mem_readb(opaque, addr + 1) << 8;
932 v |= vga_mem_readb(opaque, addr + 2) << 16;
933 v |= vga_mem_readb(opaque, addr + 3) << 24;
934#endif
935 return v;
936}
937#endif /* !VBOX */
938
939/* called for accesses between 0xa0000 and 0xc0000 */
940#ifdef VBOX
941static
942#endif /* VBOX */
943int vga_mem_writeb(void *opaque, target_phys_addr_t addr, uint32_t val)
944{
945 VGAState *s = (VGAState*)opaque;
946 int memory_map_mode, plane, write_mode, b, func_select, mask;
947 uint32_t write_mask, bit_mask, set_mask;
948
949#ifdef DEBUG_VGA_MEM
950 Log(("vga: [0x%x] = 0x%02x\n", addr, val));
951#endif
952 /* convert to VGA memory offset */
953 memory_map_mode = (s->gr[6] >> 2) & 3;
954 addr &= 0x1ffff;
955 switch(memory_map_mode) {
956 case 0:
957 break;
958 case 1:
959 if (addr >= 0x10000)
960 return VINF_SUCCESS;
961 addr += s->bank_offset;
962 break;
963 case 2:
964 addr -= 0x10000;
965 if (addr >= 0x8000)
966 return VINF_SUCCESS;
967 break;
968 default:
969 case 3:
970 addr -= 0x18000;
971 if (addr >= 0x8000)
972 return VINF_SUCCESS;
973 break;
974 }
975
976 if (s->sr[4] & 0x08) {
977 /* chain 4 mode : simplest access */
978 plane = addr & 3;
979 mask = (1 << plane);
980 if (s->sr[2] & mask) {
981#ifndef VBOX
982 s->vram_ptr[addr] = val;
983#else /* VBOX */
984#ifdef IN_GC
985 if (addr >= VGA_MAPPING_SIZE)
986 return VINF_IOM_HC_MMIO_WRITE;
987#else
988 if (addr >= s->vram_size)
989 {
990 AssertMsgFailed(("addr=%VGp - this needs to be done in HC! bank_offset=%08x memory_map_mode=%d\n",
991 addr, s->bank_offset, memory_map_mode));
992 return VINF_SUCCESS;
993 }
994#endif
995 s->CTXSUFF(vram_ptr)[addr] = val;
996#endif /* VBOX */
997#ifdef DEBUG_VGA_MEM
998 Log(("vga: chain4: [0x%x]\n", addr));
999#endif
1000 s->plane_updated |= mask; /* only used to detect font change */
1001#ifndef VBOX
1002 cpu_physical_memory_set_dirty(s->vram_offset + addr);
1003#else /* VBOX */
1004 vga_set_dirty(s, addr);
1005#endif /* VBOX */
1006 }
1007 } else if (s->gr[5] & 0x10) {
1008 /* odd/even mode (aka text mode mapping) */
1009 plane = (s->gr[4] & 2) | (addr & 1);
1010 mask = (1 << plane);
1011 if (s->sr[2] & mask) {
1012 addr = ((addr & ~1) << 1) | plane;
1013#ifndef VBOX
1014 s->vram_ptr[addr] = val;
1015#else /* VBOX */
1016#ifdef IN_GC
1017 if (addr >= VGA_MAPPING_SIZE)
1018 return VINF_IOM_HC_MMIO_WRITE;
1019#else
1020 if (addr >= s->vram_size)
1021 {
1022 AssertMsgFailed(("addr=%VGp - this needs to be done in HC! bank_offset=%08x memory_map_mode=%d\n",
1023 addr, s->bank_offset, memory_map_mode));
1024 return VINF_SUCCESS;
1025 }
1026#endif
1027 s->CTXSUFF(vram_ptr)[addr] = val;
1028#endif /* VBOX */
1029#ifdef DEBUG_VGA_MEM
1030 Log(("vga: odd/even: [0x%x]\n", addr));
1031#endif
1032 s->plane_updated |= mask; /* only used to detect font change */
1033#ifndef VBOX
1034 cpu_physical_memory_set_dirty(s->vram_offset + addr);
1035#else /* VBOX */
1036 vga_set_dirty(s, addr);
1037#endif /* VBOX */
1038 }
1039 } else {
1040#ifdef IN_GC
1041 if (addr * 4 >= VGA_MAPPING_SIZE)
1042 return VINF_IOM_HC_MMIO_WRITE;
1043#else
1044 if (addr * 4 >= s->vram_size)
1045 {
1046 AssertMsgFailed(("addr=%VGp - this needs to be done in HC! bank_offset=%08x memory_map_mode=%d\n",
1047 addr * 4, s->bank_offset, memory_map_mode));
1048 return VINF_SUCCESS;
1049 }
1050#endif
1051
1052 /* standard VGA latched access */
1053 write_mode = s->gr[5] & 3;
1054 switch(write_mode) {
1055 default:
1056 case 0:
1057 /* rotate */
1058 b = s->gr[3] & 7;
1059 val = ((val >> b) | (val << (8 - b))) & 0xff;
1060 val |= val << 8;
1061 val |= val << 16;
1062
1063 /* apply set/reset mask */
1064 set_mask = mask16[s->gr[1]];
1065 val = (val & ~set_mask) | (mask16[s->gr[0]] & set_mask);
1066 bit_mask = s->gr[8];
1067 break;
1068 case 1:
1069 val = s->latch;
1070 goto do_write;
1071 case 2:
1072 val = mask16[val & 0x0f];
1073 bit_mask = s->gr[8];
1074 break;
1075 case 3:
1076 /* rotate */
1077 b = s->gr[3] & 7;
1078 val = (val >> b) | (val << (8 - b));
1079
1080 bit_mask = s->gr[8] & val;
1081 val = mask16[s->gr[0]];
1082 break;
1083 }
1084
1085 /* apply logical operation */
1086 func_select = s->gr[3] >> 3;
1087 switch(func_select) {
1088 case 0:
1089 default:
1090 /* nothing to do */
1091 break;
1092 case 1:
1093 /* and */
1094 val &= s->latch;
1095 break;
1096 case 2:
1097 /* or */
1098 val |= s->latch;
1099 break;
1100 case 3:
1101 /* xor */
1102 val ^= s->latch;
1103 break;
1104 }
1105
1106 /* apply bit mask */
1107 bit_mask |= bit_mask << 8;
1108 bit_mask |= bit_mask << 16;
1109 val = (val & bit_mask) | (s->latch & ~bit_mask);
1110
1111 do_write:
1112 /* mask data according to sr[2] */
1113 mask = s->sr[2];
1114 s->plane_updated |= mask; /* only used to detect font change */
1115 write_mask = mask16[mask];
1116#ifndef VBOX
1117 ((uint32_t *)s->vram_ptr)[addr] =
1118 (((uint32_t *)s->vram_ptr)[addr] & ~write_mask) |
1119 (val & write_mask);
1120#else /* VBOX */
1121 ((uint32_t *)s->CTXSUFF(vram_ptr))[addr] =
1122 (((uint32_t *)s->CTXSUFF(vram_ptr))[addr] & ~write_mask) |
1123 (val & write_mask);
1124#endif /* VBOX */
1125#ifdef DEBUG_VGA_MEM
1126 Log(("vga: latch: [0x%x] mask=0x%08x val=0x%08x\n",
1127 addr * 4, write_mask, val));
1128#endif
1129#ifndef VBOX
1130 cpu_physical_memory_set_dirty(s->vram_offset + (addr << 2));
1131#else /* VBOX */
1132 vga_set_dirty(s, (addr << 2));
1133#endif /* VBOX */
1134 }
1135
1136 return VINF_SUCCESS;
1137}
1138
1139#ifndef VBOX
1140static void vga_mem_writew(void *opaque, target_phys_addr_t addr, uint32_t val)
1141{
1142#ifdef TARGET_WORDS_BIGENDIAN
1143 vga_mem_writeb(opaque, addr, (val >> 8) & 0xff);
1144 vga_mem_writeb(opaque, addr + 1, val & 0xff);
1145#else
1146 vga_mem_writeb(opaque, addr, val & 0xff);
1147 vga_mem_writeb(opaque, addr + 1, (val >> 8) & 0xff);
1148#endif
1149}
1150
1151static void vga_mem_writel(void *opaque, target_phys_addr_t addr, uint32_t val)
1152{
1153#ifdef TARGET_WORDS_BIGENDIAN
1154 vga_mem_writeb(opaque, addr, (val >> 24) & 0xff);
1155 vga_mem_writeb(opaque, addr + 1, (val >> 16) & 0xff);
1156 vga_mem_writeb(opaque, addr + 2, (val >> 8) & 0xff);
1157 vga_mem_writeb(opaque, addr + 3, val & 0xff);
1158#else
1159 vga_mem_writeb(opaque, addr, val & 0xff);
1160 vga_mem_writeb(opaque, addr + 1, (val >> 8) & 0xff);
1161 vga_mem_writeb(opaque, addr + 2, (val >> 16) & 0xff);
1162 vga_mem_writeb(opaque, addr + 3, (val >> 24) & 0xff);
1163#endif
1164}
1165#endif /* !VBOX */
1166
1167#if !defined(VBOX) || defined(IN_RING3)
1168typedef void vga_draw_glyph8_func(uint8_t *d, int linesize,
1169 const uint8_t *font_ptr, int h,
1170 uint32_t fgcol, uint32_t bgcol);
1171typedef void vga_draw_glyph9_func(uint8_t *d, int linesize,
1172 const uint8_t *font_ptr, int h,
1173 uint32_t fgcol, uint32_t bgcol, int dup9);
1174typedef void vga_draw_line_func(VGAState *s1, uint8_t *d,
1175 const uint8_t *s, int width);
1176
1177static inline unsigned int rgb_to_pixel8(unsigned int r, unsigned int g, unsigned b)
1178{
1179 return ((r >> 5) << 5) | ((g >> 5) << 2) | (b >> 6);
1180}
1181
1182static inline unsigned int rgb_to_pixel15(unsigned int r, unsigned int g, unsigned b)
1183{
1184 return ((r >> 3) << 10) | ((g >> 3) << 5) | (b >> 3);
1185}
1186
1187static inline unsigned int rgb_to_pixel16(unsigned int r, unsigned int g, unsigned b)
1188{
1189 return ((r >> 3) << 11) | ((g >> 2) << 5) | (b >> 3);
1190}
1191
1192static inline unsigned int rgb_to_pixel32(unsigned int r, unsigned int g, unsigned b)
1193{
1194 return (r << 16) | (g << 8) | b;
1195}
1196
1197#define DEPTH 8
1198#include "DevVGATmpl.h"
1199
1200#define DEPTH 15
1201#include "DevVGATmpl.h"
1202
1203#define DEPTH 16
1204#include "DevVGATmpl.h"
1205
1206#define DEPTH 32
1207#include "DevVGATmpl.h"
1208
1209static unsigned int rgb_to_pixel8_dup(unsigned int r, unsigned int g, unsigned b)
1210{
1211 unsigned int col;
1212 col = rgb_to_pixel8(r, g, b);
1213 col |= col << 8;
1214 col |= col << 16;
1215 return col;
1216}
1217
1218static unsigned int rgb_to_pixel15_dup(unsigned int r, unsigned int g, unsigned b)
1219{
1220 unsigned int col;
1221 col = rgb_to_pixel15(r, g, b);
1222 col |= col << 16;
1223 return col;
1224}
1225
1226static unsigned int rgb_to_pixel16_dup(unsigned int r, unsigned int g, unsigned b)
1227{
1228 unsigned int col;
1229 col = rgb_to_pixel16(r, g, b);
1230 col |= col << 16;
1231 return col;
1232}
1233
1234static unsigned int rgb_to_pixel32_dup(unsigned int r, unsigned int g, unsigned b)
1235{
1236 unsigned int col;
1237 col = rgb_to_pixel32(r, g, b);
1238 return col;
1239}
1240
1241/* return true if the palette was modified */
1242static int update_palette16(VGAState *s)
1243{
1244 int full_update, i;
1245 uint32_t v, col, *palette;
1246
1247 full_update = 0;
1248 palette = s->last_palette;
1249 for(i = 0; i < 16; i++) {
1250 v = s->ar[i];
1251 if (s->ar[0x10] & 0x80)
1252 v = ((s->ar[0x14] & 0xf) << 4) | (v & 0xf);
1253 else
1254 v = ((s->ar[0x14] & 0xc) << 4) | (v & 0x3f);
1255 v = v * 3;
1256 col = s->rgb_to_pixel(c6_to_8(s->palette[v]),
1257 c6_to_8(s->palette[v + 1]),
1258 c6_to_8(s->palette[v + 2]));
1259 if (col != palette[i]) {
1260 full_update = 1;
1261 palette[i] = col;
1262 }
1263 }
1264 return full_update;
1265}
1266
1267/* return true if the palette was modified */
1268static int update_palette256(VGAState *s)
1269{
1270 int full_update, i;
1271 uint32_t v, col, *palette;
1272
1273 full_update = 0;
1274 palette = s->last_palette;
1275 v = 0;
1276 for(i = 0; i < 256; i++) {
1277 col = s->rgb_to_pixel(c6_to_8(s->palette[v]),
1278 c6_to_8(s->palette[v + 1]),
1279 c6_to_8(s->palette[v + 2]));
1280 if (col != palette[i]) {
1281 full_update = 1;
1282 palette[i] = col;
1283 }
1284 v += 3;
1285 }
1286 return full_update;
1287}
1288
1289static void vga_get_offsets(VGAState *s,
1290 uint32_t *pline_offset,
1291 uint32_t *pstart_addr,
1292 uint32_t *pline_compare)
1293{
1294 uint32_t start_addr, line_offset, line_compare;
1295#ifdef CONFIG_BOCHS_VBE
1296 if (s->vbe_regs[VBE_DISPI_INDEX_ENABLE] & VBE_DISPI_ENABLED) {
1297 line_offset = s->vbe_line_offset;
1298 start_addr = s->vbe_start_addr;
1299 line_compare = 65535;
1300 } else
1301#endif
1302 {
1303 /* compute line_offset in bytes */
1304 line_offset = s->cr[0x13];
1305 line_offset <<= 3;
1306
1307 /* starting address */
1308 start_addr = s->cr[0x0d] | (s->cr[0x0c] << 8);
1309
1310 /* line compare */
1311 line_compare = s->cr[0x18] |
1312 ((s->cr[0x07] & 0x10) << 4) |
1313 ((s->cr[0x09] & 0x40) << 3);
1314 }
1315 *pline_offset = line_offset;
1316 *pstart_addr = start_addr;
1317 *pline_compare = line_compare;
1318}
1319
1320/* update start_addr and line_offset. Return TRUE if modified */
1321static int update_basic_params(VGAState *s)
1322{
1323 int full_update;
1324 uint32_t start_addr, line_offset, line_compare;
1325
1326 full_update = 0;
1327
1328 s->get_offsets(s, &line_offset, &start_addr, &line_compare);
1329
1330 if (line_offset != s->line_offset ||
1331 start_addr != s->start_addr ||
1332 line_compare != s->line_compare) {
1333 s->line_offset = line_offset;
1334 s->start_addr = start_addr;
1335 s->line_compare = line_compare;
1336 full_update = 1;
1337 }
1338 return full_update;
1339}
1340
1341static inline int get_depth_index(int depth)
1342{
1343 switch(depth) {
1344 default:
1345 case 8:
1346 return 0;
1347 case 15:
1348 return 1;
1349 case 16:
1350 return 2;
1351 case 32:
1352 return 3;
1353 }
1354}
1355
1356static vga_draw_glyph8_func *vga_draw_glyph8_table[4] = {
1357 vga_draw_glyph8_8,
1358 vga_draw_glyph8_16,
1359 vga_draw_glyph8_16,
1360 vga_draw_glyph8_32,
1361};
1362
1363static vga_draw_glyph8_func *vga_draw_glyph16_table[4] = {
1364 vga_draw_glyph16_8,
1365 vga_draw_glyph16_16,
1366 vga_draw_glyph16_16,
1367 vga_draw_glyph16_32,
1368};
1369
1370static vga_draw_glyph9_func *vga_draw_glyph9_table[4] = {
1371 vga_draw_glyph9_8,
1372 vga_draw_glyph9_16,
1373 vga_draw_glyph9_16,
1374 vga_draw_glyph9_32,
1375};
1376
1377static const uint8_t cursor_glyph[32 * 4] = {
1378 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
1379 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
1380 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
1381 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
1382 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
1383 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
1384 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
1385 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
1386 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
1387 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
1388 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
1389 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
1390 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
1391 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
1392 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
1393 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
1394};
1395
1396/*
1397 * Text mode update
1398 * Missing:
1399 * - double scan
1400 * - double width
1401 * - underline
1402 * - flashing
1403 */
1404static void vga_draw_text(VGAState *s, int full_update)
1405{
1406 int cx, cy, cheight, cw, ch, cattr, height, width, ch_attr;
1407 int cx_min, cx_max, linesize, x_incr;
1408 uint32_t offset, fgcol, bgcol, v, cursor_offset;
1409 uint8_t *d1, *d, *src, *s1, *dest, *cursor_ptr;
1410 const uint8_t *font_ptr, *font_base[2];
1411 int dup9, line_offset, depth_index;
1412 uint32_t *palette;
1413 uint32_t *ch_attr_ptr;
1414 vga_draw_glyph8_func *vga_draw_glyph8;
1415 vga_draw_glyph9_func *vga_draw_glyph9;
1416
1417 full_update |= update_palette16(s);
1418 palette = s->last_palette;
1419
1420 /* compute font data address (in plane 2) */
1421 v = s->sr[3];
1422 offset = (((v >> 4) & 1) | ((v << 1) & 6)) * 8192 * 4 + 2;
1423 if (offset != s->font_offsets[0]) {
1424 s->font_offsets[0] = offset;
1425 full_update = 1;
1426 }
1427#ifndef VBOX
1428 font_base[0] = s->vram_ptr + offset;
1429#else /* VBOX */
1430 font_base[0] = s->CTXSUFF(vram_ptr) + offset;
1431#endif /* VBOX */
1432
1433 offset = (((v >> 5) & 1) | ((v >> 1) & 6)) * 8192 * 4 + 2;
1434#ifndef VBOX
1435 font_base[1] = s->vram_ptr + offset;
1436#else /* VBOX */
1437 font_base[1] = s->CTXSUFF(vram_ptr) + offset;
1438#endif /* VBOX */
1439 if (offset != s->font_offsets[1]) {
1440 s->font_offsets[1] = offset;
1441 full_update = 1;
1442 }
1443 if (s->plane_updated & (1 << 2)) {
1444 /* if the plane 2 was modified since the last display, it
1445 indicates the font may have been modified */
1446 s->plane_updated = 0;
1447 full_update = 1;
1448 }
1449 full_update |= update_basic_params(s);
1450
1451 line_offset = s->line_offset;
1452#ifndef VBOX
1453 s1 = s->vram_ptr + (s->start_addr * 4);
1454#else /* VBOX */
1455 s1 = s->CTXSUFF(vram_ptr) + (s->start_addr * 4);
1456#endif /* VBOX */
1457
1458 /* total width & height */
1459 cheight = (s->cr[9] & 0x1f) + 1;
1460 cw = 8;
1461 if (!(s->sr[1] & 0x01))
1462 cw = 9;
1463 if (s->sr[1] & 0x08)
1464 cw = 16; /* NOTE: no 18 pixel wide */
1465#ifndef VBOX
1466 x_incr = cw * ((s->ds->depth + 7) >> 3);
1467#else /* VBOX */
1468 x_incr = cw * ((s->pDrv->cBits + 7) >> 3);
1469#endif /* VBOX */
1470 width = (s->cr[0x01] + 1);
1471 if (s->cr[0x06] == 100) {
1472 /* ugly hack for CGA 160x100x16 - explain me the logic */
1473 height = 100;
1474 } else {
1475 height = s->cr[0x12] |
1476 ((s->cr[0x07] & 0x02) << 7) |
1477 ((s->cr[0x07] & 0x40) << 3);
1478 height = (height + 1) / cheight;
1479 }
1480 if ((height * width) > CH_ATTR_SIZE) {
1481 /* better than nothing: exit if transient size is too big */
1482 return;
1483 }
1484
1485 if (width != (int)s->last_width || height != (int)s->last_height ||
1486 cw != s->last_cw || cheight != s->last_ch) {
1487 s->last_scr_width = width * cw;
1488 s->last_scr_height = height * cheight;
1489#ifndef VBOX
1490 dpy_resize(s->ds, s->last_scr_width, s->last_scr_height);
1491#else /* VBOX */
1492 /* For text modes the external memory buffer can not be used, so bpp and cbLine are 0 here. */
1493 s->pDrv->pfnResize(s->pDrv, 0, NULL, 0, s->last_scr_width, s->last_scr_height);
1494#endif /* VBOX */
1495 s->last_width = width;
1496 s->last_height = height;
1497 s->last_ch = cheight;
1498 s->last_cw = cw;
1499 full_update = 1;
1500 }
1501 cursor_offset = ((s->cr[0x0e] << 8) | s->cr[0x0f]) - s->start_addr;
1502 if (cursor_offset != s->cursor_offset ||
1503 s->cr[0xa] != s->cursor_start ||
1504 s->cr[0xb] != s->cursor_end) {
1505 /* if the cursor position changed, we update the old and new
1506 chars */
1507 if (s->cursor_offset < CH_ATTR_SIZE)
1508 s->last_ch_attr[s->cursor_offset] = ~0;
1509 if (cursor_offset < CH_ATTR_SIZE)
1510 s->last_ch_attr[cursor_offset] = ~0;
1511 s->cursor_offset = cursor_offset;
1512 s->cursor_start = s->cr[0xa];
1513 s->cursor_end = s->cr[0xb];
1514 }
1515#ifndef VBOX
1516 cursor_ptr = s->vram_ptr + (s->start_addr + cursor_offset) * 4;
1517
1518 depth_index = get_depth_index(s->ds->depth);
1519#else /* VBOX */
1520 cursor_ptr = s->CTXSUFF(vram_ptr) + (s->start_addr + cursor_offset) * 4;
1521 depth_index = get_depth_index(s->pDrv->cBits);
1522#endif /* VBOX */
1523 if (cw == 16)
1524 vga_draw_glyph8 = vga_draw_glyph16_table[depth_index];
1525 else
1526 vga_draw_glyph8 = vga_draw_glyph8_table[depth_index];
1527 vga_draw_glyph9 = vga_draw_glyph9_table[depth_index];
1528
1529#ifndef VBOX
1530 dest = s->ds->data;
1531 linesize = s->ds->linesize;
1532#else /* VBOX */
1533 dest = s->pDrv->pu8Data;
1534 linesize = s->pDrv->cbScanline;
1535#endif /* VBOX */
1536 ch_attr_ptr = s->last_ch_attr;
1537
1538 for(cy = 0; cy < height; cy++) {
1539 d1 = dest;
1540 src = s1;
1541 cx_min = width;
1542 cx_max = -1;
1543 for(cx = 0; cx < width; cx++) {
1544 ch_attr = *(uint16_t *)src;
1545 if (full_update || ch_attr != (int)*ch_attr_ptr) {
1546 if (cx < cx_min)
1547 cx_min = cx;
1548 if (cx > cx_max)
1549 cx_max = cx;
1550 *ch_attr_ptr = ch_attr;
1551#ifdef WORDS_BIGENDIAN
1552 ch = ch_attr >> 8;
1553 cattr = ch_attr & 0xff;
1554#else
1555 ch = ch_attr & 0xff;
1556 cattr = ch_attr >> 8;
1557#endif
1558 font_ptr = font_base[(cattr >> 3) & 1];
1559 font_ptr += 32 * 4 * ch;
1560 bgcol = palette[cattr >> 4];
1561 fgcol = palette[cattr & 0x0f];
1562 if (cw != 9) {
1563 vga_draw_glyph8(d1, linesize,
1564 font_ptr, cheight, fgcol, bgcol);
1565 } else {
1566 dup9 = 0;
1567 if (ch >= 0xb0 && ch <= 0xdf && (s->ar[0x10] & 0x04))
1568 dup9 = 1;
1569 vga_draw_glyph9(d1, linesize,
1570 font_ptr, cheight, fgcol, bgcol, dup9);
1571 }
1572 if (src == cursor_ptr &&
1573 !(s->cr[0x0a] & 0x20)) {
1574 int line_start, line_last, h;
1575 /* draw the cursor */
1576 line_start = s->cr[0x0a] & 0x1f;
1577 line_last = s->cr[0x0b] & 0x1f;
1578 /* XXX: check that */
1579 if (line_last > cheight - 1)
1580 line_last = cheight - 1;
1581 if (line_last >= line_start && line_start < cheight) {
1582 h = line_last - line_start + 1;
1583 d = d1 + linesize * line_start;
1584 if (cw != 9) {
1585 vga_draw_glyph8(d, linesize,
1586 cursor_glyph, h, fgcol, bgcol);
1587 } else {
1588 vga_draw_glyph9(d, linesize,
1589 cursor_glyph, h, fgcol, bgcol, 1);
1590 }
1591 }
1592 }
1593 }
1594 d1 += x_incr;
1595 src += 4;
1596 ch_attr_ptr++;
1597 }
1598#ifndef VBOX
1599 if (cx_max != -1) {
1600 dpy_update(s->ds, cx_min * cw, cy * cheight,
1601 (cx_max - cx_min + 1) * cw, cheight);
1602 }
1603#else
1604 if (cx_max != -1)
1605 s->pDrv->pfnUpdateRect(s->pDrv, cx_min * cw, cy * cheight, (cx_max - cx_min + 1) * cw, cheight);
1606#endif
1607 dest += linesize * cheight;
1608 s1 += line_offset;
1609 }
1610}
1611
1612enum {
1613 VGA_DRAW_LINE2,
1614 VGA_DRAW_LINE2D2,
1615 VGA_DRAW_LINE4,
1616 VGA_DRAW_LINE4D2,
1617 VGA_DRAW_LINE8D2,
1618 VGA_DRAW_LINE8,
1619 VGA_DRAW_LINE15,
1620 VGA_DRAW_LINE16,
1621 VGA_DRAW_LINE24,
1622 VGA_DRAW_LINE32,
1623 VGA_DRAW_LINE_NB
1624};
1625
1626static vga_draw_line_func *vga_draw_line_table[4 * VGA_DRAW_LINE_NB] = {
1627 vga_draw_line2_8,
1628 vga_draw_line2_16,
1629 vga_draw_line2_16,
1630 vga_draw_line2_32,
1631
1632 vga_draw_line2d2_8,
1633 vga_draw_line2d2_16,
1634 vga_draw_line2d2_16,
1635 vga_draw_line2d2_32,
1636
1637 vga_draw_line4_8,
1638 vga_draw_line4_16,
1639 vga_draw_line4_16,
1640 vga_draw_line4_32,
1641
1642 vga_draw_line4d2_8,
1643 vga_draw_line4d2_16,
1644 vga_draw_line4d2_16,
1645 vga_draw_line4d2_32,
1646
1647 vga_draw_line8d2_8,
1648 vga_draw_line8d2_16,
1649 vga_draw_line8d2_16,
1650 vga_draw_line8d2_32,
1651
1652 vga_draw_line8_8,
1653 vga_draw_line8_16,
1654 vga_draw_line8_16,
1655 vga_draw_line8_32,
1656
1657 vga_draw_line15_8,
1658 vga_draw_line15_15,
1659 vga_draw_line15_16,
1660 vga_draw_line15_32,
1661
1662 vga_draw_line16_8,
1663 vga_draw_line16_15,
1664 vga_draw_line16_16,
1665 vga_draw_line16_32,
1666
1667 vga_draw_line24_8,
1668 vga_draw_line24_15,
1669 vga_draw_line24_16,
1670 vga_draw_line24_32,
1671
1672 vga_draw_line32_8,
1673 vga_draw_line32_15,
1674 vga_draw_line32_16,
1675 vga_draw_line32_32,
1676};
1677
1678static int vga_get_bpp(VGAState *s)
1679{
1680 int ret;
1681#ifdef CONFIG_BOCHS_VBE
1682 if (s->vbe_regs[VBE_DISPI_INDEX_ENABLE] & VBE_DISPI_ENABLED) {
1683 ret = s->vbe_regs[VBE_DISPI_INDEX_BPP];
1684 } else
1685#endif
1686 {
1687 ret = 0;
1688 }
1689 return ret;
1690}
1691
1692static void vga_get_resolution(VGAState *s, int *pwidth, int *pheight)
1693{
1694 int width, height;
1695#ifdef CONFIG_BOCHS_VBE
1696 if (s->vbe_regs[VBE_DISPI_INDEX_ENABLE] & VBE_DISPI_ENABLED) {
1697 width = s->vbe_regs[VBE_DISPI_INDEX_XRES];
1698 height = s->vbe_regs[VBE_DISPI_INDEX_YRES];
1699 } else
1700#endif
1701 {
1702 width = (s->cr[0x01] + 1) * 8;
1703 height = s->cr[0x12] |
1704 ((s->cr[0x07] & 0x02) << 7) |
1705 ((s->cr[0x07] & 0x40) << 3);
1706 height = (height + 1);
1707 }
1708 *pwidth = width;
1709 *pheight = height;
1710}
1711
1712#ifndef VBOX
1713void vga_invalidate_scanlines(VGAState *s, int y1, int y2)
1714{
1715 int y;
1716 if (y1 >= VGA_MAX_HEIGHT)
1717 return;
1718 if (y2 >= VGA_MAX_HEIGHT)
1719 y2 = VGA_MAX_HEIGHT;
1720 for(y = y1; y < y2; y++) {
1721 s->invalidated_y_table[y >> 5] |= 1 << (y & 0x1f);
1722 }
1723}
1724#endif /* !VBOX*/
1725
1726#ifdef VBOX
1727/**
1728 * Performs the display driver resizing when in graphics mode.
1729 *
1730 * This will recalc / update any status data depending on the driver
1731 * properties (bit depth mostly).
1732 *
1733 * @returns VINF_SUCCESS on success.
1734 * @returns VINF_VGA_RESIZE_IN_PROGRESS if the operation wasn't complete.
1735 * @param s Pointer to the vga status.
1736 * @param cx The width.
1737 * @param cy The height.
1738 */
1739static int vga_resize_graphic(VGAState *s, int cx, int cy, int v)
1740{
1741 const unsigned cBits = s->get_bpp(s);
1742#if 0 /** @todo kill the nasty resize deadlocks! */
1743 int rc = s->pDrv->pfnResize(s->pDrv, cBits, s->CTXSUFF(vram_ptr), s->line_offset, cx, cy);
1744 if (rc == VINF_VGA_RESIZE_IN_PROGRESS)
1745 return rc;
1746 AssertRC(rc);
1747#else
1748 /** @todo r=sunlover: If the guest changes VBE_DISPI_INDEX_X_OFFSET, VBE_DISPI_INDEX_Y_OFFSET
1749 * registers, then the third parameter of the following call should be
1750 * probably 's->CTXSUFF(vram_ptr) + s->vbe_start_addr'.
1751 */
1752 s->pDrv->pfnResize(s->pDrv, cBits, s->CTXSUFF(vram_ptr), s->line_offset, cx, cy);
1753#endif
1754
1755 /* last stuff */
1756 s->last_bpp = cBits;
1757 s->last_scr_width = cx;
1758 s->last_scr_height = cy;
1759 s->last_width = cx;
1760 s->last_height = cy;
1761
1762 /* update palette */
1763 switch (s->pDrv->cBits)
1764 {
1765 case 32: s->rgb_to_pixel = rgb_to_pixel32_dup; break;
1766 case 16:
1767 default: s->rgb_to_pixel = rgb_to_pixel16_dup; break;
1768 case 15: s->rgb_to_pixel = rgb_to_pixel15_dup; break;
1769 case 8: s->rgb_to_pixel = rgb_to_pixel8_dup; break;
1770 }
1771 if (s->shift_control == 0)
1772 update_palette16(s);
1773 else if (s->shift_control == 1)
1774 update_palette16(s);
1775 return VINF_SUCCESS;
1776}
1777#endif /* VBOX */
1778
1779/*
1780 * graphic modes
1781 */
1782static void vga_draw_graphic(VGAState *s, int full_update)
1783{
1784 int y1, y, update, page_min, page_max, linesize, y_start, double_scan, mask;
1785 int width, height, shift_control, line_offset, page0, page1, bwidth;
1786 int disp_width, multi_scan, multi_run;
1787 uint8_t *d;
1788 uint32_t v, addr1, addr;
1789 vga_draw_line_func *vga_draw_line;
1790 bool offsets_changed;
1791
1792 offsets_changed = update_basic_params(s);
1793
1794 full_update |= offsets_changed;
1795
1796 s->get_resolution(s, &width, &height);
1797 disp_width = width;
1798
1799 shift_control = (s->gr[0x05] >> 5) & 3;
1800 double_scan = (s->cr[0x09] >> 7);
1801 if (shift_control != 1) {
1802 multi_scan = (((s->cr[0x09] & 0x1f) + 1) << double_scan) - 1;
1803 } else {
1804 /* in CGA modes, multi_scan is ignored */
1805 /* XXX: is it correct ? */
1806 multi_scan = double_scan;
1807 }
1808 multi_run = multi_scan;
1809 if (shift_control != s->shift_control ||
1810 double_scan != s->double_scan) {
1811 full_update = 1;
1812 s->shift_control = shift_control;
1813 s->double_scan = double_scan;
1814 }
1815
1816 if (shift_control == 0) {
1817 full_update |= update_palette16(s);
1818 if (s->sr[0x01] & 8) {
1819 v = VGA_DRAW_LINE4D2;
1820 disp_width <<= 1;
1821 } else {
1822 v = VGA_DRAW_LINE4;
1823 }
1824 } else if (shift_control == 1) {
1825 full_update |= update_palette16(s);
1826 if (s->sr[0x01] & 8) {
1827 v = VGA_DRAW_LINE2D2;
1828 disp_width <<= 1;
1829 } else {
1830 v = VGA_DRAW_LINE2;
1831 }
1832 } else {
1833 switch(s->get_bpp(s)) {
1834 default:
1835 case 0:
1836 full_update |= update_palette256(s);
1837 v = VGA_DRAW_LINE8D2;
1838 break;
1839 case 8:
1840 full_update |= update_palette256(s);
1841 v = VGA_DRAW_LINE8;
1842 break;
1843 case 15:
1844 v = VGA_DRAW_LINE15;
1845 break;
1846 case 16:
1847 v = VGA_DRAW_LINE16;
1848 break;
1849 case 24:
1850 v = VGA_DRAW_LINE24;
1851 break;
1852 case 32:
1853 v = VGA_DRAW_LINE32;
1854 break;
1855 }
1856 }
1857#ifndef VBOX
1858 vga_draw_line = vga_draw_line_table[v * 4 + get_depth_index(s->ds->depth)];
1859
1860 if (disp_width != s->last_width ||
1861 height != s->last_height) {
1862 dpy_resize(s->ds, disp_width, height);
1863 s->last_scr_width = disp_width;
1864 s->last_scr_height = height;
1865 s->last_width = disp_width;
1866 s->last_height = height;
1867 full_update = 1;
1868 }
1869#else /* VBOX */
1870 if ( disp_width != (int)s->last_width
1871 || height != (int)s->last_height
1872 || s->get_bpp(s) != (int)s->last_bpp
1873 || offsets_changed) {
1874 if (vga_resize_graphic(s, disp_width, height, v) != VINF_SUCCESS)
1875 return;
1876 full_update = 1;
1877 }
1878 vga_draw_line = vga_draw_line_table[v * 4 + get_depth_index(s->pDrv->cBits)];
1879
1880#endif /* VBOX */
1881 if (s->cursor_invalidate)
1882 s->cursor_invalidate(s);
1883
1884 line_offset = s->line_offset;
1885#if 0
1886 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",
1887 width, height, v, line_offset, s->cr[9], s->cr[0x17], s->line_compare, s->sr[0x01]));
1888#endif
1889 addr1 = (s->start_addr * 4);
1890 bwidth = width * 4;
1891 y_start = -1;
1892 page_min = 0x7fffffff;
1893 page_max = -1;
1894#ifndef VBOX
1895 d = s->ds->data;
1896 linesize = s->ds->linesize;
1897#else /* VBOX */
1898 d = s->pDrv->pu8Data;
1899 linesize = s->pDrv->cbScanline;
1900#endif /* VBOX */
1901
1902 y1 = 0;
1903 for(y = 0; y < height; y++) {
1904 addr = addr1;
1905 if (!(s->cr[0x17] & 1)) {
1906 int shift;
1907 /* CGA compatibility handling */
1908 shift = 14 + ((s->cr[0x17] >> 6) & 1);
1909 addr = (addr & ~(1 << shift)) | ((y1 & 1) << shift);
1910 }
1911 if (!(s->cr[0x17] & 2)) {
1912 addr = (addr & ~0x8000) | ((y1 & 2) << 14);
1913 }
1914#ifndef VBOX
1915 page0 = s->vram_offset + (addr & TARGET_PAGE_MASK);
1916 page1 = s->vram_offset + ((addr + bwidth - 1) & TARGET_PAGE_MASK);
1917 update = full_update |
1918 cpu_physical_memory_get_dirty(page0, VGA_DIRTY_FLAG) |
1919 cpu_physical_memory_get_dirty(page1, VGA_DIRTY_FLAG);
1920 if ((page1 - page0) > TARGET_PAGE_SIZE) {
1921 /* if wide line, can use another page */
1922 update |= cpu_physical_memory_get_dirty(page0 + TARGET_PAGE_SIZE,
1923 VGA_DIRTY_FLAG);
1924 }
1925#else /* VBOX */
1926 page0 = addr & TARGET_PAGE_MASK;
1927 page1 = (addr + bwidth - 1) & TARGET_PAGE_MASK;
1928 update = full_update | vga_is_dirty(s, page0) | vga_is_dirty(s, page1);
1929 if (page1 - page0 > TARGET_PAGE_SIZE) {
1930 /* if wide line, can use another page */
1931 update |= vga_is_dirty(s, page0 + TARGET_PAGE_SIZE);
1932 }
1933#endif /* VBOX */
1934 /* explicit invalidation for the hardware cursor */
1935 update |= (s->invalidated_y_table[y >> 5] >> (y & 0x1f)) & 1;
1936 if (update) {
1937 if (y_start < 0)
1938 y_start = y;
1939 if (page0 < page_min)
1940 page_min = page0;
1941 if (page1 > page_max)
1942 page_max = page1;
1943#ifndef VBOX
1944 vga_draw_line(s, d, s->vram_ptr + addr, width);
1945#else /* VBOX */
1946 if (s->pvExtVRAMHC != s->pDrv->pu8Data) /* Only if external VRAM was not setup. */
1947 vga_draw_line(s, d, s->CTXSUFF(vram_ptr) + addr, width);
1948#endif /* VBOX */
1949 if (s->cursor_draw_line)
1950 s->cursor_draw_line(s, d, y);
1951 } else {
1952 if (y_start >= 0) {
1953 /* flush to display */
1954#ifndef VBOX
1955 dpy_update(s->ds, 0, y_start,
1956 disp_width, y - y_start);
1957#else /* VBOX */
1958 s->pDrv->pfnUpdateRect(s->pDrv, 0, y_start, disp_width, y - y_start);
1959#endif /* VBOX */
1960 y_start = -1;
1961 }
1962 }
1963 if (!multi_run) {
1964 mask = (s->cr[0x17] & 3) ^ 3;
1965 if ((y1 & mask) == mask)
1966 addr1 += line_offset;
1967 y1++;
1968 multi_run = multi_scan;
1969 } else {
1970 multi_run--;
1971 }
1972 /* line compare acts on the displayed lines */
1973 if ((uint32_t)y == s->line_compare)
1974 addr1 = 0;
1975 d += linesize;
1976 }
1977 if (y_start >= 0) {
1978 /* flush to display */
1979#ifndef VBOX
1980 dpy_update(s->ds, 0, y_start,
1981 disp_width, y - y_start);
1982#else /* VBOX */
1983 s->pDrv->pfnUpdateRect(s->pDrv, 0, y_start, disp_width, y - y_start);
1984#endif /* VBOX */
1985 }
1986 /* reset modified pages */
1987 if (page_max != -1) {
1988#ifndef VBOX
1989 cpu_physical_memory_reset_dirty(page_min, page_max + TARGET_PAGE_SIZE,
1990 VGA_DIRTY_FLAG);
1991#else /* VBOX */
1992 vga_reset_dirty(s, page_min, page_max + TARGET_PAGE_SIZE);
1993#endif /* VBOX */
1994 }
1995 memset(s->invalidated_y_table, 0, ((height + 31) >> 5) * 4);
1996}
1997
1998static void vga_draw_blank(VGAState *s, int full_update)
1999{
2000#ifndef VBOX
2001 int i, w, val;
2002 uint8_t *d;
2003
2004 if (!full_update)
2005 return;
2006 if (s->last_scr_width <= 0 || s->last_scr_height <= 0)
2007 return;
2008 if (s->ds->depth == 8)
2009 val = s->rgb_to_pixel(0, 0, 0);
2010 else
2011 val = 0;
2012 w = s->last_scr_width * ((s->ds->depth + 7) >> 3);
2013 d = s->ds->data;
2014 for(i = 0; i < s->last_scr_height; i++) {
2015 memset(d, val, w);
2016 d += s->ds->linesize;
2017 }
2018 dpy_update(s->ds, 0, 0,
2019 s->last_scr_width, s->last_scr_height);
2020#else /* VBOX */
2021
2022 int i, w, val;
2023 uint8_t *d;
2024 uint32_t cbScanline = s->pDrv->cbScanline;
2025
2026 if (!full_update)
2027 return;
2028 if (s->last_scr_width <= 0 || s->last_scr_height <= 0)
2029 return;
2030 if (s->pDrv->cBits == 8)
2031 val = s->rgb_to_pixel(0, 0, 0);
2032 else
2033 val = 0;
2034 w = s->last_scr_width * ((s->pDrv->cBits + 7) >> 3);
2035 d = s->pDrv->pu8Data;
2036 for(i = 0; i < (int)s->last_scr_height; i++) {
2037 memset(d, val, w);
2038 d += cbScanline;
2039 }
2040 s->pDrv->pfnUpdateRect(s->pDrv, 0, 0, s->last_scr_width, s->last_scr_height);
2041#endif /* VBOX */
2042}
2043
2044#define GMODE_TEXT 0
2045#define GMODE_GRAPH 1
2046#define GMODE_BLANK 2
2047
2048#ifndef VBOX
2049void vga_update_display(void)
2050{
2051#ifdef DEBUG_sunlover
2052 LogFlow(("vga_update_display"));
2053#endif /* DEBUG_sunlover */
2054
2055 VGAState *s = vga_state;
2056#else /* VBOX */
2057static void vga_update_display(PVGASTATE s)
2058{
2059#endif /* VBOX */
2060 int full_update, graphic_mode;
2061
2062#ifndef VBOX
2063 if (s->ds->depth == 0) {
2064#else /* VBOX */
2065 if (s->pDrv->cBits == 0) {
2066#endif /* VBOX */
2067 /* nothing to do */
2068 } else {
2069#ifndef VBOX
2070 switch(s->ds->depth) {
2071#else /* VBOX */
2072 switch(s->pDrv->cBits) {
2073#endif /* VBOX */
2074 case 8:
2075 s->rgb_to_pixel = rgb_to_pixel8_dup;
2076 break;
2077 case 15:
2078 s->rgb_to_pixel = rgb_to_pixel15_dup;
2079 break;
2080 default:
2081 case 16:
2082 s->rgb_to_pixel = rgb_to_pixel16_dup;
2083 break;
2084 case 32:
2085 s->rgb_to_pixel = rgb_to_pixel32_dup;
2086 break;
2087 }
2088
2089 full_update = 0;
2090 if (!(s->ar_index & 0x20)) {
2091 graphic_mode = GMODE_BLANK;
2092 } else {
2093 graphic_mode = s->gr[6] & 1;
2094 }
2095 if (graphic_mode != s->graphic_mode) {
2096 s->graphic_mode = graphic_mode;
2097 full_update = 1;
2098 }
2099 switch(graphic_mode) {
2100 case GMODE_TEXT:
2101 vga_draw_text(s, full_update);
2102 break;
2103 case GMODE_GRAPH:
2104 vga_draw_graphic(s, full_update);
2105 break;
2106 case GMODE_BLANK:
2107 default:
2108 vga_draw_blank(s, full_update);
2109 break;
2110 }
2111 }
2112}
2113
2114/* force a full display refresh */
2115#ifndef VBOX
2116void vga_invalidate_display(void)
2117{
2118 VGAState *s = vga_state;
2119
2120 s->last_width = -1;
2121 s->last_height = -1;
2122}
2123#endif /* !VBOX */
2124
2125#ifndef VBOX /* see vgaR3Reset() */
2126static void vga_reset(VGAState *s)
2127{
2128 memset(s, 0, sizeof(VGAState));
2129 s->graphic_mode = -1; /* force full update */
2130}
2131#endif /* !VBOX */
2132
2133#ifndef VBOX
2134static CPUReadMemoryFunc *vga_mem_read[3] = {
2135 vga_mem_readb,
2136 vga_mem_readw,
2137 vga_mem_readl,
2138};
2139
2140static CPUWriteMemoryFunc *vga_mem_write[3] = {
2141 vga_mem_writeb,
2142 vga_mem_writew,
2143 vga_mem_writel,
2144};
2145#endif /* !VBOX */
2146
2147static void vga_save(QEMUFile *f, void *opaque)
2148{
2149 VGAState *s = (VGAState*)opaque;
2150 int i;
2151
2152 qemu_put_be32s(f, &s->latch);
2153 qemu_put_8s(f, &s->sr_index);
2154 qemu_put_buffer(f, s->sr, 8);
2155 qemu_put_8s(f, &s->gr_index);
2156 qemu_put_buffer(f, s->gr, 16);
2157 qemu_put_8s(f, &s->ar_index);
2158 qemu_put_buffer(f, s->ar, 21);
2159 qemu_put_be32s(f, &s->ar_flip_flop);
2160 qemu_put_8s(f, &s->cr_index);
2161 qemu_put_buffer(f, s->cr, 256);
2162 qemu_put_8s(f, &s->msr);
2163 qemu_put_8s(f, &s->fcr);
2164 qemu_put_8s(f, &s->st00);
2165 qemu_put_8s(f, &s->st01);
2166
2167 qemu_put_8s(f, &s->dac_state);
2168 qemu_put_8s(f, &s->dac_sub_index);
2169 qemu_put_8s(f, &s->dac_read_index);
2170 qemu_put_8s(f, &s->dac_write_index);
2171 qemu_put_buffer(f, s->dac_cache, 3);
2172 qemu_put_buffer(f, s->palette, 768);
2173
2174 qemu_put_be32s(f, &s->bank_offset);
2175#ifdef CONFIG_BOCHS_VBE
2176 qemu_put_byte(f, 1);
2177 qemu_put_be16s(f, &s->vbe_index);
2178 for(i = 0; i < VBE_DISPI_INDEX_NB; i++)
2179 qemu_put_be16s(f, &s->vbe_regs[i]);
2180 qemu_put_be32s(f, &s->vbe_start_addr);
2181 qemu_put_be32s(f, &s->vbe_line_offset);
2182 qemu_put_be32s(f, &s->vbe_bank_mask);
2183#else
2184 qemu_put_byte(f, 0);
2185#endif
2186}
2187
2188static int vga_load(QEMUFile *f, void *opaque, int version_id)
2189{
2190 VGAState *s = (VGAState*)opaque;
2191 int is_vbe, i;
2192
2193 if (version_id != 1)
2194#ifndef VBOX
2195 return -EINVAL;
2196#else /* VBOX */
2197 {
2198 Log(("vga_load: version_id=%d - UNKNOWN\n", version_id));
2199 return VERR_SSM_UNSUPPORTED_DATA_UNIT_VERSION;
2200 }
2201#endif /* VBOX */
2202
2203 qemu_get_be32s(f, &s->latch);
2204 qemu_get_8s(f, &s->sr_index);
2205 qemu_get_buffer(f, s->sr, 8);
2206 qemu_get_8s(f, &s->gr_index);
2207 qemu_get_buffer(f, s->gr, 16);
2208 qemu_get_8s(f, &s->ar_index);
2209 qemu_get_buffer(f, s->ar, 21);
2210 qemu_get_be32s(f, (uint32_t *)&s->ar_flip_flop);
2211 qemu_get_8s(f, &s->cr_index);
2212 qemu_get_buffer(f, s->cr, 256);
2213 qemu_get_8s(f, &s->msr);
2214 qemu_get_8s(f, &s->fcr);
2215 qemu_get_8s(f, &s->st00);
2216 qemu_get_8s(f, &s->st01);
2217
2218 qemu_get_8s(f, &s->dac_state);
2219 qemu_get_8s(f, &s->dac_sub_index);
2220 qemu_get_8s(f, &s->dac_read_index);
2221 qemu_get_8s(f, &s->dac_write_index);
2222 qemu_get_buffer(f, s->dac_cache, 3);
2223 qemu_get_buffer(f, s->palette, 768);
2224
2225 qemu_get_be32s(f, (uint32_t *)&s->bank_offset);
2226 is_vbe = qemu_get_byte(f);
2227#ifdef CONFIG_BOCHS_VBE
2228 if (!is_vbe)
2229#ifndef VBOX
2230 return -EINVAL;
2231#else /* VBOX */
2232 {
2233 Log(("vga_load: !is_vbe !!\n"));
2234 return VERR_SSM_DATA_UNIT_FORMAT_CHANGED;
2235 }
2236#endif /* VBOX */
2237 qemu_get_be16s(f, &s->vbe_index);
2238 for(i = 0; i < VBE_DISPI_INDEX_NB; i++)
2239 qemu_get_be16s(f, &s->vbe_regs[i]);
2240 qemu_get_be32s(f, &s->vbe_start_addr);
2241 qemu_get_be32s(f, &s->vbe_line_offset);
2242 qemu_get_be32s(f, &s->vbe_bank_mask);
2243#else
2244 if (is_vbe)
2245#ifndef VBOX
2246 return -EINVAL;
2247#else /* VBOX */
2248 {
2249 Log(("vga_load: is_vbe !!\n"));
2250 return VERR_SSM_DATA_UNIT_FORMAT_CHANGED;
2251 }
2252#endif /* VBOX */
2253#endif
2254
2255 /* force refresh */
2256 s->graphic_mode = -1;
2257 return 0;
2258}
2259
2260#ifndef VBOX /* see vgaR3IORegionMap */
2261static void vga_map(PCIDevice *pci_dev, int region_num,
2262 uint32_t addr, uint32_t size, int type)
2263{
2264 VGAState *s = vga_state;
2265
2266 cpu_register_physical_memory(addr, s->vram_size, s->vram_offset);
2267}
2268#endif
2269
2270#ifndef VBOX /* see vgaR3Construct */
2271void vga_common_init(VGAState *s, DisplayState *ds, uint8_t *vga_ram_base,
2272 unsigned long vga_ram_offset, int vga_ram_size)
2273#else
2274static void vga_init_expand(void)
2275#endif
2276{
2277 int i, j, v, b;
2278
2279 for(i = 0;i < 256; i++) {
2280 v = 0;
2281 for(j = 0; j < 8; j++) {
2282 v |= ((i >> j) & 1) << (j * 4);
2283 }
2284 expand4[i] = v;
2285
2286 v = 0;
2287 for(j = 0; j < 4; j++) {
2288 v |= ((i >> (2 * j)) & 3) << (j * 4);
2289 }
2290 expand2[i] = v;
2291 }
2292 for(i = 0; i < 16; i++) {
2293 v = 0;
2294 for(j = 0; j < 4; j++) {
2295 b = ((i >> j) & 1);
2296 v |= b << (2 * j);
2297 v |= b << (2 * j + 1);
2298 }
2299 expand4to8[i] = v;
2300 }
2301#ifdef VBOX
2302}
2303#else /* !VBOX */
2304 vga_reset(s);
2305
2306 s->vram_ptr = vga_ram_base;
2307 s->vram_offset = vga_ram_offset;
2308 s->vram_size = vga_ram_size;
2309 s->ds = ds;
2310 s->get_bpp = vga_get_bpp;
2311 s->get_offsets = vga_get_offsets;
2312 s->get_resolution = vga_get_resolution;
2313 /* XXX: currently needed for display */
2314 vga_state = s;
2315}
2316
2317
2318int vga_initialize(PCIBus *bus, DisplayState *ds, uint8_t *vga_ram_base,
2319 unsigned long vga_ram_offset, int vga_ram_size)
2320{
2321 VGAState *s;
2322
2323 s = qemu_mallocz(sizeof(VGAState));
2324 if (!s)
2325 return -1;
2326
2327 vga_common_init(s, ds, vga_ram_base, vga_ram_offset, vga_ram_size);
2328
2329 register_savevm("vga", 0, 1, vga_save, vga_load, s);
2330
2331 register_ioport_write(0x3c0, 16, 1, vga_ioport_write, s);
2332
2333 register_ioport_write(0x3b4, 2, 1, vga_ioport_write, s);
2334 register_ioport_write(0x3d4, 2, 1, vga_ioport_write, s);
2335 register_ioport_write(0x3ba, 1, 1, vga_ioport_write, s);
2336 register_ioport_write(0x3da, 1, 1, vga_ioport_write, s);
2337
2338 register_ioport_read(0x3c0, 16, 1, vga_ioport_read, s);
2339
2340 register_ioport_read(0x3b4, 2, 1, vga_ioport_read, s);
2341 register_ioport_read(0x3d4, 2, 1, vga_ioport_read, s);
2342 register_ioport_read(0x3ba, 1, 1, vga_ioport_read, s);
2343 register_ioport_read(0x3da, 1, 1, vga_ioport_read, s);
2344 s->bank_offset = 0;
2345
2346#ifdef CONFIG_BOCHS_VBE
2347 s->vbe_regs[VBE_DISPI_INDEX_ID] = VBE_DISPI_ID0;
2348 s->vbe_bank_mask = ((s->vram_size >> 16) - 1);
2349#if defined (TARGET_I386)
2350 register_ioport_read(0x1ce, 1, 2, vbe_ioport_read_index, s);
2351 register_ioport_read(0x1cf, 1, 2, vbe_ioport_read_data, s);
2352
2353 register_ioport_write(0x1ce, 1, 2, vbe_ioport_write_index, s);
2354 register_ioport_write(0x1cf, 1, 2, vbe_ioport_write_data, s);
2355
2356 /* old Bochs IO ports */
2357 register_ioport_read(0xff80, 1, 2, vbe_ioport_read_index, s);
2358 register_ioport_read(0xff81, 1, 2, vbe_ioport_read_data, s);
2359
2360 register_ioport_write(0xff80, 1, 2, vbe_ioport_write_index, s);
2361 register_ioport_write(0xff81, 1, 2, vbe_ioport_write_data, s);
2362#else
2363 register_ioport_read(0x1ce, 1, 2, vbe_ioport_read_index, s);
2364 register_ioport_read(0x1d0, 1, 2, vbe_ioport_read_data, s);
2365
2366 register_ioport_write(0x1ce, 1, 2, vbe_ioport_write_index, s);
2367 register_ioport_write(0x1d0, 1, 2, vbe_ioport_write_data, s);
2368#endif
2369#endif /* CONFIG_BOCHS_VBE */
2370
2371 vga_io_memory = cpu_register_io_memory(0, vga_mem_read, vga_mem_write, s);
2372 cpu_register_physical_memory(isa_mem_base + 0x000a0000, 0x20000,
2373 vga_io_memory);
2374
2375 if (bus) {
2376 PCIDevice *d;
2377 uint8_t *pci_conf;
2378
2379 d = pci_register_device(bus, "VGA",
2380 sizeof(PCIDevice),
2381 -1, NULL, NULL);
2382 pci_conf = d->config;
2383 pci_conf[0x00] = 0x34; // dummy VGA (same as Bochs ID)
2384 pci_conf[0x01] = 0x12;
2385 pci_conf[0x02] = 0x11;
2386 pci_conf[0x03] = 0x11;
2387 pci_conf[0x0a] = 0x00; // VGA controller
2388 pci_conf[0x0b] = 0x03;
2389 pci_conf[0x0e] = 0x00; // header_type
2390
2391 /* XXX: vga_ram_size must be a power of two */
2392 pci_register_io_region(d, 0, vga_ram_size,
2393 PCI_ADDRESS_SPACE_MEM_PREFETCH, vga_map);
2394 } else {
2395#ifdef CONFIG_BOCHS_VBE
2396 /* XXX: use optimized standard vga accesses */
2397 cpu_register_physical_memory(VBE_DISPI_LFB_PHYSICAL_ADDRESS,
2398 vga_ram_size, vga_ram_offset);
2399#endif
2400 }
2401 return 0;
2402}
2403#endif /* !VBOX */
2404
2405
2406#ifndef VBOX
2407/********************************************************/
2408/* vga screen dump */
2409
2410static int vga_save_w, vga_save_h;
2411
2412static void vga_save_dpy_update(DisplayState *s,
2413 int x, int y, int w, int h)
2414{
2415}
2416
2417static void vga_save_dpy_resize(DisplayState *s, int w, int h)
2418{
2419 s->linesize = w * 4;
2420#ifndef VBOX
2421 s->data = qemu_malloc(h * s->linesize);
2422#else /* VBOX */
2423 if (!s->data)
2424 {
2425 PPDMDEVINS pDevIns = VGASTATE2DEVINS((PVGASTATE)s->pvVgaState);
2426 s->data = PDMDevHlpMMHeapAlloc(pDevIns, h * s->linesize);
2427 }
2428 else // (32-bpp buffer is allocated by the caller)
2429 s->linesize = ((w * 32 + 31) / 32) * 4;
2430#endif /* VBOX */
2431 vga_save_w = w;
2432 vga_save_h = h;
2433}
2434
2435static void vga_save_dpy_refresh(DisplayState *s)
2436{
2437}
2438
2439static int ppm_save(const char *filename, uint8_t *data,
2440 int w, int h, int linesize)
2441{
2442 FILE *f;
2443 uint8_t *d, *d1;
2444 unsigned int v;
2445 int y, x;
2446
2447 f = fopen(filename, "wb");
2448 if (!f)
2449 return -1;
2450 fprintf(f, "P6\n%d %d\n%d\n",
2451 w, h, 255);
2452 d1 = data;
2453 for(y = 0; y < h; y++) {
2454 d = d1;
2455 for(x = 0; x < w; x++) {
2456 v = *(uint32_t *)d;
2457 fputc((v >> 16) & 0xff, f);
2458 fputc((v >> 8) & 0xff, f);
2459 fputc((v) & 0xff, f);
2460 d += 4;
2461 }
2462 d1 += linesize;
2463 }
2464 fclose(f);
2465 return 0;
2466}
2467
2468/* save the vga display in a PPM image even if no display is
2469 available */
2470void vga_screen_dump(const char *filename)
2471{
2472 VGAState *s = vga_state;
2473 DisplayState *saved_ds, ds1, *ds = &ds1;
2474
2475 /* XXX: this is a little hackish */
2476 vga_invalidate_display();
2477 saved_ds = s->ds;
2478
2479 memset(ds, 0, sizeof(DisplayState));
2480 ds->dpy_update = vga_save_dpy_update;
2481 ds->dpy_resize = vga_save_dpy_resize;
2482 ds->dpy_refresh = vga_save_dpy_refresh;
2483 ds->depth = 32;
2484
2485 s->ds = ds;
2486 s->graphic_mode = -1;
2487 vga_update_display();
2488
2489 if (ds->data) {
2490 ppm_save(filename, ds->data, vga_save_w, vga_save_h,
2491 s->ds->linesize);
2492 qemu_free(ds->data);
2493 }
2494 s->ds = saved_ds;
2495}
2496#endif /* !VBOX */
2497
2498
2499#if 0 //def VBOX
2500/* copy the vga display contents to the given buffer. the size of the buffer
2501 must be sufficient to store the screen copy (see below). the width and height
2502 parameters determine the required dimensions of the copy. If they differ
2503 from the actual screen dimensions, then the returned copy is shrinked or
2504 stretched accordingly. The copy is always a 32-bit image, so the size of
2505 the buffer supplied must be at least (((width * 32 + 31) / 32) * 4) * height,
2506 i.e. dword-aligned. returns zero if the operation was successfull and -1
2507 otherwise. */
2508
2509static int vga_copy_screen_to(PVGASTATE s, uint8_t *buf, int width, int height)
2510{
2511 DisplayState *saved_ds, ds1, *ds = &ds1;
2512 if (!buf || width <= 0 || height <= 0)
2513 return -1;
2514
2515 /* XXX: this is a little hackish */
2516 vga_invalidate_display(s);
2517 saved_ds = s->ds;
2518
2519 memset(ds, 0, sizeof(DisplayState));
2520 ds->dpy_update = vga_save_dpy_update;
2521 ds->dpy_resize = vga_save_dpy_resize;
2522 ds->dpy_refresh = vga_save_dpy_refresh;
2523 ds->depth = 32;
2524 ds->data = buf;
2525 ds->pvVgaState = s;
2526
2527 s->ds = ds;
2528 s->graphic_mode = -1;
2529 vga_update_display(s);
2530
2531//@@TODO (dmik): implement stretching/shrinking!
2532
2533 s->ds = saved_ds;
2534 return 0;
2535}
2536
2537/* copy the given buffer to the vga display. width and height define the
2538 dimensions of the image in the buffer. x and y define the point on the
2539 vga display to copy the image to. the buffer is assumed to contain a 32-bit
2540 image, so the size of one scanline must be ((width * 32 + 31) / 32) * 4),
2541 i.e. dword-aligned. returns zero if the operation was successfull and -1
2542 otherwise. */
2543static int vga_copy_screen_from(PVGASTATE s, uint8_t *buf, int x, int y, int width, int height)
2544{
2545 int bpl = ((width * 32 + 31) / 32) * 4;
2546 int linesize = s->ds->linesize;
2547 uint8_t *dst;
2548 uint8_t *src;
2549 int bpp;
2550 vga_draw_line_func *vga_draw_line;
2551
2552 if (!buf || x < 0 || y < 0 || width <= 0 || height <= 0
2553 || x + width > s->ds->width || y + height > s->ds->height)
2554 return -1;
2555
2556 vga_draw_line = vga_draw_line_table[VGA_DRAW_LINE32 * 4 + get_depth_index(s->ds->depth)];
2557 switch (s->ds->depth) {
2558 case 8: bpp = 1; break;
2559 case 15:
2560 case 16: bpp = 2; break;
2561 case 32: bpp = 4; break;
2562 default: return -1;
2563 }
2564
2565 dst = s->ds->data + y * linesize + x * bpp;
2566 src = buf;
2567 for (y = 0; y < height; y ++)
2568 {
2569 vga_draw_line(s, dst, src, width);
2570 dst += linesize;
2571 src += bpl;
2572 }
2573
2574 return 0;
2575}
2576#endif
2577
2578#endif /* !VBOX || !IN_GC || !IN_RING0 */
2579
2580
2581
2582#ifdef VBOX /* InnoTek code start */
2583
2584
2585/* -=-=-=-=-=- all contexts -=-=-=-=-=- */
2586
2587/**
2588 * Port I/O Handler for VGA OUT operations.
2589 *
2590 * @returns VBox status code.
2591 *
2592 * @param pDevIns The device instance.
2593 * @param pvUser User argument - ignored.
2594 * @param Port Port number used for the IN operation.
2595 * @param u32 The value to output.
2596 * @param cb The value size in bytes.
2597 */
2598PDMBOTHCBDECL(int) vgaIOPortWrite(PPDMDEVINS pDevIns, void *pvUser, RTIOPORT Port, uint32_t u32, unsigned cb)
2599{
2600 NOREF(pvUser);
2601 if (cb == 1)
2602 vga_ioport_write(PDMINS2DATA(pDevIns, PVGASTATE), Port, u32);
2603 else if (cb == 2)
2604 {
2605 vga_ioport_write(PDMINS2DATA(pDevIns, PVGASTATE), Port, u32 & 0xff);
2606 vga_ioport_write(PDMINS2DATA(pDevIns, PVGASTATE), Port + 1, u32 >> 8);
2607 }
2608 return VINF_SUCCESS;
2609}
2610
2611
2612/**
2613 * Port I/O Handler for VGA IN operations.
2614 *
2615 * @returns VBox status code.
2616 *
2617 * @param pDevIns The device instance.
2618 * @param pvUser User argument - ignored.
2619 * @param Port Port number used for the IN operation.
2620 * @param pu32 Where to store the result.
2621 * @param cb Number of bytes read.
2622 */
2623PDMBOTHCBDECL(int) vgaIOPortRead(PPDMDEVINS pDevIns, void *pvUser, RTIOPORT Port, uint32_t *pu32, unsigned cb)
2624{
2625 NOREF(pvUser);
2626 if (cb == 1)
2627 {
2628 *pu32 = vga_ioport_read(PDMINS2DATA(pDevIns, PVGASTATE), Port);
2629 return VINF_SUCCESS;
2630 }
2631 else if (cb == 2)
2632 {
2633 *pu32 = vga_ioport_read(PDMINS2DATA(pDevIns, PVGASTATE), Port)
2634 | (vga_ioport_read(PDMINS2DATA(pDevIns, PVGASTATE), Port + 1) << 8);
2635 return VINF_SUCCESS;
2636 }
2637 return VERR_IOM_IOPORT_UNUSED;
2638}
2639
2640
2641/**
2642 * Port I/O Handler for VBE OUT operations.
2643 *
2644 * @returns VBox status code.
2645 *
2646 * @param pDevIns The device instance.
2647 * @param pvUser User argument - ignored.
2648 * @param Port Port number used for the IN operation.
2649 * @param u32 The value to output.
2650 * @param cb The value size in bytes.
2651 */
2652PDMBOTHCBDECL(int) vgaIOPortWriteVBEData(PPDMDEVINS pDevIns, void *pvUser, RTIOPORT Port, uint32_t u32, unsigned cb)
2653{
2654 VGAState *s = PDMINS2DATA(pDevIns, PVGASTATE);
2655
2656 NOREF(pvUser);
2657
2658#ifdef IN_GC
2659 /*
2660 * The VBE_DISPI_INDEX_ENABLE has to be done on the host in order to call pfnLFBModeChange callback.
2661 */
2662 if (s->vbe_index == VBE_DISPI_INDEX_ENABLE)
2663 {
2664 Log(("vgaIOPortWriteVBEData: VBE_DISPI_INDEX_ENABLE - Switching to host...\n"));
2665 return VINF_IOM_HC_IOPORT_WRITE;
2666 }
2667#endif
2668#ifdef VBE_BYTEWISE_IO
2669 if (cb == 1)
2670 {
2671 if (!s->fWriteVBEData)
2672 {
2673 if ( (s->vbe_index == VBE_DISPI_INDEX_ENABLE)
2674 && (u32 & VBE_DISPI_ENABLED))
2675 {
2676 s->fWriteVBEData = false;
2677 vbe_ioport_write_data(s, Port, u32 & 0xFF);
2678 return VINF_SUCCESS;
2679 }
2680 else
2681 {
2682 s->cbWriteVBEData = u32 & 0xFF;
2683 s->fWriteVBEData = true;
2684 return VINF_SUCCESS;
2685 }
2686 }
2687 else
2688 {
2689 u32 = (s->cbWriteVBEData << 8) | (u32 & 0xFF);
2690 s->fWriteVBEData = false;
2691 cb = 2;
2692 }
2693 }
2694#endif
2695 if (cb == 2)
2696 {
2697//#ifdef IN_GC
2698// /*
2699// * The VBE_DISPI_INDEX_ENABLE memsets the entire frame buffer.
2700// * Since we're not mapping the entire framebuffer any longer that
2701// * has to be done on the host.
2702// */
2703// if ( (s->vbe_index == VBE_DISPI_INDEX_ENABLE)
2704// && (u32 & VBE_DISPI_ENABLED))
2705// {
2706// Log(("vgaIOPortWriteVBEData: VBE_DISPI_INDEX_ENABLE & VBE_DISPI_ENABLED - Switching to host...\n"));
2707// return VINF_IOM_HC_IOPORT_WRITE;
2708// }
2709//#endif
2710 vbe_ioport_write_data(s, Port, u32);
2711 }
2712 else
2713 AssertMsgFailed(("vgaIOPortWriteVBEData: Port=%#x cb=%d u32=%#x\n", Port, cb, u32));
2714 return VINF_SUCCESS;
2715}
2716
2717
2718/**
2719 * Port I/O Handler for VBE OUT operations.
2720 *
2721 * @returns VBox status code.
2722 *
2723 * @param pDevIns The device instance.
2724 * @param pvUser User argument - ignored.
2725 * @param Port Port number used for the IN operation.
2726 * @param u32 The value to output.
2727 * @param cb The value size in bytes.
2728 */
2729PDMBOTHCBDECL(int) vgaIOPortWriteVBEIndex(PPDMDEVINS pDevIns, void *pvUser, RTIOPORT Port, uint32_t u32, unsigned cb)
2730{
2731 NOREF(pvUser);
2732#ifdef VBE_BYTEWISE_IO
2733 if (cb == 1)
2734 {
2735 VGAState *s = PDMINS2DATA(pDevIns, PVGASTATE);
2736 if (!s->fWriteVBEIndex)
2737 {
2738 s->cbWriteVBEIndex = u32 & 0x00FF;
2739 s->fWriteVBEIndex = true;
2740 return VINF_SUCCESS;
2741 }
2742 else
2743 {
2744 s->fWriteVBEIndex = false;
2745 vbe_ioport_write_index(s, Port, (s->cbWriteVBEIndex << 8) | (u32 & 0x00FF));
2746 return VINF_SUCCESS;
2747 }
2748 }
2749 else
2750#endif
2751 if (cb == 2)
2752 vbe_ioport_write_index(PDMINS2DATA(pDevIns, PVGASTATE), Port, u32);
2753 else
2754 AssertMsgFailed(("vgaIOPortWriteVBEIndex: Port=%#x cb=%d u32=%#x\n", Port, cb, u32));
2755 return VINF_SUCCESS;
2756}
2757
2758
2759/**
2760 * Port I/O Handler for VBE IN operations.
2761 *
2762 * @returns VBox status code.
2763 *
2764 * @param pDevIns The device instance.
2765 * @param pvUser User argument - ignored.
2766 * @param Port Port number used for the IN operation.
2767 * @param pu32 Where to store the result.
2768 * @param cb Number of bytes to read.
2769 */
2770PDMBOTHCBDECL(int) vgaIOPortReadVBEData(PPDMDEVINS pDevIns, void *pvUser, RTIOPORT Port, uint32_t *pu32, unsigned cb)
2771{
2772 NOREF(pvUser);
2773#ifdef VBE_BYTEWISE_IO
2774 if (cb == 1)
2775 {
2776 VGAState *s = PDMINS2DATA(pDevIns, PVGASTATE);
2777
2778 if (!s->fReadVBEData)
2779 {
2780 *pu32 = (vbe_ioport_read_data(s, Port) >> 8) & 0xFF;
2781 s->fReadVBEData = true;
2782 return VINF_SUCCESS;
2783 }
2784 else
2785 {
2786 *pu32 = vbe_ioport_read_data(s, Port) & 0xFF;
2787 s->fReadVBEData = false;
2788 return VINF_SUCCESS;
2789 }
2790 }
2791 else
2792#endif
2793 if (cb == 2)
2794 {
2795 *pu32 = vbe_ioport_read_data(PDMINS2DATA(pDevIns, PVGASTATE), Port);
2796 return VINF_SUCCESS;
2797 }
2798 else if (cb == 4)
2799 {
2800 /* Quick hack for getting the vram size. */
2801 VGAState *s = PDMINS2DATA(pDevIns, PVGASTATE);
2802 *pu32 = s->vram_size;
2803 return VINF_SUCCESS;
2804 }
2805 AssertMsgFailed(("vgaIOPortReadVBEData: Port=%#x cb=%d\n", Port, cb));
2806 return VERR_IOM_IOPORT_UNUSED;
2807}
2808
2809
2810/**
2811 * Port I/O Handler for VBE IN operations.
2812 *
2813 * @returns VBox status code.
2814 *
2815 * @param pDevIns The device instance.
2816 * @param pvUser User argument - ignored.
2817 * @param Port Port number used for the IN operation.
2818 * @param pu32 Where to store the result.
2819 * @param cb Number of bytes to read.
2820 */
2821PDMBOTHCBDECL(int) vgaIOPortReadVBEIndex(PPDMDEVINS pDevIns, void *pvUser, RTIOPORT Port, uint32_t *pu32, unsigned cb)
2822{
2823 NOREF(pvUser);
2824#ifdef VBE_BYTEWISE_IO
2825 if (cb == 1)
2826 {
2827 VGAState *s = PDMINS2DATA(pDevIns, PVGASTATE);
2828
2829 if (!s->fReadVBEIndex)
2830 {
2831 *pu32 = (vbe_ioport_read_index(s, Port) >> 8) & 0xFF;
2832 s->fReadVBEIndex = true;
2833 return VINF_SUCCESS;
2834 }
2835 else
2836 {
2837 *pu32 = vbe_ioport_read_index(s, Port) & 0xFF;
2838 s->fReadVBEIndex = false;
2839 return VINF_SUCCESS;
2840 }
2841 }
2842 else
2843#endif
2844 if (cb == 2)
2845 {
2846 *pu32 = vbe_ioport_read_index(PDMINS2DATA(pDevIns, PVGASTATE), Port);
2847 return VINF_SUCCESS;
2848 }
2849 AssertMsgFailed(("vgaIOPortReadVBEIndex: Port=%#x cb=%d\n", Port, cb));
2850 return VERR_IOM_IOPORT_UNUSED;
2851}
2852
2853
2854
2855
2856
2857/* -=-=-=-=-=- Guest Context -=-=-=-=-=- */
2858
2859/*
2860 * Internal. For use inside VGAGCMemoryFillWrite only.
2861 * Macro for apply logical operation and bit mask.
2862 */
2863#define APPLY_LOGICAL_AND_MASK(s, val, bit_mask) \
2864 /* apply logical operation */ \
2865 switch(s->gr[3] >> 3) \
2866 { \
2867 case 0: \
2868 default: \
2869 /* nothing to do */ \
2870 break; \
2871 case 1: \
2872 /* and */ \
2873 val &= s->latch; \
2874 break; \
2875 case 2: \
2876 /* or */ \
2877 val |= s->latch; \
2878 break; \
2879 case 3: \
2880 /* xor */ \
2881 val ^= s->latch; \
2882 break; \
2883 } \
2884 /* apply bit mask */ \
2885 val = (val & bit_mask) | (s->latch & ~bit_mask)
2886
2887/**
2888 * Legacy VGA memory (0xa0000 - 0xbffff) write hook, to be called from IOM and from the inside of VGADeviceGC.cpp.
2889 * This is the advanced version of vga_mem_writeb function.
2890 *
2891 * @returns VBox status code.
2892 * @param pDevIns Pointer device instance.
2893 * @param pvUser User argument - ignored.
2894 * @param GCPhysAddr Physical address of memory to write.
2895 * @param u32Item Data to write, up to 4 bytes.
2896 * @param cbItem Size of data Item, only 1/2/4 bytes is allowed for now.
2897 * @param cItems Number of data items to write.
2898 */
2899PDMBOTHCBDECL(int) vgaMMIOFill(PPDMDEVINS pDevIns, void *pvUser, RTGCPHYS GCPhysAddr, uint32_t u32Item, unsigned cbItem, unsigned cItems)
2900{
2901 PVGASTATE pData = PDMINS2DATA(pDevIns, PVGASTATE);
2902 uint32_t b;
2903 uint32_t write_mask, bit_mask, set_mask;
2904 uint32_t aVal[4]; /** @todo r=bird: Why is this an 32-bit array? */
2905 unsigned i;
2906 NOREF(pvUser);
2907 for (i = 0; i < cbItem; i++)
2908 {
2909 aVal[i] = u32Item & 0xff;
2910 u32Item >>= 8;
2911 }
2912
2913 /* convert to VGA memory offset */
2914 /// @todo add check for the end of region
2915 GCPhysAddr &= 0x1ffff;
2916 switch((pData->gr[6] >> 2) & 3) {
2917 case 0:
2918 break;
2919 case 1:
2920 if (GCPhysAddr >= 0x10000)
2921 return VINF_SUCCESS;
2922 GCPhysAddr += pData->bank_offset;
2923 break;
2924 case 2:
2925 GCPhysAddr -= 0x10000;
2926 if (GCPhysAddr >= 0x8000)
2927 return VINF_SUCCESS;
2928 break;
2929 default:
2930 case 3:
2931 GCPhysAddr -= 0x18000;
2932 if (GCPhysAddr >= 0x8000)
2933 return VINF_SUCCESS;
2934 break;
2935 }
2936
2937 if (pData->sr[4] & 0x08) {
2938 /* chain 4 mode : simplest access */
2939#ifdef IN_GC
2940 if (GCPhysAddr + cItems * cbItem >= VGA_MAPPING_SIZE)
2941 return VINF_IOM_HC_MMIO_WRITE;
2942#else
2943 if (GCPhysAddr + cItems * cbItem >= pData->vram_size)
2944 {
2945 AssertMsgFailed(("GCPhysAddr=%VGp cItems=%#x cbItem=%d\n", GCPhysAddr, cItems, cbItem));
2946 return VINF_SUCCESS;
2947 }
2948#endif
2949
2950 while (cItems-- > 0)
2951 for (i = 0; i < cbItem; i++)
2952 {
2953 if (pData->sr[2] & (1 << (GCPhysAddr & 3)))
2954 {
2955 CTXSUFF(pData->vram_ptr)[GCPhysAddr] = aVal[i];
2956 vga_set_dirty(pData, GCPhysAddr);
2957 }
2958 GCPhysAddr++;
2959 }
2960 } else if (pData->gr[5] & 0x10) {
2961 /* odd/even mode (aka text mode mapping) */
2962#ifdef IN_GC
2963 if (GCPhysAddr * 2 + cItems * cbItem >= VGA_MAPPING_SIZE)
2964 return VINF_IOM_HC_MMIO_WRITE;
2965#else
2966 if (GCPhysAddr * 2 + cItems * cbItem >= pData->vram_size)
2967 {
2968 AssertMsgFailed(("GCPhysAddr=%VGp cItems=%#x cbItem=%d\n", GCPhysAddr, cItems, cbItem));
2969 return VINF_SUCCESS;
2970 }
2971#endif
2972 while (cItems-- > 0)
2973 for (i = 0; i < cbItem; i++)
2974 {
2975 unsigned plane = (pData->gr[4] & 2) | (GCPhysAddr & 1);
2976 if (pData->sr[2] & (1 << plane)) {
2977 RTGCPHYS PhysAddr2 = ((GCPhysAddr & ~1) << 1) | plane;
2978 CTXSUFF(pData->vram_ptr)[PhysAddr2] = aVal[i];
2979 vga_set_dirty(pData, PhysAddr2);
2980 }
2981 GCPhysAddr++;
2982 }
2983 } else {
2984#ifdef IN_GC
2985 if (GCPhysAddr + cItems * cbItem >= VGA_MAPPING_SIZE)
2986 return VINF_IOM_HC_MMIO_WRITE;
2987#else
2988 if (GCPhysAddr + cItems * cbItem >= pData->vram_size)
2989 {
2990 AssertMsgFailed(("GCPhysAddr=%VGp cItems=%#x cbItem=%d\n", GCPhysAddr, cItems, cbItem));
2991 return VINF_SUCCESS;
2992 }
2993#endif
2994
2995 /* standard VGA latched access */
2996 switch(pData->gr[5] & 3) {
2997 default:
2998 case 0:
2999 /* rotate */
3000 b = pData->gr[3] & 7;
3001 bit_mask = pData->gr[8];
3002 bit_mask |= bit_mask << 8;
3003 bit_mask |= bit_mask << 16;
3004 set_mask = mask16[pData->gr[1]];
3005
3006 for (i = 0; i < cbItem; i++)
3007 {
3008 aVal[i] = ((aVal[i] >> b) | (aVal[i] << (8 - b))) & 0xff;
3009 aVal[i] |= aVal[i] << 8;
3010 aVal[i] |= aVal[i] << 16;
3011
3012 /* apply set/reset mask */
3013 aVal[i] = (aVal[i] & ~set_mask) | (mask16[pData->gr[0]] & set_mask);
3014
3015 APPLY_LOGICAL_AND_MASK(pData, aVal[i], bit_mask);
3016 }
3017 break;
3018 case 1:
3019 for (i = 0; i < cbItem; i++)
3020 aVal[i] = pData->latch;
3021 break;
3022 case 2:
3023 bit_mask = pData->gr[8];
3024 bit_mask |= bit_mask << 8;
3025 bit_mask |= bit_mask << 16;
3026 for (i = 0; i < cbItem; i++)
3027 {
3028 aVal[i] = mask16[aVal[i] & 0x0f];
3029
3030 APPLY_LOGICAL_AND_MASK(pData, aVal[i], bit_mask);
3031 }
3032 break;
3033 case 3:
3034 /* rotate */
3035 b = pData->gr[3] & 7;
3036
3037 for (i = 0; i < cbItem; i++)
3038 {
3039 aVal[i] = (aVal[i] >> b) | (aVal[i] << (8 - b));
3040 bit_mask = pData->gr[8] & aVal[i];
3041 bit_mask |= bit_mask << 8;
3042 bit_mask |= bit_mask << 16;
3043 aVal[i] = mask16[pData->gr[0]];
3044
3045 APPLY_LOGICAL_AND_MASK(pData, aVal[i], bit_mask);
3046 }
3047 break;
3048 }
3049
3050 /* mask data according to sr[2] */
3051 write_mask = mask16[pData->sr[2]];
3052
3053 /* actually write data */
3054 if (cbItem == 1)
3055 {
3056 /* The most frequently case is 1 byte I/O. */
3057 while (cItems-- > 0)
3058 {
3059 ((uint32_t *)pData->CTXSUFF(vram_ptr))[GCPhysAddr] = (((uint32_t *)pData->CTXSUFF(vram_ptr))[GCPhysAddr] & ~write_mask) | (aVal[0] & write_mask);
3060 vga_set_dirty(pData, GCPhysAddr << 2);
3061 GCPhysAddr++;
3062 }
3063 }
3064 else if (cbItem == 2)
3065 {
3066 /* The second case is 2 bytes I/O. */
3067 while (cItems-- > 0)
3068 {
3069 ((uint32_t *)pData->CTXSUFF(vram_ptr))[GCPhysAddr] = (((uint32_t *)pData->CTXSUFF(vram_ptr))[GCPhysAddr] & ~write_mask) | (aVal[0] & write_mask);
3070 vga_set_dirty(pData, GCPhysAddr << 2);
3071 GCPhysAddr++;
3072
3073 ((uint32_t *)pData->CTXSUFF(vram_ptr))[GCPhysAddr] = (((uint32_t *)pData->CTXSUFF(vram_ptr))[GCPhysAddr] & ~write_mask) | (aVal[1] & write_mask);
3074 vga_set_dirty(pData, GCPhysAddr << 2);
3075 GCPhysAddr++;
3076 }
3077 }
3078 else
3079 {
3080 /* And the rest is 4 bytes. */
3081 Assert(cbItem == 4);
3082 while (cItems-- > 0)
3083 for (i = 0; i < cbItem; i++)
3084 {
3085 ((uint32_t *)pData->CTXSUFF(vram_ptr))[GCPhysAddr] = (((uint32_t *)pData->CTXSUFF(vram_ptr))[GCPhysAddr] & ~write_mask) | (aVal[i] & write_mask);
3086 vga_set_dirty(pData, GCPhysAddr << 2);
3087 GCPhysAddr++;
3088 }
3089 }
3090 }
3091 return VINF_SUCCESS;
3092}
3093#undef APPLY_LOGICAL_AND_MASK
3094
3095
3096/**
3097 * Legacy VGA memory (0xa0000 - 0xbffff) read hook, to be called from IOM.
3098 *
3099 * @returns VBox status code.
3100 * @param pDevIns Pointer device instance.
3101 * @param pvUser User argument - ignored.
3102 * @param GCPhysAddr Physical address of memory to read.
3103 * @param pv Where to store readed data.
3104 * @param cb Bytes to read.
3105 */
3106PDMBOTHCBDECL(int) vgaMMIORead(PPDMDEVINS pDevIns, void *pvUser, RTGCPHYS GCPhysAddr, void *pv, unsigned cb)
3107{
3108 PVGASTATE pData = PDMINS2DATA(pDevIns, PVGASTATE);
3109 STAM_PROFILE_START(&pData->StatGCMemoryRead, a);
3110 NOREF(pvUser);
3111 switch (cb)
3112 {
3113 case 1:
3114 *(uint8_t *)pv = vga_mem_readb(pData, GCPhysAddr); break;
3115 case 2:
3116 *(uint16_t *)pv = vga_mem_readb(pData, GCPhysAddr)
3117 | (vga_mem_readb(pData, GCPhysAddr + 1) << 8);
3118 break;
3119 case 4:
3120 *(uint32_t *)pv = vga_mem_readb(pData, GCPhysAddr)
3121 | (vga_mem_readb(pData, GCPhysAddr + 1) << 8)
3122 | (vga_mem_readb(pData, GCPhysAddr + 2) << 16)
3123 | (vga_mem_readb(pData, GCPhysAddr + 3) << 24);
3124 break;
3125
3126 default:
3127 {
3128 uint8_t *pu8Data = (uint8_t *)pv;
3129 while (cb-- > 0)
3130 *pu8Data++ = vga_mem_readb(pData, GCPhysAddr++);
3131 }
3132 }
3133 STAM_PROFILE_STOP(&pData->StatGCMemoryRead, a);
3134 return VINF_SUCCESS;
3135}
3136
3137/**
3138 * Legacy VGA memory (0xa0000 - 0xbffff) write hook, to be called from IOM.
3139 *
3140 * @returns VBox status code.
3141 * @param pDevIns Pointer device instance.
3142 * @param pvUser User argument - ignored.
3143 * @param GCPhysAddr Physical address of memory to write.
3144 * @param pv Pointer to data.
3145 * @param cb Bytes to write.
3146 */
3147PDMBOTHCBDECL(int) vgaMMIOWrite(PPDMDEVINS pDevIns, void *pvUser, RTGCPHYS GCPhysAddr, void *pv, unsigned cb)
3148{
3149 PVGASTATE pData = PDMINS2DATA(pDevIns, PVGASTATE);
3150 uint8_t *pu8 = (uint8_t *)pv;
3151 int rc = VINF_SUCCESS;
3152 STAM_PROFILE_START(&pData->StatGCMemoryWrite, a);
3153
3154 switch (cb)
3155 {
3156 case 1:
3157 rc = vga_mem_writeb(pData, GCPhysAddr, *pu8);
3158 break;
3159#if 1
3160 case 2:
3161 rc = vga_mem_writeb(pData, GCPhysAddr + 0, pu8[0]);
3162 if (RT_LIKELY(rc == VINF_SUCCESS))
3163 rc = vga_mem_writeb(pData, GCPhysAddr + 1, pu8[1]);
3164 break;
3165 case 4:
3166 rc = vga_mem_writeb(pData, GCPhysAddr + 0, pu8[0]);
3167 if (RT_LIKELY(rc == VINF_SUCCESS))
3168 rc = vga_mem_writeb(pData, GCPhysAddr + 1, pu8[1]);
3169 if (RT_LIKELY(rc == VINF_SUCCESS))
3170 rc = vga_mem_writeb(pData, GCPhysAddr + 2, pu8[2]);
3171 if (RT_LIKELY(rc == VINF_SUCCESS))
3172 rc = vga_mem_writeb(pData, GCPhysAddr + 3, pu8[3]);
3173 break;
3174#else
3175 case 2:
3176 rc = vgaMMIOFill(pDevIns, GCPhysAddr, *(uint16_t *)pv, 2, 1);
3177 break;
3178 case 4:
3179 rc = vgaMMIOFill(pDevIns, GCPhysAddr, *(uint32_t *)pv, 4, 1);
3180 break;
3181#endif
3182 default:
3183 while (cb-- > 0 && rc == VINF_SUCCESS)
3184 rc = vga_mem_writeb(pData, GCPhysAddr++, *pu8++);
3185 break;
3186
3187 }
3188 STAM_PROFILE_STOP(&pData->StatGCMemoryWrite, a);
3189 return rc;
3190}
3191
3192
3193/**
3194 * Handle LFB access.
3195 * @returns VBox status code.
3196 * @param pVM VM handle.
3197 * @param pData VGA device instance data.
3198 * @param GCPhys The access physical address.
3199 * @param GCPtr The access virtual address (only GC).
3200 */
3201static int vgaLFBAccess(PVM pVM, PVGASTATE pData, RTGCPHYS GCPhys, RTGCPTR GCPtr)
3202{
3203 int rc;
3204
3205 /*
3206 * Set page dirty bit.
3207 */
3208 vga_set_dirty(pData, GCPhys - pData->GCPhysVRAM);
3209 pData->fLFBUpdated = true;
3210
3211 /*
3212 * Turn of the write handler for this particular page and make it R/W.
3213 * Then return telling the caller to restart the guest instruction.
3214 * ASSUME: the guest always maps video memory RW.
3215 */
3216 rc = PGMHandlerPhysicalPageTempOff(pVM, pData->GCPhysVRAM, GCPhys);
3217 if (VBOX_SUCCESS(rc))
3218 {
3219#ifndef IN_RING3
3220 rc = PGMShwModifyPage(pVM, GCPtr, 1, X86_PTE_RW, ~(uint64_t)X86_PTE_RW);
3221 if (VBOX_SUCCESS(rc))
3222 return VINF_SUCCESS;
3223 else
3224 AssertMsgFailed(("PGMShwModifyPage -> rc=%d\n", rc));
3225#else /* IN_RING3 : We don't have any virtual page address of the access here. */
3226 Assert(GCPtr == 0);
3227 return VINF_SUCCESS;
3228#endif
3229 }
3230 else
3231 AssertMsgFailed(("PGMHandlerPhysicalPageTempOff -> rc=%d\n", rc));
3232
3233 return rc;
3234}
3235
3236
3237#ifdef IN_GC
3238/**
3239 * #PF Handler for VBE LFB access.
3240 *
3241 * @returns VBox status code (appropriate for GC return).
3242 * @param pVM VM Handle.
3243 * @param uErrorCode CPU Error code.
3244 * @param pRegFrame Trap register frame.
3245 * @param pvFault The fault address (cr2).
3246 * @param GCPhysFault The GC physical address corresponding to pvFault.
3247 * @param pvUser User argument, ignored.
3248 */
3249PDMBOTHCBDECL(int) vgaGCLFBAccessHandler(PVM pVM, RTGCUINT uErrorCode, PCPUMCTXCORE pRegFrame, RTGCPTR pvFault, RTGCPHYS GCPhysFault, void *pvUser)
3250{
3251 PVGASTATE pData = (PVGASTATE)pvUser;
3252 Assert(pData);
3253 Assert(GCPhysFault >= pData->GCPhysVRAM);
3254 AssertMsg(uErrorCode & X86_TRAP_PF_RW, ("uErrorCode=%#x\n", uErrorCode));
3255
3256 return vgaLFBAccess(pVM, pData, GCPhysFault, pvFault);
3257}
3258
3259#elif IN_RING0
3260
3261/**
3262 * #PF Handler for VBE LFB access.
3263 *
3264 * @returns VBox status code (appropriate for GC return).
3265 * @param pVM VM Handle.
3266 * @param uErrorCode CPU Error code.
3267 * @param pRegFrame Trap register frame.
3268 * @param pvFault The fault address (cr2).
3269 * @param GCPhysFault The GC physical address corresponding to pvFault.
3270 * @param pvUser User argument, ignored.
3271 */
3272PDMBOTHCBDECL(int) vgaR0LFBAccessHandler(PVM pVM, RTGCUINT uErrorCode, PCPUMCTXCORE pRegFrame, RTGCPTR pvFault, RTGCPHYS GCPhysFault, void *pvUser)
3273{
3274 PVGASTATE pData = (PVGASTATE)pvUser;
3275 Assert(pData);
3276 Assert(GCPhysFault >= pData->GCPhysVRAM);
3277 AssertMsg(uErrorCode & X86_TRAP_PF_RW, ("uErrorCode=%#x\n", uErrorCode));
3278
3279 return vgaLFBAccess(pVM, pData, GCPhysFault, pvFault);
3280}
3281
3282#else /* IN_RING3 */
3283
3284/**
3285 * HC access handler for the LFB.
3286 *
3287 * @returns VINF_SUCCESS if the handler have carried out the operation.
3288 * @returns VINF_PGM_HANDLER_DO_DEFAULT if the caller should carry out the access operation.
3289 * @param pVM VM Handle.
3290 * @param GCPhys The physical address the guest is writing to.
3291 * @param pvPhys The HC mapping of that address.
3292 * @param pvBuf What the guest is reading/writing.
3293 * @param cbBuf How much it's reading/writing.
3294 * @param enmAccessType The access type.
3295 * @param pvUser User argument.
3296 */
3297static DECLCALLBACK(int) vgaR3LFBAccessHandler(PVM pVM, RTGCPHYS GCPhys, void *pvPhys, void *pvBuf, size_t cbBuf, PGMACCESSTYPE enmAccessType, void *pvUser)
3298{
3299 PVGASTATE pData = (PVGASTATE)pvUser;
3300 int rc;
3301 Assert(pData);
3302 Assert(GCPhys >= pData->GCPhysVRAM);
3303 rc = vgaLFBAccess(pVM, pData, GCPhys, 0);
3304 if (VBOX_SUCCESS(rc))
3305 return VINF_PGM_HANDLER_DO_DEFAULT;
3306 AssertMsg(rc <= VINF_SUCCESS, ("rc=%Vrc\n", rc));
3307 return rc;
3308}
3309#endif /* IN_RING3 */
3310
3311
3312/* -=-=-=-=-=- Ring 3 -=-=-=-=-=- */
3313
3314#ifdef IN_RING3
3315
3316# ifdef VBE_NEW_DYN_LIST
3317/**
3318 * Port I/O Handler for VBE Extra OUT operations.
3319 *
3320 * @returns VBox status code.
3321 *
3322 * @param pDevIns The device instance.
3323 * @param pvUser User argument - ignored.
3324 * @param Port Port number used for the IN operation.
3325 * @param u32 The value to output.
3326 * @param cb The value size in bytes.
3327 */
3328PDMBOTHCBDECL(int) vbeIOPortWriteVBEExtra(PPDMDEVINS pDevIns, void *pvUser, RTIOPORT Port, uint32_t u32, unsigned cb)
3329{
3330 PVGASTATE pData = PDMINS2DATA(pDevIns, PVGASTATE);
3331 NOREF(pvUser);
3332 NOREF(Port);
3333
3334 if (cb == 2)
3335 {
3336 Log(("vbeIOPortWriteVBEExtra: addr=%#RX32\n", u32));
3337 pData->u16VBEExtraAddress = u32;
3338 return VINF_SUCCESS;
3339 }
3340
3341 Log(("vbeIOPortWriteVBEExtra: Ignoring invalid cb=%d writes to the VBE Extra port!!!\n", cb));
3342 return VINF_SUCCESS;
3343}
3344
3345
3346/**
3347 * Port I/O Handler for VBE Extra IN operations.
3348 *
3349 * @returns VBox status code.
3350 *
3351 * @param pDevIns The device instance.
3352 * @param pvUser User argument - ignored.
3353 * @param Port Port number used for the IN operation.
3354 * @param pu32 Where to store the result.
3355 * @param cb Number of bytes read.
3356 */
3357PDMBOTHCBDECL(int) vbeIOPortReadVBEExtra(PPDMDEVINS pDevIns, void *pvUser, RTIOPORT Port, uint32_t *pu32, unsigned cb)
3358{
3359 PVGASTATE pData = PDMINS2DATA(pDevIns, PVGASTATE);
3360 NOREF(pvUser);
3361 NOREF(Port);
3362
3363 if (pData->u16VBEExtraAddress == 0xffff)
3364 {
3365 Log(("vbeIOPortReadVBEExtra: Requested number of 64k video banks\n"));
3366 *pu32 = pData->vram_size / _64K;
3367 return VINF_SUCCESS;
3368 }
3369
3370 if ( pData->u16VBEExtraAddress >= pData->cbVBEExtraData
3371 || pData->u16VBEExtraAddress + cb > pData->cbVBEExtraData)
3372 {
3373 *pu32 = 0;
3374 Log(("vbeIOPortReadVBEExtra: Requested address is out of VBE data!!! Address=%#x(%d) cbVBEExtraData=%#x(%d)\n",
3375 pData->u16VBEExtraAddress, pData->u16VBEExtraAddress, pData->cbVBEExtraData, pData->cbVBEExtraData));
3376 return VINF_SUCCESS;
3377 }
3378
3379 if (cb == 1)
3380 {
3381 *pu32 = pData->pu8VBEExtraData[pData->u16VBEExtraAddress] & 0xFF;
3382
3383 Log(("vbeIOPortReadVBEExtra: cb=%#x %.*Vhxs\n", cb, cb, pu32));
3384 return VINF_SUCCESS;
3385 }
3386
3387 if (cb == 2)
3388 {
3389 *pu32 = pData->pu8VBEExtraData[pData->u16VBEExtraAddress]
3390 | pData->pu8VBEExtraData[pData->u16VBEExtraAddress + 1] << 8;
3391
3392 Log(("vbeIOPortReadVBEExtra: cb=%#x %.*Vhxs\n", cb, cb, pu32));
3393 return VINF_SUCCESS;
3394 }
3395 Log(("vbeIOPortReadVBEExtra: Invalid cb=%d read from the VBE Extra port!!!\n", cb));
3396 return VERR_IOM_IOPORT_UNUSED;
3397}
3398# endif /* VBE_NEW_DYN_LIST */
3399
3400
3401
3402
3403/* -=-=-=-=-=- Ring 3: VGA BIOS I/Os -=-=-=-=-=- */
3404
3405/**
3406 * Port I/O Handler for VGA BIOS IN operations.
3407 *
3408 * @returns VBox status code.
3409 *
3410 * @param pDevIns The device instance.
3411 * @param pvUser User argument - ignored.
3412 * @param Port Port number used for the IN operation.
3413 * @param pu32 Where to store the result.
3414 * @param cb Number of bytes read.
3415 */
3416static DECLCALLBACK(int) vgaIOPortReadBIOS(PPDMDEVINS pDevIns, void *pvUser, RTIOPORT Port, uint32_t *pu32, unsigned cb)
3417{
3418 NOREF(pDevIns);
3419 NOREF(pvUser);
3420 NOREF(Port);
3421 NOREF(pu32);
3422 NOREF(cb);
3423 return VERR_IOM_IOPORT_UNUSED;
3424}
3425
3426/**
3427 * Port I/O Handler for VGA BIOS OUT operations.
3428 *
3429 * @returns VBox status code.
3430 *
3431 * @param pDevIns The device instance.
3432 * @param pvUser User argument - ignored.
3433 * @param Port Port number used for the IN operation.
3434 * @param u32 The value to output.
3435 * @param cb The value size in bytes.
3436 */
3437static DECLCALLBACK(int) vgaIOPortWriteBIOS(PPDMDEVINS pDevIns, void *pvUser, RTIOPORT Port, uint32_t u32, unsigned cb)
3438{
3439 static int lastWasNotNewline = 0; /* We are only called in a single-threaded way */
3440 /*
3441 * VGA BIOS char printing.
3442 */
3443 if ( cb == 1
3444 && Port == VBE_PRINTF_PORT)
3445 {
3446#if 0
3447 switch (u32)
3448 {
3449 case '\r': Log(("vgabios: <return>\n")); break;
3450 case '\n': Log(("vgabios: <newline>\n")); break;
3451 case '\t': Log(("vgabios: <tab>\n")); break;
3452 default:
3453 Log(("vgabios: %c\n", u32));
3454 }
3455#else
3456 if (lastWasNotNewline == 0)
3457 Log(("vgabios: "));
3458 if (u32 != '\r') /* return - is only sent in conjunction with '\n' */
3459 Log(("%c", u32));
3460 if (u32 == '\n')
3461 lastWasNotNewline = 0;
3462 else
3463 lastWasNotNewline = 1;
3464#endif
3465 return VINF_SUCCESS;
3466 }
3467
3468 /* not in use. */
3469 return VINF_SUCCESS;
3470}
3471
3472
3473/* -=-=-=-=-=- Ring 3: IBase -=-=-=-=-=- */
3474
3475/**
3476 * Queries an interface to the driver.
3477 *
3478 * @returns Pointer to interface.
3479 * @returns NULL if the interface was not supported by the driver.
3480 * @param pInterface Pointer to this interface structure.
3481 * @param enmInterface The requested interface identification.
3482 * @thread Any thread.
3483 */
3484static DECLCALLBACK(void *) vgaPortQueryInterface(PPDMIBASE pInterface, PDMINTERFACE enmInterface)
3485{
3486 PVGASTATE pData = (PVGASTATE)((uintptr_t)pInterface - RT_OFFSETOF(VGASTATE, Base));
3487 switch (enmInterface)
3488 {
3489 case PDMINTERFACE_BASE:
3490 return &pData->Base;
3491 case PDMINTERFACE_DISPLAY_PORT:
3492 return &pData->Port;
3493 default:
3494 return NULL;
3495 }
3496}
3497
3498
3499/* -=-=-=-=-=- Ring 3: Dummy IDisplayConnector -=-=-=-=-=- */
3500
3501/**
3502 * Resize the display.
3503 * This is called when the resolution changes. This usually happens on
3504 * request from the guest os, but may also happen as the result of a reset.
3505 *
3506 * @param pInterface Pointer to this interface.
3507 * @param cx New display width.
3508 * @param cy New display height
3509 * @thread The emulation thread.
3510 */
3511static DECLCALLBACK(void) vgaDummyResize(PPDMIDISPLAYCONNECTOR pInterface, uint32_t bpp, void *pvVRAM, uint32_t cbLine, uint32_t cx, uint32_t cy)
3512{
3513}
3514
3515
3516/**
3517 * Update a rectangle of the display.
3518 * PDMIDISPLAYPORT::pfnUpdateDisplay is the caller.
3519 *
3520 * @param pInterface Pointer to this interface.
3521 * @param x The upper left corner x coordinate of the rectangle.
3522 * @param y The upper left corner y coordinate of the rectangle.
3523 * @param cx The width of the rectangle.
3524 * @param cy The height of the rectangle.
3525 * @thread The emulation thread.
3526 */
3527static DECLCALLBACK(void) vgaDummyUpdateRect(PPDMIDISPLAYCONNECTOR pInterface, uint32_t x, uint32_t y, uint32_t cx, uint32_t cy)
3528{
3529}
3530
3531
3532/**
3533 * Refresh the display.
3534 *
3535 * The interval between these calls is set by
3536 * PDMIDISPLAYPORT::pfnSetRefreshRate(). The driver should call
3537 * PDMIDISPLAYPORT::pfnUpdateDisplay() if it wishes to refresh the
3538 * display. PDMIDISPLAYPORT::pfnUpdateDisplay calls pfnUpdateRect with
3539 * the changed rectangles.
3540 *
3541 * @param pInterface Pointer to this interface.
3542 * @thread The emulation thread.
3543 */
3544static DECLCALLBACK(void) vgaDummyRefresh(PPDMIDISPLAYCONNECTOR pInterface)
3545{
3546}
3547
3548
3549/* -=-=-=-=-=- Ring 3: IDisplayPort -=-=-=-=-=- */
3550
3551/** Converts a display port interface pointer to a vga state pointer. */
3552#define IDISPLAYPORT_2_VGASTATE(pInterface) ( (PVGASTATE)((uintptr_t)pInterface - RT_OFFSETOF(VGASTATE, Port)) )
3553
3554
3555/**
3556 * Update the display with any changed regions.
3557 *
3558 * @param pInterface Pointer to this interface.
3559 * @see PDMIKEYBOARDPORT::pfnUpdateDisplay() for details.
3560 */
3561static DECLCALLBACK(void) vgaPortUpdateDisplay(PPDMIDISPLAYPORT pInterface)
3562{
3563 PVGASTATE pData = IDISPLAYPORT_2_VGASTATE(pInterface);
3564 PDMDEV_ASSERT_EMT(VGASTATE2DEVINS(pData));
3565
3566#ifdef DEBUG_sunlover
3567 LogFlow(("vgaPortUpdateDisplay\n"));
3568#endif /* DEBUG_sunlover */
3569
3570 /* This should be called only in non VBVA mode. */
3571
3572 vga_update_display(pData);
3573
3574 if (pData->fHaveDirtyBits)
3575 {
3576 PPDMDEVINS pDevIns = pData->pDevInsHC;
3577 PGMHandlerPhysicalReset(PDMDevHlpGetVM(pDevIns), pData->GCPhysVRAM);
3578 pData->fHaveDirtyBits = false;
3579 }
3580}
3581
3582
3583/**
3584 * Update the entire display.
3585 *
3586 * @param pInterface Pointer to this interface.
3587 * @see PDMIKEYBOARDPORT::pfnUpdateDisplayAll() for details.
3588 */
3589static DECLCALLBACK(void) vgaPortUpdateDisplayAll(PPDMIDISPLAYPORT pInterface)
3590{
3591 PVGASTATE pData = IDISPLAYPORT_2_VGASTATE(pInterface);
3592 PDMDEV_ASSERT_EMT(VGASTATE2DEVINS(pData));
3593
3594 /* This is called both in VBVA mode and normal modes. */
3595
3596#ifdef DEBUG_sunlover
3597 LogFlow(("vgaPortUpdateDisplayAll\n"));
3598#endif /* DEBUG_sunlover */
3599
3600 pData->graphic_mode = -1; /* force full update */
3601 vga_update_display(pData);
3602}
3603
3604
3605/**
3606 * Sets the refresh rate and restart the timer.
3607 *
3608 * @returns VBox status code.
3609 * @param pInterface Pointer to this interface.
3610 * @param cMilliesInterval Number of millies between two refreshes.
3611 * @see PDMIKEYBOARDPORT::pfnSetRefreshRate() for details.
3612 */
3613static DECLCALLBACK(int) vgaPortSetRefreshRate(PPDMIDISPLAYPORT pInterface, uint32_t cMilliesInterval)
3614{
3615 PVGASTATE pData = IDISPLAYPORT_2_VGASTATE(pInterface);
3616
3617 pData->cMilliesRefreshInterval = cMilliesInterval;
3618 if (cMilliesInterval)
3619 return TMTimerSetMillies(pData->RefreshTimer, cMilliesInterval);
3620 return TMTimerStop(pData->RefreshTimer);
3621}
3622
3623
3624/** @copydoc PDMIDISPLAYPORT::pfnQueryColorDepth */
3625static DECLCALLBACK(int) vgaPortQueryColorDepth(PPDMIDISPLAYPORT pInterface, uint32_t *pcBits)
3626{
3627 PVGASTATE pData = IDISPLAYPORT_2_VGASTATE(pInterface);
3628
3629 if (!pcBits)
3630 return VERR_INVALID_PARAMETER;
3631 *pcBits = vga_get_bpp(pData);
3632 return VINF_SUCCESS;
3633}
3634
3635
3636/**
3637 * Create a 32-bbp snapshot of the display.
3638 *
3639 * @param pInterface Pointer to this interface.
3640 * @param pvData Pointer the buffer to copy the bits to.
3641 * @param cbData Size of the buffer.
3642 * @param pcx Where to store the width of the bitmap. (optional)
3643 * @param pcy Where to store the height of the bitmap. (optional)
3644 * @param pcbData Where to store the actual size of the bitmap. (optional)
3645 * @see PDMIKEYBOARDPORT::pfnSnapshot() for details.
3646 */
3647static DECLCALLBACK(int) vgaPortSnapshot(PPDMIDISPLAYPORT pInterface, void *pvData, size_t cbData, uint32_t *pcx, uint32_t *pcy, size_t *pcbData)
3648{
3649 PPDMIDISPLAYCONNECTOR pConnector;
3650 PDMIDISPLAYCONNECTOR Connector;
3651 int32_t graphic_mode;
3652 size_t cbRequired;
3653 PVGASTATE pData = IDISPLAYPORT_2_VGASTATE(pInterface);
3654 PDMDEV_ASSERT_EMT(VGASTATE2DEVINS(pData));
3655 LogFlow(("vgaPortSnapshot: pvData=%p cbData=%d pcx=%p pcy=%p pcbData=%p\n", pvData, cbData, pcx, pcy, pcbData));
3656
3657 /*
3658 * Validate input.
3659 */
3660 if (!pvData)
3661 return VERR_INVALID_PARAMETER;
3662
3663 /*
3664 * Do a regular refresh first to resolve any pending resize issues.
3665 *
3666 * 20060317 It used to be pfnUpdateDisplay, but by VBVA design
3667 * only pfnUpdateDisplayAll is allowed to be called in VBVA mode.
3668 * Also since the goal here is to have updated display for screenshot,
3669 * the UpdateDisplayAll is even more logical to call. (sunlover)
3670 */
3671 pInterface->pfnUpdateDisplayAll(pInterface);
3672
3673 /*
3674 * Validate the buffer size.
3675 */
3676 cbRequired = RT_ALIGN_Z(pData->last_scr_width, 4) * pData->last_scr_height * 4;
3677 if (cbRequired > cbData)
3678 {
3679 Log(("vgaPortSnapshot: %d bytes are required, a buffer of %d bytes is profiled.\n", cbRequired, cbData));
3680 return VERR_BUFFER_OVERFLOW;
3681 }
3682
3683 /*
3684 * Temporarily replace the display connector interface with a fake one.
3685 */
3686 Connector.pu8Data = (uint8_t*)pvData;
3687 Connector.cBits = 32;
3688 Connector.cx = pData->pDrv->cx;
3689 Connector.cy = pData->pDrv->cy;
3690 Connector.cbScanline = RT_ALIGN_32(Connector.cx, 4) * 4;
3691 Connector.pfnRefresh = vgaDummyRefresh;
3692 Connector.pfnResize = vgaDummyResize;
3693 Connector.pfnUpdateRect = vgaDummyUpdateRect;
3694
3695 /* save & replace state data. */
3696 pConnector = pData->pDrv;
3697 pData->pDrv = &Connector;
3698 graphic_mode = pData->graphic_mode;
3699 pData->graphic_mode = -1; /* force a full refresh. */
3700
3701 /* make the snapshot. */
3702 vga_update_display(pData);
3703
3704 /* restore */
3705 pData->pDrv = pConnector;
3706 pData->graphic_mode = graphic_mode;
3707
3708 /*
3709 * Return the result.
3710 */
3711 if (pcx)
3712 *pcx = Connector.cx;
3713 if (pcy)
3714 *pcy = Connector.cy;
3715 if (pcbData)
3716 *pcbData = cbRequired;
3717 LogFlow(("vgaPortSnapshot: returns VINF_SUCCESS (cx=%d cy=%d cbData=%d)\n", Connector.cx, Connector.cy, cbRequired));
3718 return VINF_SUCCESS;
3719}
3720
3721
3722/**
3723 * Copy bitmap to the display.
3724 *
3725 * @param pInterface Pointer to this interface.
3726 * @param pvData Pointer to the bitmap bits.
3727 * @param x The upper left corner x coordinate of the destination rectangle.
3728 * @param y The upper left corner y coordinate of the destination rectangle.
3729 * @param cx The width of the source and destination rectangles.
3730 * @param cy The height of the source and destination rectangles.
3731 * @see PDMIDISPLAYPORT::pfnDisplayBlt() for details.
3732 */
3733static DECLCALLBACK(int) vgaPortDisplayBlt(PPDMIDISPLAYPORT pInterface, const void *pvData, uint32_t x, uint32_t y, uint32_t cx, uint32_t cy)
3734{
3735 PVGASTATE pData = IDISPLAYPORT_2_VGASTATE(pInterface);
3736 int rc = VINF_SUCCESS;
3737 PDMDEV_ASSERT_EMT(VGASTATE2DEVINS(pData));
3738 LogFlow(("vgaPortDisplayBlt: pvData=%p x=%d y=%d cx=%d cy=%d\n", pvData, x, y, cx, cy));
3739
3740 /*
3741 * Validate input.
3742 */
3743 if ( pvData
3744 && x < pData->pDrv->cx
3745 && cx <= pData->pDrv->cx
3746 && cx + x <= pData->pDrv->cx
3747 && y < pData->pDrv->cy
3748 && cy <= pData->pDrv->cy
3749 && cy + y <= pData->pDrv->cy)
3750 {
3751 /*
3752 * Determin bytes per pixel in the destination buffer.
3753 */
3754 size_t cbPixelDst = 0;
3755 switch (pData->pDrv->cBits)
3756 {
3757 case 8:
3758 cbPixelDst = 1;
3759 break;
3760 case 15:
3761 case 16:
3762 cbPixelDst = 2;
3763 break;
3764 case 24:
3765 cbPixelDst = 3;
3766 break;
3767 case 32:
3768 cbPixelDst = 4;
3769 break;
3770 default:
3771 rc = VERR_INVALID_PARAMETER;
3772 break;
3773 }
3774 if (VBOX_SUCCESS(rc))
3775 {
3776 /*
3777 * The blitting loop.
3778 */
3779 size_t cbLineSrc = RT_ALIGN_Z(cx, 4) * 4;
3780 uint8_t *pu8Src = (uint8_t *)pvData;
3781 size_t cbLineDst = pData->pDrv->cbScanline;
3782 uint8_t *pu8Dst = pData->pDrv->pu8Data + y * cbLineDst + x * cbPixelDst;
3783 uint32_t cyLeft = cy;
3784 vga_draw_line_func *pfnVgaDrawLine = vga_draw_line_table[VGA_DRAW_LINE32 * 4 + get_depth_index(pData->pDrv->cBits)];
3785 Assert(pfnVgaDrawLine);
3786 while (cyLeft-- > 0)
3787 {
3788 pfnVgaDrawLine(pData, pu8Dst, pu8Src, cx);
3789 pu8Dst += cbLineDst;
3790 pu8Src += cbLineSrc;
3791 }
3792
3793 /*
3794 * Invalidate the area.
3795 */
3796 pData->pDrv->pfnUpdateRect(pData->pDrv, x, y, cx, cy);
3797 }
3798 }
3799 else
3800 rc = VERR_INVALID_PARAMETER;
3801
3802 LogFlow(("vgaPortDisplayBlt: returns %Vrc\n", rc));
3803 return rc;
3804}
3805
3806static DECLCALLBACK(void) vgaPortUpdateDisplayRect (PPDMIDISPLAYPORT pInterface, int32_t x, int32_t y, uint32_t w, uint32_t h)
3807{
3808 uint32_t v;
3809 vga_draw_line_func *vga_draw_line;
3810
3811 uint32_t cbPixelDst;
3812 uint32_t cbLineDst;
3813 uint8_t *pu8Dst;
3814
3815 uint32_t cbPixelSrc;
3816 uint32_t cbLineSrc;
3817 uint8_t *pu8Src;
3818
3819 uint32_t u32OffsetSrc, u32Dummy;
3820
3821 PVGASTATE s = IDISPLAYPORT_2_VGASTATE(pInterface);
3822
3823#ifdef DEBUG_sunlover
3824 LogFlow(("vgaPortUpdateDisplayRect: %d,%d %dx%d\n", x, y, w, h));
3825#endif /* DEBUG_sunlover */
3826
3827 Assert(pInterface);
3828 Assert(s->pDrv);
3829 Assert(s->pDrv->pu8Data);
3830
3831 /* Check if there is something to do at all. */
3832 if (s->pvExtVRAMHC == s->pDrv->pu8Data)
3833 {
3834 /* The framebuffer uses the guest VRAM directly. */
3835#ifdef DEBUG_sunlover
3836 LogFlow(("vgaPortUpdateDisplayRect: nothing to do: s->pvExtVRAMHC %p, s->pDrv->pu8Data %p\n", s->pvExtVRAMHC, s->pDrv->pu8Data));
3837#endif /* DEBUG_sunlover */
3838 return;
3839 }
3840
3841 /* Correct negative x and y coordinates. */
3842 if (x < 0)
3843 {
3844 x += w; /* Compute xRight which is also the new width. */
3845 w = (x < 0) ? 0 : x;
3846 x = 0;
3847 }
3848
3849 if (y < 0)
3850 {
3851 y += h; /* Compute yBottom, which is also the new height. */
3852 h = (y < 0) ? 0 : y;
3853 y = 0;
3854 }
3855
3856 /* Also check if coords are greater than the display resolution. */
3857 if (x + w > s->pDrv->cx)
3858 {
3859#ifndef VBOX
3860 w = s->pDrv->cx > x? s->pDrv->cx - x: 0;
3861#else
3862 // x < 0 is not possible here
3863 w = s->pDrv->cx > (uint32_t)x? s->pDrv->cx - x: 0;
3864#endif
3865 }
3866
3867 if (y + h > s->pDrv->cy)
3868 {
3869#ifndef VBOX
3870 h = s->pDrv->cy > y? s->pDrv->cy - y: 0;
3871#else
3872 // y < 0 is not possible here
3873 h = s->pDrv->cy > (uint32_t)y? s->pDrv->cy - y: 0;
3874#endif
3875 }
3876
3877#ifdef DEBUG_sunlover
3878 LogFlow(("vgaPortUpdateDisplayRect: %d,%d %dx%d (corrected coords)\n", x, y, w, h));
3879#endif /* DEBUG_sunlover */
3880
3881 /* Check if there is something to do at all. */
3882 if (w == 0 || h == 0)
3883 {
3884 /* Empty rectangle. */
3885#ifdef DEBUG_sunlover
3886 LogFlow(("vgaPortUpdateDisplayRect: nothing to do: %dx%d\n", w, h));
3887#endif /* DEBUG_sunlover */
3888 return;
3889 }
3890
3891 /** @todo This method should be made universal and not only for VBVA.
3892 * VGA_DRAW_LINE* must be selected and src/dst address calculation
3893 * changed.
3894 */
3895
3896 /* Choose the rendering function. */
3897 switch(s->get_bpp(s))
3898 {
3899 default:
3900 case 0:
3901 AssertMsgFailed(("Unsupported BPP %d\n", s->get_bpp (s)));
3902 return;
3903 case 8:
3904 v = VGA_DRAW_LINE8;
3905 break;
3906 case 15:
3907 v = VGA_DRAW_LINE15;
3908 break;
3909 case 16:
3910 v = VGA_DRAW_LINE16;
3911 break;
3912 case 24:
3913 v = VGA_DRAW_LINE24;
3914 break;
3915 case 32:
3916 v = VGA_DRAW_LINE32;
3917 break;
3918 }
3919
3920 vga_draw_line = vga_draw_line_table[v * 4 + get_depth_index(s->pDrv->cBits)];
3921
3922 /* Compute source and destination addresses and pitches. */
3923 cbPixelDst = (s->pDrv->cBits + 7) / 8;
3924 cbLineDst = s->pDrv->cbScanline;
3925 pu8Dst = s->pDrv->pu8Data + y * cbLineDst + x * cbPixelDst;
3926
3927 cbPixelSrc = (s->get_bpp(s) + 7) / 8;
3928 s->get_offsets (s, &cbLineSrc, &u32OffsetSrc, &u32Dummy);
3929
3930 /* Assume that rendering is performed only on visible part of VRAM.
3931 * This is true because coordinates were verified.
3932 */
3933 pu8Src = s->pvExtVRAMHC? (uint8_t *)s->pvExtVRAMHC: s->vram_ptrHC;
3934 pu8Src += u32OffsetSrc + y * cbLineSrc + x * cbPixelSrc;
3935
3936 /* Render VRAM to framebuffer. */
3937
3938#ifdef DEBUG_sunlover
3939 LogFlow(("vgaPortUpdateDisplayRect: dst: %p, %d, %d. src: %p, %d, %d\n", pu8Dst, cbLineDst, cbPixelDst, pu8Src, cbLineSrc, cbPixelSrc));
3940#endif /* DEBUG_sunlover */
3941
3942 while (h-- > 0)
3943 {
3944 vga_draw_line (s, pu8Dst, pu8Src, w);
3945 pu8Dst += cbLineDst;
3946 pu8Src += cbLineSrc;
3947 }
3948
3949#ifdef DEBUG_sunlover
3950 LogFlow(("vgaPortUpdateDisplayRect: completed.\n"));
3951#endif /* DEBUG_sunlover */
3952}
3953
3954static DECLCALLBACK(int) vgaPortSetupVRAM (PPDMIDISPLAYPORT pInterface, void *pvBuffer, uint32_t cbBuffer)
3955{
3956 int rc = VINF_SUCCESS;
3957 PVGASTATE s = IDISPLAYPORT_2_VGASTATE(pInterface);
3958 PPDMDEVINS pDevIns;
3959 PVM pVM;
3960
3961 uint32_t u32OverlayOffset = 0;
3962
3963 LogFlow(("vgaPortSetupVRAM: pvBuffer = %p, cbBuffer = 0x%08X, s->vram_ptrHC = %p\n", pvBuffer, cbBuffer, s->vram_ptrHC));
3964
3965 /* Check input values, Main::Display rely on that. */
3966 if (ALIGNP(pvBuffer, PAGE_SIZE) != pvBuffer)
3967 {
3968 LogFlow(("vgaPortSetupVRAM: Not aligned address.\n"));
3969 return VERR_INVALID_PARAMETER;
3970 }
3971
3972 pDevIns = s->pDevInsHC;
3973 Assert(pDevIns);
3974
3975 pVM = PDMDevHlpGetVM(pDevIns);
3976 Assert(pVM);
3977
3978 if (s->pvExtVRAMHC != NULL)
3979 {
3980 if (s->pvExtVRAMHC != s->vram_ptrHC)
3981 {
3982 /* @todo */
3983 AssertMsgFailed(("Too early to call SetupVRAM with external VRAM\n"));
3984
3985 /* Remove existing overlay mapping. */
3986// MMR3PPhysUnregisterOverlay (pVM, s->GCPhysVRAM, u32OverlayOffset, s->pvExtVRAMHC, s->cbExtVRAM);
3987
3988 /* Copy the content of the external VRAM to the device VRAM. */
3989 memcpy (s->vram_ptrHC + u32OverlayOffset, s->pvExtVRAMHC, s->cbExtVRAM);
3990 }
3991
3992 s->pvExtVRAMHC = NULL;
3993 s->cbExtVRAM = 0;
3994 }
3995
3996 if (pvBuffer == NULL)
3997 {
3998 /* Resetting VRAM to use only vga device allocated memory. */
3999 Assert (cbBuffer == 0);
4000 return VINF_SUCCESS;
4001 }
4002
4003 if (pvBuffer == s->vram_ptrHC)
4004 {
4005 /* Caller will use the guest VRAM directly. Do nothing. Ignore cbBuffer. */
4006 cbBuffer = 0;
4007 rc = VINF_SUCCESS;
4008 }
4009 else
4010 {
4011 /* Register new overlay. */
4012
4013 /* @todo */
4014 AssertMsgFailed(("Too early to call SetupVRAM with external VRAM\n"));
4015
4016 cbBuffer = RT_ALIGN_32(cbBuffer, PAGE_SIZE);
4017
4018// rc = MMR3PhysRegisterOverlay(pVM, s->GCPhysVRAM, u32OverlayOffset, pvBuffer, cbBuffer, "FBVRam");
4019 }
4020
4021 if (VBOX_SUCCESS (rc))
4022 {
4023 s->pvExtVRAMHC = pvBuffer;
4024 s->cbExtVRAM = cbBuffer;
4025 }
4026
4027 return rc;
4028}
4029
4030
4031static DECLCALLBACK(void) vgaTimerRefresh(PPDMDEVINS pDevIns, PTMTIMER pTimer)
4032{
4033 PVGASTATE pData = PDMINS2DATA(pDevIns, PVGASTATE);
4034 if (pData->pDrv)
4035 pData->pDrv->pfnRefresh(pData->pDrv);
4036 if (pData->cMilliesRefreshInterval)
4037 TMTimerSetMillies(pTimer, pData->cMilliesRefreshInterval);
4038}
4039
4040
4041/* -=-=-=-=-=- Ring 3: PCI Device -=-=-=-=-=- */
4042
4043/**
4044 * Callback function for mapping an PCI I/O region.
4045 *
4046 * @return VBox status code.
4047 * @param pPciDev Pointer to PCI device. Use pPciDev->pDevIns to get the device instance.
4048 * @param iRegion The region number.
4049 * @param GCPhysAddress Physical address of the region. If iType is PCI_ADDRESS_SPACE_IO, this is an
4050 * I/O port, else it's a physical address.
4051 * This address is *NOT* relative to pci_mem_base like earlier!
4052 * @param enmType One of the PCI_ADDRESS_SPACE_* values.
4053 */
4054static DECLCALLBACK(int) vgaR3IORegionMap(PPCIDEVICE pPciDev, /*unsigned*/ int iRegion, RTGCPHYS GCPhysAddress, uint32_t cb, PCIADDRESSSPACE enmType)
4055{
4056 int rc;
4057 PVGASTATE pData = PDMINS2DATA(pPciDev->pDevIns, PVGASTATE);
4058 LogFlow(("vgaR3IORegionMap: iRegion=%d GCPhysAddress=%VGp cb=%#x enmType=%d\n", iRegion, GCPhysAddress, cb, enmType));
4059
4060 /*
4061 * VRam mapping.
4062 */
4063 if (iRegion == 0 && enmType == PCI_ADDRESS_SPACE_MEM_PREFETCH)
4064 {
4065 /*
4066 * Register and lock the VRAM.
4067 *
4068 * Windows usually re-initializes the PCI devices, so we have to check whether the memory was
4069 * already registered before trying to do that all over again.
4070 */
4071 PVM pVM = PDMDevHlpGetVM(pPciDev->pDevIns);
4072 if (pData->GCPhysVRAM)
4073 {
4074 AssertMsg(pData->GCPhysVRAM == GCPhysAddress,
4075 ("The Guest OS relocated our LFB! old GCPhysVRAM=%VGp new GCPhysAddress=%VGp\n",
4076 pData->GCPhysVRAM, GCPhysAddress));
4077 rc = VINF_SUCCESS;
4078 }
4079 else
4080 {
4081 /*
4082 * Register and lock the VRAM.
4083 */
4084 rc = MMR3PhysRegister(pVM, pData->vram_ptrHC, GCPhysAddress, pData->vram_size, MM_RAM_FLAGS_MMIO2, "VRam");
4085 if (VBOX_SUCCESS(rc))
4086 {
4087 if (!pData->GCPhysVRAM)
4088 rc = PGMR3HandlerPhysicalRegister(pVM, PGMPHYSHANDLERTYPE_PHYSICAL_WRITE,
4089 GCPhysAddress, GCPhysAddress + (pData->vram_size - 1),
4090 vgaR3LFBAccessHandler, pData,
4091 g_DeviceVga.szR0Mod, "vgaR0LFBAccessHandler", pData->pDevInsHC->pvInstanceDataHC,
4092 g_DeviceVga.szGCMod, "vgaGCLFBAccessHandler", pData->pDevInsHC->pvInstanceDataGC,
4093 "VGA LFB");
4094 if (VBOX_SUCCESS(rc))
4095 {
4096 /*
4097 * Map the first 256KB of the VRAM into GC for GC VGA support.
4098 */
4099 RTGCPTR GCPtr;
4100 rc = MMR3HyperMapGCPhys(pVM, GCPhysAddress, VGA_MAPPING_SIZE, "VGA VRam", &GCPtr);
4101 if (VBOX_SUCCESS(rc))
4102 {
4103 MMR3HyperReserve(pVM, PAGE_SIZE, "fence", NULL);
4104
4105 pData->vram_ptrGC = GCPtr;
4106 pData->GCPhysVRAM = GCPhysAddress;
4107 return VINF_SUCCESS;
4108 }
4109 AssertMsgFailed(("MMR3HyperMapGCPhys failed, rc=%Vrc\n", rc));
4110 }
4111 else
4112 AssertMsgFailed(("Failed to register write handler for VRAM! rc=%Vrc\n", rc));
4113 }
4114 else
4115 AssertReleaseMsgFailed(("Failed to register VRAM! rc=%Vra\n", rc));
4116 }
4117 return rc;
4118 }
4119 else
4120 AssertReleaseMsgFailed(("Huh!?! iRegion=%d enmType=%d\n", iRegion, enmType));
4121 return VERR_INTERNAL_ERROR;
4122}
4123
4124
4125/* -=-=-=-=-=- Ring3: Misc Wrappers -=-=-=-=-=- */
4126
4127/**
4128 * Saves a state of the VGA device.
4129 *
4130 * @returns VBox status code.
4131 * @param pDevIns The device instance.
4132 * @param pSSMHandle The handle to save the state to.
4133 */
4134static DECLCALLBACK(int) vgaR3SaveExec(PPDMDEVINS pDevIns, PSSMHANDLE pSSMHandle)
4135{
4136 vga_save(pSSMHandle, PDMINS2DATA(pDevIns, PVGASTATE));
4137 return VINF_SUCCESS;
4138}
4139
4140
4141/**
4142 * Loads a saved VGA device state.
4143 *
4144 * @returns VBox status code.
4145 * @param pDevIns The device instance.
4146 * @param pSSMHandle The handle to the saved state.
4147 * @param u32Version The data unit version number.
4148 */
4149static DECLCALLBACK(int) vgaR3LoadExec(PPDMDEVINS pDevIns, PSSMHANDLE pSSMHandle, uint32_t u32Version)
4150{
4151 if (vga_load(pSSMHandle, PDMINS2DATA(pDevIns, PVGASTATE), u32Version))
4152 return VERR_SSM_UNSUPPORTED_DATA_UNIT_VERSION;
4153 return VINF_SUCCESS;
4154}
4155
4156
4157/* -=-=-=-=-=- Ring 3: Device callbacks -=-=-=-=-=- */
4158
4159/**
4160 * Reset notification.
4161 *
4162 * @returns VBox status.
4163 * @param pDevIns The device instance data.
4164 */
4165static DECLCALLBACK(void) vgaR3Reset(PPDMDEVINS pDevIns)
4166{
4167 PVGASTATE pData = PDMINS2DATA(pDevIns, PVGASTATE);
4168 char *pchStart;
4169 char *pchEnd;
4170 LogFlow(("vgaReset\n"));
4171
4172 /* Clear the VRAM ourselves. */
4173 if (pData->vram_ptrHC && pData->vram_size)
4174 {
4175#ifdef LOG_ENABLED /** @todo separate function. */
4176 /* First dump the textmode contents to the log; handy for capturing Windows blue screens. */
4177 uint8_t graphic_mode;
4178 VGAState *s = pData;
4179
4180 if (!(s->ar_index & 0x20)) {
4181 graphic_mode = GMODE_BLANK;
4182 } else {
4183 graphic_mode = s->gr[6] & 1;
4184 }
4185 switch(graphic_mode)
4186 case GMODE_TEXT:
4187 {
4188 int cw, height, width, cheight, cx_min, cx_max, cy, cx;
4189 int x_incr;
4190 uint8_t *s1, *src, ch, cattr;
4191 int line_offset;
4192 uint16_t ch_attr;
4193
4194 line_offset = s->line_offset;
4195 s1 = s->CTXSUFF(vram_ptr) + (s->start_addr * 4);
4196
4197 /* total width & height */
4198 cheight = (s->cr[9] & 0x1f) + 1;
4199 cw = 8;
4200 if (!(s->sr[1] & 0x01))
4201 cw = 9;
4202 if (s->sr[1] & 0x08)
4203 cw = 16; /* NOTE: no 18 pixel wide */
4204 x_incr = cw * ((s->pDrv->cBits + 7) >> 3);
4205 width = (s->cr[0x01] + 1);
4206 if (s->cr[0x06] == 100) {
4207 /* ugly hack for CGA 160x100x16 - explain me the logic */
4208 height = 100;
4209 } else {
4210 height = s->cr[0x12] |
4211 ((s->cr[0x07] & 0x02) << 7) |
4212 ((s->cr[0x07] & 0x40) << 3);
4213 height = (height + 1) / cheight;
4214 }
4215 if ((height * width) > CH_ATTR_SIZE) {
4216 /* better than nothing: exit if transient size is too big */
4217 break;
4218 }
4219 RTLogPrintf("VGA textmode BEGIN (%dx%d):\n\n", height, width);
4220 for(cy = 0; cy < height; cy++) {
4221 src = s1;
4222 cx_min = width;
4223 cx_max = -1;
4224 for(cx = 0; cx < width; cx++) {
4225 ch_attr = *(uint16_t *)src;
4226 if (cx < cx_min)
4227 cx_min = cx;
4228 if (cx > cx_max)
4229 cx_max = cx;
4230# ifdef WORDS_BIGENDIAN
4231 ch = ch_attr >> 8;
4232 cattr = ch_attr & 0xff;
4233# else
4234 ch = ch_attr & 0xff;
4235 cattr = ch_attr >> 8;
4236# endif
4237 RTLogPrintf("%c", ch);
4238
4239 src += 4;
4240 }
4241 if (cx_max != -1)
4242 RTLogPrintf("\n");
4243
4244 s1 += line_offset;
4245 }
4246 RTLogPrintf("VGA textmode END:\n\n");
4247 }
4248
4249#endif
4250 memset(pData->vram_ptrHC, 0, pData->vram_size);
4251 }
4252
4253 /*
4254 * Zero most of it.
4255 *
4256 * Unlike vga_reset we're leaving out a few members which believe must
4257 * remain unchanged....
4258 */
4259 /* 1st part. */
4260 pchStart = (char *)&pData->latch;
4261 pchEnd = (char *)&pData->invalidated_y_table;
4262 memset(pchStart, 0, pchEnd - pchStart);
4263
4264 /* 2nd part. */
4265 pchStart = (char *)&pData->last_palette;
4266 pchEnd = (char *)&pData->u32Marker;
4267 memset(pchStart, 0, pchEnd - pchStart);
4268
4269
4270 /*
4271 * Restore and re-init some bits.
4272 */
4273 pData->get_bpp = vga_get_bpp;
4274 pData->get_offsets = vga_get_offsets;
4275 pData->get_resolution = vga_get_resolution;
4276 pData->graphic_mode = -1; /* Force full update. */
4277#ifdef CONFIG_BOCHS_VBE
4278 pData->vbe_regs[VBE_DISPI_INDEX_ID] = VBE_DISPI_ID0;
4279 pData->vbe_bank_mask = ((pData->vram_size >> 16) - 1);
4280#endif /* CONFIG_BOCHS_VBE */
4281
4282 /*
4283 * Reset the LBF mapping.
4284 */
4285 pData->fLFBUpdated = false;
4286 if ( ( pData->fGCEnabled
4287 || pData->fR0Enabled)
4288 && pData->GCPhysVRAM)
4289 {
4290 int rc = PGMHandlerPhysicalReset(PDMDevHlpGetVM(pDevIns), pData->GCPhysVRAM);
4291 AssertRC(rc);
4292 }
4293
4294 /* notify port handler */
4295 if (pData->pDrv)
4296 pData->pDrv->pfnReset(pData->pDrv);
4297}
4298
4299
4300/**
4301 * Device relocation callback.
4302 *
4303 * @param pDevIns Pointer to the device instance.
4304 * @param offDelta The relocation delta relative to the old location.
4305 *
4306 * @see FNPDMDEVRELOCATE for details.
4307 */
4308static DECLCALLBACK(void) vgaR3Relocate(PPDMDEVINS pDevIns, RTGCINTPTR offDelta)
4309{
4310 if (offDelta)
4311 {
4312 PVGASTATE pData = PDMINS2DATA(pDevIns, PVGASTATE);
4313 LogFlow(("vgaRelocate: offDelta = %08X\n", offDelta));
4314
4315 pData->GCPtrLFBHandler += offDelta;
4316 pData->vram_ptrGC += offDelta;
4317 }
4318}
4319
4320
4321/**
4322 * Attach command.
4323 *
4324 * This is called to let the device attach to a driver for a specified LUN
4325 * during runtime. This is not called during VM construction, the device
4326 * constructor have to attach to all the available drivers.
4327 *
4328 * This is like plugging in the monitor after turning on the PC.
4329 *
4330 * @returns VBox status code.
4331 * @param pDevIns The device instance.
4332 * @param iLUN The logical unit which is being detached.
4333 */
4334static DECLCALLBACK(int) vgaAttach(PPDMDEVINS pDevIns, unsigned iLUN)
4335{
4336 PVGASTATE pData = PDMINS2DATA(pDevIns, PVGASTATE);
4337 switch (iLUN)
4338 {
4339 /* LUN #0: Display port. */
4340 case 0:
4341 {
4342 int rc = PDMDevHlpDriverAttach(pDevIns, iLUN, &pData->Base, &pData->pDrvBase, "Display Port");
4343 if (VBOX_SUCCESS(rc))
4344 {
4345 pData->pDrv = (PDMIDISPLAYCONNECTOR*)pData->pDrvBase->pfnQueryInterface(pData->pDrvBase, PDMINTERFACE_DISPLAY_CONNECTOR);
4346 if (pData->pDrv)
4347 {
4348 if ( pData->pDrv->pu8Data
4349 && pData->pDrv->pfnRefresh
4350 && pData->pDrv->pfnResize
4351 && pData->pDrv->pfnUpdateRect)
4352 rc = VINF_SUCCESS;
4353 else
4354 {
4355 Assert(pData->pDrv->pu8Data);
4356 Assert(pData->pDrv->pfnRefresh);
4357 Assert(pData->pDrv->pfnResize);
4358 Assert(pData->pDrv->pfnUpdateRect);
4359 pData->pDrv = NULL;
4360 pData->pDrvBase = NULL;
4361 rc = VERR_INTERNAL_ERROR;
4362 }
4363 }
4364 else
4365 {
4366 AssertMsgFailed(("LUN #0 doesn't have a display connector interface! rc=%Vrc\n", rc));
4367 pData->pDrvBase = NULL;
4368 rc = VERR_PDM_MISSING_INTERFACE;
4369 }
4370 }
4371 else if (rc == VERR_PDM_NO_ATTACHED_DRIVER)
4372 {
4373 Log(("%s/%d: warning: no driver attached to LUN #0!\n", pDevIns->pDevReg->szDeviceName, pDevIns->iInstance));
4374 rc = VINF_SUCCESS;
4375 }
4376 else
4377 AssertMsgFailed(("Failed to attach LUN #0! rc=%Vrc\n", rc));
4378 return rc;
4379 }
4380
4381 default:
4382 AssertMsgFailed(("Invalid LUN #%d\n", iLUN));
4383 return VERR_PDM_NO_SUCH_LUN;
4384 }
4385}
4386
4387
4388/**
4389 * Detach notification.
4390 *
4391 * This is called when a driver is detaching itself from a LUN of the device.
4392 * The device should adjust it's state to reflect this.
4393 *
4394 * This is like unplugging the monitor while the PC is still running.
4395 *
4396 * @param pDevIns The device instance.
4397 * @param iLUN The logical unit which is being detached.
4398 */
4399static DECLCALLBACK(void) vgaDetach(PPDMDEVINS pDevIns, unsigned iLUN)
4400{
4401 /*
4402 * Reset the interfaces and update the controller state.
4403 */
4404 PVGASTATE pData = PDMINS2DATA(pDevIns, PVGASTATE);
4405 switch (iLUN)
4406 {
4407 /* LUN #0: Display port. */
4408 case 0:
4409 pData->pDrv = NULL;
4410 pData->pDrvBase = NULL;
4411 break;
4412
4413 default:
4414 AssertMsgFailed(("Invalid LUN #%d\n", iLUN));
4415 break;
4416 }
4417}
4418
4419
4420
4421/**
4422 * Construct a VGA device instance for a VM.
4423 *
4424 * @returns VBox status.
4425 * @param pDevIns The device instance data.
4426 * If the registration structure is needed, pDevIns->pDevReg points to it.
4427 * @param iInstance Instance number. Use this to figure out which registers and such to use.
4428 * The device number is also found in pDevIns->iInstance, but since it's
4429 * likely to be freqently used PDM passes it as parameter.
4430 * @param pCfgHandle Configuration node handle for the device. Use this to obtain the configuration
4431 * of the device instance. It's also found in pDevIns->pCfgHandle, but like
4432 * iInstance it's expected to be used a bit in this function.
4433 */
4434static DECLCALLBACK(int) vgaR3Construct(PPDMDEVINS pDevIns, int iInstance, PCFGMNODE pCfgHandle)
4435{
4436 static bool fExpandDone = false;
4437 bool f;
4438 int rc;
4439 unsigned i;
4440 PVGASTATE pData = PDMINS2DATA(pDevIns, PVGASTATE);
4441 PVM pVM = PDMDevHlpGetVM(pDevIns);
4442#ifdef VBE_NEW_DYN_LIST
4443 uint32_t cCustomModes;
4444 uint32_t cyReduction;
4445 PVBEHEADER pVBEDataHdr;
4446 ModeInfoListItem *pCurMode;
4447 unsigned cb;
4448#endif
4449 Assert(iInstance == 0);
4450 Assert(pVM);
4451
4452 /*
4453 * Init static data.
4454 */
4455 if (!fExpandDone)
4456 {
4457 fExpandDone = true;
4458 vga_init_expand();
4459 }
4460
4461 /*
4462 * Validate configuration.
4463 */
4464 if (!CFGMR3AreValuesValid(pCfgHandle, "VRamSize\0"
4465 "GCEnabled\0"
4466 "R0Enabled\0"
4467 "CustomVideoModes\0"
4468 "HeightReduction\0"
4469 "CustomVideoMode1\0"
4470 "CustomVideoMode2\0"
4471 "CustomVideoMode3\0"
4472 "CustomVideoMode4\0"
4473 "CustomVideoMode5\0"
4474 "CustomVideoMode6\0"
4475 "CustomVideoMode7\0"
4476 "CustomVideoMode8\0"
4477 "CustomVideoMode9\0"
4478 "CustomVideoMode10\0"
4479 "CustomVideoMode11\0"
4480 "CustomVideoMode12\0"
4481 "CustomVideoMode13\0"
4482 "CustomVideoMode14\0"
4483 "CustomVideoMode15\0"
4484 "CustomVideoMode16"))
4485 return PDMDEV_SET_ERROR(pDevIns, VERR_PDM_DEVINS_UNKNOWN_CFG_VALUES,
4486 N_("Invalid configuration for vga device"));
4487
4488 /*
4489 * Init state data.
4490 */
4491 rc = CFGMR3QueryU32(pCfgHandle, "VRamSize", &pData->vram_size);
4492 if (VBOX_FAILURE(rc) || !pData->vram_size)
4493 pData->vram_size = VGA_VRAM_DEFAULT;
4494 else if (pData->vram_size > VGA_VRAM_MAX)
4495 {
4496 AssertMsgFailed(("vram_size=%d max=%d\n", pData->vram_size, VGA_VRAM_MAX));
4497 pData->vram_size = VGA_VRAM_MAX;
4498 }
4499 else if (pData->vram_size < VGA_VRAM_MIN)
4500 {
4501 AssertMsgFailed(("vram_size=%d min=%d\n", pData->vram_size, VGA_VRAM_MIN));
4502 pData->vram_size = RT_ALIGN_32(pData->vram_size, _1M);
4503 }
4504 Log(("VGA: VRamSize=%#x\n", pData->vram_size));
4505
4506 pData->fGCEnabled = true;
4507 rc = CFGMR3QueryBool(pCfgHandle, "GCEnabled", &f);
4508 if (VBOX_SUCCESS(rc) && !f)
4509 pData->fGCEnabled = false;
4510 Log(("VGA: fGCEnabled=%d\n", pData->fGCEnabled));
4511
4512 pData->fR0Enabled = true;
4513 rc = CFGMR3QueryBool(pCfgHandle, "R0Enabled", &f);
4514 if (VBOX_SUCCESS(rc) && !f)
4515 pData->fR0Enabled = false;
4516 Log(("VGA: fR0Enabled=%d\n", pData->fR0Enabled));
4517
4518 pData->pDevInsHC = pDevIns;
4519
4520 vgaR3Reset(pDevIns);
4521
4522 /* The PCI devices configuration. */
4523 pData->Dev.config[0x00] = 0xee; /* PCI vendor, just a free bogus value */
4524 pData->Dev.config[0x01] = 0x80;
4525
4526 pData->Dev.config[0x02] = 0xef; /* Device ID */
4527 pData->Dev.config[0x03] = 0xbe;
4528
4529 pData->Dev.config[0x0a] = 0x00; /* VGA controller */
4530 pData->Dev.config[0x0b] = 0x03;
4531 pData->Dev.config[0x0e] = 0x00; /* header_type */
4532
4533 /* The LBF access handler - error handling is better here than in the map function. */
4534 rc = PDMR3GetSymbolGCLazy(pVM, pDevIns->pDevReg->szGCMod, "vgaGCLFBAccessHandler", &pData->GCPtrLFBHandler);
4535 if (VBOX_FAILURE(rc))
4536 {
4537 AssertReleaseMsgFailed(("PDMR3GetSymbolGC(, %s, \"vgaGCLFBAccessHandler\",) -> %Vrc\n", pDevIns->pDevReg->szGCMod, rc));
4538 return rc;
4539 }
4540
4541 /* the interfaces. */
4542 pData->Base.pfnQueryInterface = vgaPortQueryInterface;
4543
4544 pData->Port.pfnUpdateDisplay = vgaPortUpdateDisplay;
4545 pData->Port.pfnUpdateDisplayAll = vgaPortUpdateDisplayAll;
4546 pData->Port.pfnQueryColorDepth = vgaPortQueryColorDepth;
4547 pData->Port.pfnSetRefreshRate = vgaPortSetRefreshRate;
4548 pData->Port.pfnSnapshot = vgaPortSnapshot;
4549 pData->Port.pfnDisplayBlt = vgaPortDisplayBlt;
4550 pData->Port.pfnUpdateDisplayRect= vgaPortUpdateDisplayRect;
4551 pData->Port.pfnSetupVRAM = vgaPortSetupVRAM;
4552
4553
4554 /*
4555 * Register I/O ports, ROM and save state.
4556 */
4557 rc = PDMDevHlpIOPortRegister(pDevIns, 0x3c0, 16, NULL, vgaIOPortWrite, vgaIOPortRead, NULL, NULL, "VGA - 3c0");
4558 if (VBOX_FAILURE(rc))
4559 return rc;
4560 rc = PDMDevHlpIOPortRegister(pDevIns, 0x3b4, 2, NULL, vgaIOPortWrite, vgaIOPortRead, NULL, NULL, "VGA - 3b4");
4561 if (VBOX_FAILURE(rc))
4562 return rc;
4563 rc = PDMDevHlpIOPortRegister(pDevIns, 0x3ba, 1, NULL, vgaIOPortWrite, vgaIOPortRead, NULL, NULL, "VGA - 3ba");
4564 if (VBOX_FAILURE(rc))
4565 return rc;
4566 rc = PDMDevHlpIOPortRegister(pDevIns, 0x3d4, 2, NULL, vgaIOPortWrite, vgaIOPortRead, NULL, NULL, "VGA - 3d4");
4567 if (VBOX_FAILURE(rc))
4568 return rc;
4569 rc = PDMDevHlpIOPortRegister(pDevIns, 0x3da, 1, NULL, vgaIOPortWrite, vgaIOPortRead, NULL, NULL, "VGA - 3da");
4570 if (VBOX_FAILURE(rc))
4571 return rc;
4572
4573#ifdef CONFIG_BOCHS_VBE
4574 rc = PDMDevHlpIOPortRegister(pDevIns, 0x1ce, 1, NULL, vgaIOPortWriteVBEIndex, vgaIOPortReadVBEIndex, NULL, NULL, "VGA/VBE - Index");
4575 if (VBOX_FAILURE(rc))
4576 return rc;
4577 rc = PDMDevHlpIOPortRegister(pDevIns, 0x1cf, 1, NULL, vgaIOPortWriteVBEData, vgaIOPortReadVBEData, NULL, NULL, "VGA/VBE - Data");
4578 if (VBOX_FAILURE(rc))
4579 return rc;
4580#if 0
4581 /* This now causes conflicts with Win2k & XP; it is not aware this range is taken
4582 and tries to map other devices there */
4583 /* Old Bochs. */
4584 rc = PDMDevHlpIOPortRegister(pDevIns, 0xff80, 1, NULL, vgaIOPortWriteVBEIndex, vgaIOPortReadVBEIndex, "VGA/VBE - Index Old");
4585 if (VBOX_FAILURE(rc))
4586 return rc;
4587 rc = PDMDevHlpIOPortRegister(pDevIns, 0xff81, 1, NULL, vgaIOPortWriteVBEData, vgaIOPortReadVBEData, "VGA/VBE - Data Old");
4588 if (VBOX_FAILURE(rc))
4589 return rc;
4590#endif
4591#endif /* CONFIG_BOCHS_VBE */
4592
4593 /* guest context extension */
4594 if (pData->fGCEnabled)
4595 {
4596 rc = PDMDevHlpIOPortRegisterGC(pDevIns, 0x3c0, 16, 0, "vgaIOPortWrite", "vgaIOPortRead", NULL, NULL, "VGA - 3c0 (GC)");
4597 if (VBOX_FAILURE(rc))
4598 return rc;
4599 rc = PDMDevHlpIOPortRegisterGC(pDevIns, 0x3b4, 2, 0, "vgaIOPortWrite", "vgaIOPortRead", NULL, NULL, "VGA - 3b4 (GC)");
4600 if (VBOX_FAILURE(rc))
4601 return rc;
4602 rc = PDMDevHlpIOPortRegisterGC(pDevIns, 0x3ba, 1, 0, "vgaIOPortWrite", "vgaIOPortRead", NULL, NULL, "VGA - 3ba (GC)");
4603 if (VBOX_FAILURE(rc))
4604 return rc;
4605 rc = PDMDevHlpIOPortRegisterGC(pDevIns, 0x3d4, 2, 0, "vgaIOPortWrite", "vgaIOPortRead", NULL, NULL, "VGA - 3d4 (GC)");
4606 if (VBOX_FAILURE(rc))
4607 return rc;
4608 rc = PDMDevHlpIOPortRegisterGC(pDevIns, 0x3da, 1, 0, "vgaIOPortWrite", "vgaIOPortRead", NULL, NULL, "VGA - 3da (GC)");
4609 if (VBOX_FAILURE(rc))
4610 return rc;
4611#ifdef CONFIG_BOCHS_VBE
4612 rc = PDMDevHlpIOPortRegisterGC(pDevIns, 0x1ce, 1, 0, "vgaIOPortWriteVBEIndex", "vgaIOPortReadVBEIndex", NULL, NULL, "VGA/VBE - Index (GC)");
4613 if (VBOX_FAILURE(rc))
4614 return rc;
4615 rc = PDMDevHlpIOPortRegisterGC(pDevIns, 0x1cf, 1, 0, "vgaIOPortWriteVBEData", "vgaIOPortReadVBEData", NULL, NULL, "VGA/VBE - Data (GC)");
4616 if (VBOX_FAILURE(rc))
4617 return rc;
4618
4619#if 0
4620 /* This now causes conflicts with Win2k & XP; it is not aware this range is taken
4621 and tries to map other devices there */
4622 /* Old Bochs. */
4623 rc = PDMDevHlpIOPortRegisterGC(pDevIns, 0xff80, 1, 0, "vgaIOPortWriteVBEIndex", "vgaIOPortReadVBEIndex", "VGA/VBE - Index Old (GC)");
4624 if (VBOX_FAILURE(rc))
4625 return rc;
4626 rc = PDMDevHlpIOPortRegisterGC(pDevIns, 0xff81, 1, 0, "vgaIOPortWriteVBEData", "vgaIOPortReadVBEData", "VGA/VBE - Index Old (GC)");
4627 if (VBOX_FAILURE(rc))
4628 return rc;
4629#endif
4630
4631#endif /* CONFIG_BOCHS_VBE */
4632 }
4633
4634 /* R0 context extension */
4635 if (pData->fR0Enabled)
4636 {
4637 rc = PDMDevHlpIOPortRegisterR0(pDevIns, 0x3c0, 16, 0, "vgaIOPortWrite", "vgaIOPortRead", NULL, NULL, "VGA - 3c0 (GC)");
4638 if (VBOX_FAILURE(rc))
4639 return rc;
4640 rc = PDMDevHlpIOPortRegisterR0(pDevIns, 0x3b4, 2, 0, "vgaIOPortWrite", "vgaIOPortRead", NULL, NULL, "VGA - 3b4 (GC)");
4641 if (VBOX_FAILURE(rc))
4642 return rc;
4643 rc = PDMDevHlpIOPortRegisterR0(pDevIns, 0x3ba, 1, 0, "vgaIOPortWrite", "vgaIOPortRead", NULL, NULL, "VGA - 3ba (GC)");
4644 if (VBOX_FAILURE(rc))
4645 return rc;
4646 rc = PDMDevHlpIOPortRegisterR0(pDevIns, 0x3d4, 2, 0, "vgaIOPortWrite", "vgaIOPortRead", NULL, NULL, "VGA - 3d4 (GC)");
4647 if (VBOX_FAILURE(rc))
4648 return rc;
4649 rc = PDMDevHlpIOPortRegisterR0(pDevIns, 0x3da, 1, 0, "vgaIOPortWrite", "vgaIOPortRead", NULL, NULL, "VGA - 3da (GC)");
4650 if (VBOX_FAILURE(rc))
4651 return rc;
4652#ifdef CONFIG_BOCHS_VBE
4653 rc = PDMDevHlpIOPortRegisterR0(pDevIns, 0x1ce, 1, 0, "vgaIOPortWriteVBEIndex", "vgaIOPortReadVBEIndex", NULL, NULL, "VGA/VBE - Index (GC)");
4654 if (VBOX_FAILURE(rc))
4655 return rc;
4656 rc = PDMDevHlpIOPortRegisterR0(pDevIns, 0x1cf, 1, 0, "vgaIOPortWriteVBEData", "vgaIOPortReadVBEData", NULL, NULL, "VGA/VBE - Data (GC)");
4657 if (VBOX_FAILURE(rc))
4658 return rc;
4659
4660#if 0
4661 /* This now causes conflicts with Win2k & XP; it is not aware this range is taken
4662 and tries to map other devices there */
4663 /* Old Bochs. */
4664 rc = PDMDevHlpIOPortRegisterR0(pDevIns, 0xff80, 1, 0, "vgaIOPortWriteVBEIndex", "vgaIOPortReadVBEIndex", "VGA/VBE - Index Old (GC)");
4665 if (VBOX_FAILURE(rc))
4666 return rc;
4667 rc = PDMDevHlpIOPortRegisterR0(pDevIns, 0xff81, 1, 0, "vgaIOPortWriteVBEData", "vgaIOPortReadVBEData", "VGA/VBE - Index Old (GC)");
4668 if (VBOX_FAILURE(rc))
4669 return rc;
4670#endif
4671
4672#endif /* CONFIG_BOCHS_VBE */
4673 }
4674
4675 /* vga mmio */
4676 rc = PDMDevHlpMMIORegister(pDevIns, 0x000a0000, 0x00020000, 0, vgaMMIOWrite, vgaMMIORead, vgaMMIOFill, "VGA - VGA Video Buffer");
4677 if (VBOX_FAILURE(rc))
4678 return rc;
4679 if (pData->fGCEnabled)
4680 {
4681 rc = PDMDevHlpMMIORegisterGC(pDevIns, 0x000a0000, 0x00020000, 0, "vgaMMIOWrite", "vgaMMIORead", "vgaMMIOFill", "VGA - VGA Video Buffer");
4682 if (VBOX_FAILURE(rc))
4683 return rc;
4684 }
4685 if (pData->fR0Enabled)
4686 {
4687 rc = PDMDevHlpMMIORegisterR0(pDevIns, 0x000a0000, 0x00020000, 0, "vgaMMIOWrite", "vgaMMIORead", "vgaMMIOFill", "VGA - VGA Video Buffer");
4688 if (VBOX_FAILURE(rc))
4689 return rc;
4690 }
4691
4692 /* vga bios */
4693 rc = PDMDevHlpIOPortRegister(pDevIns, VBE_PRINTF_PORT, 1, NULL, vgaIOPortWriteBIOS, vgaIOPortReadBIOS, NULL, NULL, "VGA BIOS debug/panic");
4694 if (VBOX_FAILURE(rc))
4695 return rc;
4696 AssertReleaseMsg(g_cbVgaBiosBinary <= _64K && g_cbVgaBiosBinary >= 32*_1K, ("g_cbVgaBiosBinary=%#x\n", g_cbVgaBiosBinary));
4697 AssertReleaseMsg(RT_ALIGN_Z(g_cbVgaBiosBinary, PAGE_SIZE) == g_cbVgaBiosBinary, ("g_cbVgaBiosBinary=%#x\n", g_cbVgaBiosBinary));
4698 rc = PDMDevHlpROMRegister(pDevIns, 0x000c0000, g_cbVgaBiosBinary, &g_abVgaBiosBinary[0], "VGA BIOS");
4699 if (VBOX_FAILURE(rc))
4700 return rc;
4701
4702 /* save */
4703 rc = PDMDevHlpSSMRegister(pDevIns, pDevIns->pDevReg->szDeviceName, iInstance, 1 /* version */, sizeof(*pData),
4704 NULL, vgaR3SaveExec, NULL,
4705 NULL, vgaR3LoadExec, NULL);
4706 if (VBOX_FAILURE(rc))
4707 return rc;
4708
4709 /* PCI */
4710 rc = PDMDevHlpPCIRegister(pDevIns, &pData->Dev);
4711 if (VBOX_FAILURE(rc))
4712 return rc;
4713 /*AssertMsg(pData->Dev.devfn == 16 || iInstance != 0, ("pData->Dev.devfn=%d\n", pData->Dev.devfn));*/
4714 if (pData->Dev.devfn != 16 && iInstance == 0)
4715 Log(("!!WARNING!!: pData->dev.devfn=%d (ignore if testcase or no started by Main)\n", pData->Dev.devfn));
4716 rc = PDMDevHlpPCIIORegionRegister(pDevIns, 0, pData->vram_size, PCI_ADDRESS_SPACE_MEM_PREFETCH, vgaR3IORegionMap);
4717 if (VBOX_FAILURE(rc))
4718 return rc;
4719
4720 /*
4721 * Create the refresh timer.
4722 */
4723 rc = PDMDevHlpTMTimerCreate(pDevIns, TMCLOCK_REAL, vgaTimerRefresh, "VGA Refresh Timer", &pData->RefreshTimer);
4724 if (VBOX_FAILURE(rc))
4725 return rc;
4726
4727 /*
4728 * Attach to the display.
4729 */
4730 rc = vgaAttach(pDevIns, 0 /* display LUN # */);
4731 if (VBOX_FAILURE(rc))
4732 return rc;
4733
4734 /*
4735 * Allocate the VRAM.
4736 */
4737 /** @todo freeing of the VRAM. */
4738 rc = SUPPageAlloc(pData->vram_size >> PAGE_SHIFT, (void **)&pData->vram_ptrHC);
4739 if (VBOX_FAILURE(rc))
4740 {
4741 AssertMsgFailed(("SUPPageAlloc(%#x,) -> %d\n", pData->vram_size, rc));
4742 return rc;
4743 }
4744
4745#ifdef VBE_NEW_DYN_LIST
4746 /*
4747 * Compute buffer size for the VBE BIOS Extra Data.
4748 */
4749 cb = sizeof(mode_info_list) + sizeof(ModeInfoListItem);
4750
4751 rc = CFGMR3QueryU32(pCfgHandle, "HeightReduction", &cyReduction);
4752 if (VBOX_SUCCESS(rc) && cyReduction)
4753 cb *= 2; /* Default mode list will be twice long */
4754 else
4755 cyReduction = 0;
4756
4757 rc = CFGMR3QueryU32(pCfgHandle, "CustomVideoModes", &cCustomModes);
4758 if (VBOX_SUCCESS(rc) && cCustomModes)
4759 cb += sizeof(ModeInfoListItem) * cCustomModes;
4760 else
4761 cCustomModes = 0;
4762
4763 /*
4764 * Allocate and initialize buffer for the VBE BIOS Extra Data.
4765 */
4766 pData->cbVBEExtraData = sizeof(VBEHEADER) + cb;
4767 pData->pu8VBEExtraData = (uint8_t *)PDMDevHlpMMHeapAllocZ(pDevIns, pData->cbVBEExtraData);
4768 if (!pData->pu8VBEExtraData)
4769 return VERR_NO_MEMORY;
4770
4771 pVBEDataHdr = (PVBEHEADER)pData->pu8VBEExtraData;
4772 pVBEDataHdr->u16Signature = VBEHEADER_MAGIC;
4773 pVBEDataHdr->cbData = cb;
4774
4775#ifndef VRAM_SIZE_FIX
4776 pCurMode = memcpy(pVBEDataHdr + 1, &mode_info_list, sizeof(mode_info_list));
4777 pCurMode = (ModeInfoListItem *)((uintptr_t)pCurMode + sizeof(mode_info_list));
4778#else /* VRAM_SIZE_FIX defined */
4779 pCurMode = (ModeInfoListItem *)(pVBEDataHdr + 1);
4780 for (i = 0; i < MODE_INFO_SIZE; i++)
4781 {
4782 uint32_t pixelWidth, reqSize;
4783 if (mode_info_list[i].info.MemoryModel == VBE_MEMORYMODEL_TEXT_MODE)
4784 pixelWidth = 2;
4785 else
4786 pixelWidth = mode_info_list[i].info.BitsPerPixel / 8;
4787 reqSize = mode_info_list[i].info.XResolution
4788 * mode_info_list[i].info.YResolution
4789 * pixelWidth;
4790 if (reqSize >= pData->vram_size)
4791 continue;
4792 *pCurMode = mode_info_list[i];
4793 pCurMode++;
4794 }
4795#endif /* VRAM_SIZE_FIX defined */
4796
4797 /*
4798 * Copy default modes with subtractred YResolution.
4799 */
4800 if (cyReduction)
4801 {
4802 ModeInfoListItem *pDefMode = mode_info_list;
4803 Log(("vgaR3Construct: cyReduction=%u\n", cyReduction));
4804#ifndef VRAM_SIZE_FIX
4805 for (i = 0; i < MODE_INFO_SIZE; i++, pCurMode++, pDefMode++)
4806 {
4807 *pCurMode = *pDefMode;
4808 pCurMode->mode += 0x30;
4809 pCurMode->info.YResolution -= cyReduction;
4810 }
4811#else /* VRAM_SIZE_FIX defined */
4812 for (i = 0; i < MODE_INFO_SIZE; i++, pDefMode++)
4813 {
4814 uint32_t pixelWidth, reqSize;
4815 if (pDefMode->info.MemoryModel == VBE_MEMORYMODEL_TEXT_MODE)
4816 pixelWidth = 2;
4817 else
4818 pixelWidth = pDefMode->info.BitsPerPixel / 8;
4819 reqSize = pDefMode->info.XResolution * pDefMode->info.YResolution * pixelWidth;
4820 if (reqSize >= pData->vram_size)
4821 continue;
4822 *pCurMode = *pDefMode;
4823 pCurMode->mode += 0x30;
4824 pCurMode->info.YResolution -= cyReduction;
4825 pCurMode++;
4826 }
4827#endif /* VRAM_SIZE_FIX defined */
4828 }
4829
4830
4831 /*
4832 * Add custom modes.
4833 */
4834 if (cCustomModes)
4835 {
4836 uint16_t u16CurMode = 0x160;
4837 for (i = 1; i <= cCustomModes; i++)
4838 {
4839 char szExtraDataKey[sizeof("CustomVideoModeXX")];
4840 char *pszExtraData = NULL;
4841
4842 /* query and decode the custom mode string. */
4843 RTStrPrintf(szExtraDataKey, sizeof(szExtraDataKey), "CustomVideoMode%d", i);
4844 rc = CFGMR3QueryStringAlloc(pCfgHandle, szExtraDataKey, &pszExtraData);
4845 if (VBOX_SUCCESS(rc))
4846 {
4847 ModeInfoListItem *pDefMode = mode_info_list;
4848 unsigned int cx, cy, cBits, cParams;
4849 uint16_t u16DefMode;
4850
4851 cParams = sscanf(pszExtraData, "%ux%ux%u", &cx, &cy, &cBits);
4852 if ( cParams != 3
4853 || (cBits != 16 && cBits != 24 && cBits != 32))
4854 {
4855 AssertMsgFailed(("Configuration error: Invalid mode data '%s' for '%s'! cBits=%d\n", pszExtraData, szExtraDataKey, cBits));
4856 return VERR_VGA_INVALID_CUSTOM_MODE;
4857 }
4858#ifdef VRAM_SIZE_FIX
4859 if (cx * cy * cBits / 8 >= pData->vram_size)
4860 {
4861 AssertMsgFailed(("Configuration error: custom video mode %dx%dx%dbits is too large for the virtual video memory of %dMb. Please increase the video memory size.\n",
4862 cx, cy, cBits, pData->vram_size / _1M));
4863 return VERR_VGA_INVALID_CUSTOM_MODE;
4864 }
4865#endif /* VRAM_SIZE_FIX defined */
4866 MMR3HeapFree(pszExtraData);
4867
4868 /* Use defaults from max@bpp mode. */
4869 switch (cBits)
4870 {
4871 case 16:
4872 u16DefMode = VBE_VESA_MODE_1024X768X565;
4873 break;
4874
4875 case 24:
4876 u16DefMode = VBE_VESA_MODE_1024X768X888;
4877 break;
4878
4879 case 32:
4880 u16DefMode = VBE_OWN_MODE_1024X768X8888;
4881 break;
4882
4883 default: /* gcc, shut up! */
4884 AssertMsgFailed(("gone postal!\n"));
4885 continue;
4886 }
4887
4888 while ( pDefMode->mode != u16DefMode
4889 && pDefMode->mode != VBE_VESA_MODE_END_OF_LIST)
4890 pDefMode++;
4891 Assert(pDefMode->mode != VBE_VESA_MODE_END_OF_LIST);
4892
4893 *pCurMode = *pDefMode;
4894 pCurMode->mode = u16CurMode++;
4895
4896 /* adjust defaults */
4897 pCurMode->info.XResolution = cx;
4898 pCurMode->info.YResolution = cy;
4899
4900 switch (cBits)
4901 {
4902 case 16:
4903 pCurMode->info.BytesPerScanLine = cx * 2;
4904 pCurMode->info.LinBytesPerScanLine = cx * 2;
4905 break;
4906
4907 case 24:
4908 pCurMode->info.BytesPerScanLine = cx * 3;
4909 pCurMode->info.LinBytesPerScanLine = cx * 3;
4910 break;
4911
4912 case 32:
4913 pCurMode->info.BytesPerScanLine = cx * 4;
4914 pCurMode->info.LinBytesPerScanLine = cx * 4;
4915 break;
4916 }
4917
4918 /* commit it */
4919 pCurMode++;
4920 }
4921 else if (rc != VERR_CFGM_VALUE_NOT_FOUND)
4922 {
4923 AssertMsgFailed(("CFGMR3QueryStringAlloc(,'%s',) -> %Vrc\n", szExtraDataKey, rc));
4924 return rc;
4925 }
4926 } /* foreach custom mode key */
4927 }
4928
4929 /*
4930 * Add the "End of list" mode.
4931 */
4932 memset(pCurMode, 0, sizeof(*pCurMode));
4933 pCurMode->mode = VBE_VESA_MODE_END_OF_LIST;
4934
4935 /*
4936 * Register I/O Port for the VBE BIOS Extra Data.
4937 */
4938 rc = PDMDevHlpIOPortRegister(pDevIns, VBE_EXTRA_PORT, 1, NULL, vbeIOPortWriteVBEExtra, vbeIOPortReadVBEExtra, NULL, NULL, "VBE BIOS Extra Data");
4939 if (VBOX_FAILURE(rc))
4940 return rc;
4941#endif
4942
4943 /*
4944 * Statistics.
4945 */
4946 STAM_REG(pVM, &pData->StatGCMemoryRead, STAMTYPE_PROFILE, "/Devices/VGA/GC/Memory/Read", STAMUNIT_TICKS_PER_CALL, "Profiling of the VGAGCMemoryRead() body.");
4947 STAM_REG(pVM, &pData->StatGCMemoryWrite, STAMTYPE_PROFILE, "/Devices/VGA/GC/Memory/Write", STAMUNIT_TICKS_PER_CALL, "Profiling of the VGAGCMemoryWrite() body.");
4948 STAM_REG(pVM, &pData->StatGCIOPortRead, STAMTYPE_PROFILE, "/Devices/VGA/GC/IOPort/Read", STAMUNIT_TICKS_PER_CALL, "Profiling of the VGAGCIOPortRead() body.");
4949 STAM_REG(pVM, &pData->StatGCIOPortWrite, STAMTYPE_PROFILE, "/Devices/VGA/GC/IOPort/Write", STAMUNIT_TICKS_PER_CALL, "Profiling of the VGAGCIOPortWrite() body.");
4950
4951 return VINF_SUCCESS;
4952}
4953
4954
4955/**
4956 * Destruct a device instance.
4957 *
4958 * Most VM resources are freed by the VM. This callback is provided so that any non-VM
4959 * resources can be freed correctly.
4960 *
4961 * @param pDevIns The device instance data.
4962 */
4963static DECLCALLBACK(int) vgaR3Destruct(PPDMDEVINS pDevIns)
4964{
4965#ifdef VBE_NEW_DYN_LIST
4966 PVGASTATE pData = PDMINS2DATA(pDevIns, PVGASTATE);
4967 LogFlow(("vgaR3Destruct:\n"));
4968
4969 /*
4970 * Free MM heap pointers.
4971 */
4972 if (pData->pu8VBEExtraData)
4973 {
4974 MMR3HeapFree(pData->pu8VBEExtraData);
4975 pData->pu8VBEExtraData = NULL;
4976 }
4977#endif
4978
4979 return VINF_SUCCESS;
4980}
4981
4982
4983/**
4984 * The device registration structure.
4985 */
4986const PDMDEVREG g_DeviceVga =
4987{
4988 /* u32Version */
4989 PDM_DEVREG_VERSION,
4990 /* szDeviceName */
4991 "vga",
4992 /* szGCMod */
4993 "VBoxDDGC.gc",
4994 /* szR0Mod */
4995 "VBoxDDR0.r0",
4996 /* pszDescription */
4997 "VGA Adaptor with VESA extensions.",
4998 /* fFlags */
4999 PDM_DEVREG_FLAGS_HOST_BITS_DEFAULT | PDM_DEVREG_FLAGS_GUEST_BITS_DEFAULT | PDM_DEVREG_FLAGS_GC | PDM_DEVREG_FLAGS_R0,
5000 /* fClass */
5001 PDM_DEVREG_CLASS_GRAPHICS,
5002 /* cMaxInstances */
5003 1,
5004 /* cbInstance */
5005 sizeof(VGASTATE),
5006 /* pfnConstruct */
5007 vgaR3Construct,
5008 /* pfnDestruct */
5009 vgaR3Destruct,
5010 /* pfnRelocate */
5011 vgaR3Relocate,
5012 /* pfnIOCtl */
5013 NULL,
5014 /* pfnPowerOn */
5015 NULL,
5016 /* pfnReset */
5017 vgaR3Reset,
5018 /* pfnSuspend */
5019 NULL,
5020 /* pfnResume */
5021 NULL,
5022 /* pfnAttach */
5023 vgaAttach,
5024 /* pfnDetach */
5025 vgaDetach,
5026 /* pfnQueryInterface */
5027 NULL,
5028 /* pfnInitComplete */
5029 NULL
5030};
5031
5032#endif /* !IN_RING3 */
5033#endif /* VBOX */
5034#endif /* !VBOX_DEVICE_STRUCT_TESTCASE */
5035
5036/*
5037 * Local Variables:
5038 * nuke-trailing-whitespace-p:nil
5039 * End:
5040 */
Note: See TracBrowser for help on using the repository browser.

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