VirtualBox

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

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

free VGA memory when destructing the device

  • Property svn:eol-style set to native
File size: 157.7 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 AssertMsg(offVRAM < pData->vram_size, ("offVRAM = %p, pData->vram_size = %p\n", 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 AssertMsg(offVRAM < pData->vram_size, ("offVRAM = %p, pData->vram_size = %p\n", 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#ifndef VBOX
1891 bwidth = width * 4;
1892#else /* VBOX */
1893 /* The width of VRAM scanline. */
1894 bwidth = s->line_offset;
1895 /* In some cases the variable is not yet set, probably due to incomplete
1896 * programming of the virtual hardware ports. Just return.
1897 */
1898 if (bwidth == 0) return;
1899#endif /* VBOX */
1900 y_start = -1;
1901 page_min = 0x7fffffff;
1902 page_max = -1;
1903#ifndef VBOX
1904 d = s->ds->data;
1905 linesize = s->ds->linesize;
1906#else /* VBOX */
1907 d = s->pDrv->pu8Data;
1908 linesize = s->pDrv->cbScanline;
1909#endif /* VBOX */
1910
1911 y1 = 0;
1912 for(y = 0; y < height; y++) {
1913 addr = addr1;
1914 if (!(s->cr[0x17] & 1)) {
1915 int shift;
1916 /* CGA compatibility handling */
1917 shift = 14 + ((s->cr[0x17] >> 6) & 1);
1918 addr = (addr & ~(1 << shift)) | ((y1 & 1) << shift);
1919 }
1920 if (!(s->cr[0x17] & 2)) {
1921 addr = (addr & ~0x8000) | ((y1 & 2) << 14);
1922 }
1923#ifndef VBOX
1924 page0 = s->vram_offset + (addr & TARGET_PAGE_MASK);
1925 page1 = s->vram_offset + ((addr + bwidth - 1) & TARGET_PAGE_MASK);
1926 update = full_update |
1927 cpu_physical_memory_get_dirty(page0, VGA_DIRTY_FLAG) |
1928 cpu_physical_memory_get_dirty(page1, VGA_DIRTY_FLAG);
1929 if ((page1 - page0) > TARGET_PAGE_SIZE) {
1930 /* if wide line, can use another page */
1931 update |= cpu_physical_memory_get_dirty(page0 + TARGET_PAGE_SIZE,
1932 VGA_DIRTY_FLAG);
1933 }
1934#else /* VBOX */
1935 page0 = addr & TARGET_PAGE_MASK;
1936 page1 = (addr + bwidth - 1) & TARGET_PAGE_MASK;
1937 update = full_update | vga_is_dirty(s, page0) | vga_is_dirty(s, page1);
1938 if (page1 - page0 > TARGET_PAGE_SIZE) {
1939 /* if wide line, can use another page */
1940 update |= vga_is_dirty(s, page0 + TARGET_PAGE_SIZE);
1941 }
1942#endif /* VBOX */
1943 /* explicit invalidation for the hardware cursor */
1944 update |= (s->invalidated_y_table[y >> 5] >> (y & 0x1f)) & 1;
1945 if (update) {
1946 if (y_start < 0)
1947 y_start = y;
1948 if (page0 < page_min)
1949 page_min = page0;
1950 if (page1 > page_max)
1951 page_max = page1;
1952#ifndef VBOX
1953 vga_draw_line(s, d, s->vram_ptr + addr, width);
1954#else /* VBOX */
1955 if (s->pvExtVRAMHC != s->pDrv->pu8Data) /* Only if external VRAM was not setup. */
1956 vga_draw_line(s, d, s->CTXSUFF(vram_ptr) + addr, width);
1957#endif /* VBOX */
1958 if (s->cursor_draw_line)
1959 s->cursor_draw_line(s, d, y);
1960 } else {
1961 if (y_start >= 0) {
1962 /* flush to display */
1963#ifndef VBOX
1964 dpy_update(s->ds, 0, y_start,
1965 disp_width, y - y_start);
1966#else /* VBOX */
1967 s->pDrv->pfnUpdateRect(s->pDrv, 0, y_start, disp_width, y - y_start);
1968#endif /* VBOX */
1969 y_start = -1;
1970 }
1971 }
1972 if (!multi_run) {
1973 mask = (s->cr[0x17] & 3) ^ 3;
1974 if ((y1 & mask) == mask)
1975 addr1 += line_offset;
1976 y1++;
1977 multi_run = multi_scan;
1978 } else {
1979 multi_run--;
1980 }
1981 /* line compare acts on the displayed lines */
1982 if ((uint32_t)y == s->line_compare)
1983 addr1 = 0;
1984 d += linesize;
1985 }
1986 if (y_start >= 0) {
1987 /* flush to display */
1988#ifndef VBOX
1989 dpy_update(s->ds, 0, y_start,
1990 disp_width, y - y_start);
1991#else /* VBOX */
1992 s->pDrv->pfnUpdateRect(s->pDrv, 0, y_start, disp_width, y - y_start);
1993#endif /* VBOX */
1994 }
1995 /* reset modified pages */
1996 if (page_max != -1) {
1997#ifndef VBOX
1998 cpu_physical_memory_reset_dirty(page_min, page_max + TARGET_PAGE_SIZE,
1999 VGA_DIRTY_FLAG);
2000#else /* VBOX */
2001 vga_reset_dirty(s, page_min, page_max + TARGET_PAGE_SIZE);
2002#endif /* VBOX */
2003 }
2004 memset(s->invalidated_y_table, 0, ((height + 31) >> 5) * 4);
2005}
2006
2007static void vga_draw_blank(VGAState *s, int full_update)
2008{
2009#ifndef VBOX
2010 int i, w, val;
2011 uint8_t *d;
2012
2013 if (!full_update)
2014 return;
2015 if (s->last_scr_width <= 0 || s->last_scr_height <= 0)
2016 return;
2017 if (s->ds->depth == 8)
2018 val = s->rgb_to_pixel(0, 0, 0);
2019 else
2020 val = 0;
2021 w = s->last_scr_width * ((s->ds->depth + 7) >> 3);
2022 d = s->ds->data;
2023 for(i = 0; i < s->last_scr_height; i++) {
2024 memset(d, val, w);
2025 d += s->ds->linesize;
2026 }
2027 dpy_update(s->ds, 0, 0,
2028 s->last_scr_width, s->last_scr_height);
2029#else /* VBOX */
2030
2031 int i, w, val;
2032 uint8_t *d;
2033 uint32_t cbScanline = s->pDrv->cbScanline;
2034
2035 if (!full_update)
2036 return;
2037 if (s->last_scr_width <= 0 || s->last_scr_height <= 0)
2038 return;
2039 if (s->pDrv->cBits == 8)
2040 val = s->rgb_to_pixel(0, 0, 0);
2041 else
2042 val = 0;
2043 w = s->last_scr_width * ((s->pDrv->cBits + 7) >> 3);
2044 d = s->pDrv->pu8Data;
2045 for(i = 0; i < (int)s->last_scr_height; i++) {
2046 memset(d, val, w);
2047 d += cbScanline;
2048 }
2049 s->pDrv->pfnUpdateRect(s->pDrv, 0, 0, s->last_scr_width, s->last_scr_height);
2050#endif /* VBOX */
2051}
2052
2053#define GMODE_TEXT 0
2054#define GMODE_GRAPH 1
2055#define GMODE_BLANK 2
2056
2057#ifndef VBOX
2058void vga_update_display(void)
2059{
2060#ifdef DEBUG_sunlover
2061 LogFlow(("vga_update_display"));
2062#endif /* DEBUG_sunlover */
2063
2064 VGAState *s = vga_state;
2065#else /* VBOX */
2066static void vga_update_display(PVGASTATE s)
2067{
2068#endif /* VBOX */
2069 int full_update, graphic_mode;
2070
2071#ifndef VBOX
2072 if (s->ds->depth == 0) {
2073#else /* VBOX */
2074 if (s->pDrv->cBits == 0) {
2075#endif /* VBOX */
2076 /* nothing to do */
2077 } else {
2078#ifndef VBOX
2079 switch(s->ds->depth) {
2080#else /* VBOX */
2081 switch(s->pDrv->cBits) {
2082#endif /* VBOX */
2083 case 8:
2084 s->rgb_to_pixel = rgb_to_pixel8_dup;
2085 break;
2086 case 15:
2087 s->rgb_to_pixel = rgb_to_pixel15_dup;
2088 break;
2089 default:
2090 case 16:
2091 s->rgb_to_pixel = rgb_to_pixel16_dup;
2092 break;
2093 case 32:
2094 s->rgb_to_pixel = rgb_to_pixel32_dup;
2095 break;
2096 }
2097
2098 full_update = 0;
2099 if (!(s->ar_index & 0x20)) {
2100 graphic_mode = GMODE_BLANK;
2101 } else {
2102 graphic_mode = s->gr[6] & 1;
2103 }
2104 if (graphic_mode != s->graphic_mode) {
2105 s->graphic_mode = graphic_mode;
2106 full_update = 1;
2107 }
2108 switch(graphic_mode) {
2109 case GMODE_TEXT:
2110 vga_draw_text(s, full_update);
2111 break;
2112 case GMODE_GRAPH:
2113 vga_draw_graphic(s, full_update);
2114 break;
2115 case GMODE_BLANK:
2116 default:
2117 vga_draw_blank(s, full_update);
2118 break;
2119 }
2120 }
2121}
2122
2123/* force a full display refresh */
2124#ifndef VBOX
2125void vga_invalidate_display(void)
2126{
2127 VGAState *s = vga_state;
2128
2129 s->last_width = -1;
2130 s->last_height = -1;
2131}
2132#endif /* !VBOX */
2133
2134#ifndef VBOX /* see vgaR3Reset() */
2135static void vga_reset(VGAState *s)
2136{
2137 memset(s, 0, sizeof(VGAState));
2138 s->graphic_mode = -1; /* force full update */
2139}
2140#endif /* !VBOX */
2141
2142#ifndef VBOX
2143static CPUReadMemoryFunc *vga_mem_read[3] = {
2144 vga_mem_readb,
2145 vga_mem_readw,
2146 vga_mem_readl,
2147};
2148
2149static CPUWriteMemoryFunc *vga_mem_write[3] = {
2150 vga_mem_writeb,
2151 vga_mem_writew,
2152 vga_mem_writel,
2153};
2154#endif /* !VBOX */
2155
2156static void vga_save(QEMUFile *f, void *opaque)
2157{
2158 VGAState *s = (VGAState*)opaque;
2159 int i;
2160
2161 qemu_put_be32s(f, &s->latch);
2162 qemu_put_8s(f, &s->sr_index);
2163 qemu_put_buffer(f, s->sr, 8);
2164 qemu_put_8s(f, &s->gr_index);
2165 qemu_put_buffer(f, s->gr, 16);
2166 qemu_put_8s(f, &s->ar_index);
2167 qemu_put_buffer(f, s->ar, 21);
2168 qemu_put_be32s(f, &s->ar_flip_flop);
2169 qemu_put_8s(f, &s->cr_index);
2170 qemu_put_buffer(f, s->cr, 256);
2171 qemu_put_8s(f, &s->msr);
2172 qemu_put_8s(f, &s->fcr);
2173 qemu_put_8s(f, &s->st00);
2174 qemu_put_8s(f, &s->st01);
2175
2176 qemu_put_8s(f, &s->dac_state);
2177 qemu_put_8s(f, &s->dac_sub_index);
2178 qemu_put_8s(f, &s->dac_read_index);
2179 qemu_put_8s(f, &s->dac_write_index);
2180 qemu_put_buffer(f, s->dac_cache, 3);
2181 qemu_put_buffer(f, s->palette, 768);
2182
2183 qemu_put_be32s(f, &s->bank_offset);
2184#ifdef CONFIG_BOCHS_VBE
2185 qemu_put_byte(f, 1);
2186 qemu_put_be16s(f, &s->vbe_index);
2187 for(i = 0; i < VBE_DISPI_INDEX_NB; i++)
2188 qemu_put_be16s(f, &s->vbe_regs[i]);
2189 qemu_put_be32s(f, &s->vbe_start_addr);
2190 qemu_put_be32s(f, &s->vbe_line_offset);
2191 qemu_put_be32s(f, &s->vbe_bank_mask);
2192#else
2193 qemu_put_byte(f, 0);
2194#endif
2195}
2196
2197static int vga_load(QEMUFile *f, void *opaque, int version_id)
2198{
2199 VGAState *s = (VGAState*)opaque;
2200 int is_vbe, i;
2201
2202 if (version_id != 1)
2203#ifndef VBOX
2204 return -EINVAL;
2205#else /* VBOX */
2206 {
2207 Log(("vga_load: version_id=%d - UNKNOWN\n", version_id));
2208 return VERR_SSM_UNSUPPORTED_DATA_UNIT_VERSION;
2209 }
2210#endif /* VBOX */
2211
2212 qemu_get_be32s(f, &s->latch);
2213 qemu_get_8s(f, &s->sr_index);
2214 qemu_get_buffer(f, s->sr, 8);
2215 qemu_get_8s(f, &s->gr_index);
2216 qemu_get_buffer(f, s->gr, 16);
2217 qemu_get_8s(f, &s->ar_index);
2218 qemu_get_buffer(f, s->ar, 21);
2219 qemu_get_be32s(f, (uint32_t *)&s->ar_flip_flop);
2220 qemu_get_8s(f, &s->cr_index);
2221 qemu_get_buffer(f, s->cr, 256);
2222 qemu_get_8s(f, &s->msr);
2223 qemu_get_8s(f, &s->fcr);
2224 qemu_get_8s(f, &s->st00);
2225 qemu_get_8s(f, &s->st01);
2226
2227 qemu_get_8s(f, &s->dac_state);
2228 qemu_get_8s(f, &s->dac_sub_index);
2229 qemu_get_8s(f, &s->dac_read_index);
2230 qemu_get_8s(f, &s->dac_write_index);
2231 qemu_get_buffer(f, s->dac_cache, 3);
2232 qemu_get_buffer(f, s->palette, 768);
2233
2234 qemu_get_be32s(f, (uint32_t *)&s->bank_offset);
2235 is_vbe = qemu_get_byte(f);
2236#ifdef CONFIG_BOCHS_VBE
2237 if (!is_vbe)
2238#ifndef VBOX
2239 return -EINVAL;
2240#else /* VBOX */
2241 {
2242 Log(("vga_load: !is_vbe !!\n"));
2243 return VERR_SSM_DATA_UNIT_FORMAT_CHANGED;
2244 }
2245#endif /* VBOX */
2246 qemu_get_be16s(f, &s->vbe_index);
2247 for(i = 0; i < VBE_DISPI_INDEX_NB; i++)
2248 qemu_get_be16s(f, &s->vbe_regs[i]);
2249 qemu_get_be32s(f, &s->vbe_start_addr);
2250 qemu_get_be32s(f, &s->vbe_line_offset);
2251 qemu_get_be32s(f, &s->vbe_bank_mask);
2252#else
2253 if (is_vbe)
2254#ifndef VBOX
2255 return -EINVAL;
2256#else /* VBOX */
2257 {
2258 Log(("vga_load: is_vbe !!\n"));
2259 return VERR_SSM_DATA_UNIT_FORMAT_CHANGED;
2260 }
2261#endif /* VBOX */
2262#endif
2263
2264 /* force refresh */
2265 s->graphic_mode = -1;
2266 return 0;
2267}
2268
2269#ifndef VBOX /* see vgaR3IORegionMap */
2270static void vga_map(PCIDevice *pci_dev, int region_num,
2271 uint32_t addr, uint32_t size, int type)
2272{
2273 VGAState *s = vga_state;
2274
2275 cpu_register_physical_memory(addr, s->vram_size, s->vram_offset);
2276}
2277#endif
2278
2279#ifndef VBOX /* see vgaR3Construct */
2280void vga_common_init(VGAState *s, DisplayState *ds, uint8_t *vga_ram_base,
2281 unsigned long vga_ram_offset, int vga_ram_size)
2282#else
2283static void vga_init_expand(void)
2284#endif
2285{
2286 int i, j, v, b;
2287
2288 for(i = 0;i < 256; i++) {
2289 v = 0;
2290 for(j = 0; j < 8; j++) {
2291 v |= ((i >> j) & 1) << (j * 4);
2292 }
2293 expand4[i] = v;
2294
2295 v = 0;
2296 for(j = 0; j < 4; j++) {
2297 v |= ((i >> (2 * j)) & 3) << (j * 4);
2298 }
2299 expand2[i] = v;
2300 }
2301 for(i = 0; i < 16; i++) {
2302 v = 0;
2303 for(j = 0; j < 4; j++) {
2304 b = ((i >> j) & 1);
2305 v |= b << (2 * j);
2306 v |= b << (2 * j + 1);
2307 }
2308 expand4to8[i] = v;
2309 }
2310#ifdef VBOX
2311}
2312#else /* !VBOX */
2313 vga_reset(s);
2314
2315 s->vram_ptr = vga_ram_base;
2316 s->vram_offset = vga_ram_offset;
2317 s->vram_size = vga_ram_size;
2318 s->ds = ds;
2319 s->get_bpp = vga_get_bpp;
2320 s->get_offsets = vga_get_offsets;
2321 s->get_resolution = vga_get_resolution;
2322 /* XXX: currently needed for display */
2323 vga_state = s;
2324}
2325
2326
2327int vga_initialize(PCIBus *bus, DisplayState *ds, uint8_t *vga_ram_base,
2328 unsigned long vga_ram_offset, int vga_ram_size)
2329{
2330 VGAState *s;
2331
2332 s = qemu_mallocz(sizeof(VGAState));
2333 if (!s)
2334 return -1;
2335
2336 vga_common_init(s, ds, vga_ram_base, vga_ram_offset, vga_ram_size);
2337
2338 register_savevm("vga", 0, 1, vga_save, vga_load, s);
2339
2340 register_ioport_write(0x3c0, 16, 1, vga_ioport_write, s);
2341
2342 register_ioport_write(0x3b4, 2, 1, vga_ioport_write, s);
2343 register_ioport_write(0x3d4, 2, 1, vga_ioport_write, s);
2344 register_ioport_write(0x3ba, 1, 1, vga_ioport_write, s);
2345 register_ioport_write(0x3da, 1, 1, vga_ioport_write, s);
2346
2347 register_ioport_read(0x3c0, 16, 1, vga_ioport_read, s);
2348
2349 register_ioport_read(0x3b4, 2, 1, vga_ioport_read, s);
2350 register_ioport_read(0x3d4, 2, 1, vga_ioport_read, s);
2351 register_ioport_read(0x3ba, 1, 1, vga_ioport_read, s);
2352 register_ioport_read(0x3da, 1, 1, vga_ioport_read, s);
2353 s->bank_offset = 0;
2354
2355#ifdef CONFIG_BOCHS_VBE
2356 s->vbe_regs[VBE_DISPI_INDEX_ID] = VBE_DISPI_ID0;
2357 s->vbe_bank_mask = ((s->vram_size >> 16) - 1);
2358#if defined (TARGET_I386)
2359 register_ioport_read(0x1ce, 1, 2, vbe_ioport_read_index, s);
2360 register_ioport_read(0x1cf, 1, 2, vbe_ioport_read_data, s);
2361
2362 register_ioport_write(0x1ce, 1, 2, vbe_ioport_write_index, s);
2363 register_ioport_write(0x1cf, 1, 2, vbe_ioport_write_data, s);
2364
2365 /* old Bochs IO ports */
2366 register_ioport_read(0xff80, 1, 2, vbe_ioport_read_index, s);
2367 register_ioport_read(0xff81, 1, 2, vbe_ioport_read_data, s);
2368
2369 register_ioport_write(0xff80, 1, 2, vbe_ioport_write_index, s);
2370 register_ioport_write(0xff81, 1, 2, vbe_ioport_write_data, s);
2371#else
2372 register_ioport_read(0x1ce, 1, 2, vbe_ioport_read_index, s);
2373 register_ioport_read(0x1d0, 1, 2, vbe_ioport_read_data, s);
2374
2375 register_ioport_write(0x1ce, 1, 2, vbe_ioport_write_index, s);
2376 register_ioport_write(0x1d0, 1, 2, vbe_ioport_write_data, s);
2377#endif
2378#endif /* CONFIG_BOCHS_VBE */
2379
2380 vga_io_memory = cpu_register_io_memory(0, vga_mem_read, vga_mem_write, s);
2381 cpu_register_physical_memory(isa_mem_base + 0x000a0000, 0x20000,
2382 vga_io_memory);
2383
2384 if (bus) {
2385 PCIDevice *d;
2386 uint8_t *pci_conf;
2387
2388 d = pci_register_device(bus, "VGA",
2389 sizeof(PCIDevice),
2390 -1, NULL, NULL);
2391 pci_conf = d->config;
2392 pci_conf[0x00] = 0x34; // dummy VGA (same as Bochs ID)
2393 pci_conf[0x01] = 0x12;
2394 pci_conf[0x02] = 0x11;
2395 pci_conf[0x03] = 0x11;
2396 pci_conf[0x0a] = 0x00; // VGA controller
2397 pci_conf[0x0b] = 0x03;
2398 pci_conf[0x0e] = 0x00; // header_type
2399
2400 /* XXX: vga_ram_size must be a power of two */
2401 pci_register_io_region(d, 0, vga_ram_size,
2402 PCI_ADDRESS_SPACE_MEM_PREFETCH, vga_map);
2403 } else {
2404#ifdef CONFIG_BOCHS_VBE
2405 /* XXX: use optimized standard vga accesses */
2406 cpu_register_physical_memory(VBE_DISPI_LFB_PHYSICAL_ADDRESS,
2407 vga_ram_size, vga_ram_offset);
2408#endif
2409 }
2410 return 0;
2411}
2412#endif /* !VBOX */
2413
2414
2415#ifndef VBOX
2416/********************************************************/
2417/* vga screen dump */
2418
2419static int vga_save_w, vga_save_h;
2420
2421static void vga_save_dpy_update(DisplayState *s,
2422 int x, int y, int w, int h)
2423{
2424}
2425
2426static void vga_save_dpy_resize(DisplayState *s, int w, int h)
2427{
2428 s->linesize = w * 4;
2429#ifndef VBOX
2430 s->data = qemu_malloc(h * s->linesize);
2431#else /* VBOX */
2432 if (!s->data)
2433 {
2434 PPDMDEVINS pDevIns = VGASTATE2DEVINS((PVGASTATE)s->pvVgaState);
2435 s->data = PDMDevHlpMMHeapAlloc(pDevIns, h * s->linesize);
2436 }
2437 else // (32-bpp buffer is allocated by the caller)
2438 s->linesize = ((w * 32 + 31) / 32) * 4;
2439#endif /* VBOX */
2440 vga_save_w = w;
2441 vga_save_h = h;
2442}
2443
2444static void vga_save_dpy_refresh(DisplayState *s)
2445{
2446}
2447
2448static int ppm_save(const char *filename, uint8_t *data,
2449 int w, int h, int linesize)
2450{
2451 FILE *f;
2452 uint8_t *d, *d1;
2453 unsigned int v;
2454 int y, x;
2455
2456 f = fopen(filename, "wb");
2457 if (!f)
2458 return -1;
2459 fprintf(f, "P6\n%d %d\n%d\n",
2460 w, h, 255);
2461 d1 = data;
2462 for(y = 0; y < h; y++) {
2463 d = d1;
2464 for(x = 0; x < w; x++) {
2465 v = *(uint32_t *)d;
2466 fputc((v >> 16) & 0xff, f);
2467 fputc((v >> 8) & 0xff, f);
2468 fputc((v) & 0xff, f);
2469 d += 4;
2470 }
2471 d1 += linesize;
2472 }
2473 fclose(f);
2474 return 0;
2475}
2476
2477/* save the vga display in a PPM image even if no display is
2478 available */
2479void vga_screen_dump(const char *filename)
2480{
2481 VGAState *s = vga_state;
2482 DisplayState *saved_ds, ds1, *ds = &ds1;
2483
2484 /* XXX: this is a little hackish */
2485 vga_invalidate_display();
2486 saved_ds = s->ds;
2487
2488 memset(ds, 0, sizeof(DisplayState));
2489 ds->dpy_update = vga_save_dpy_update;
2490 ds->dpy_resize = vga_save_dpy_resize;
2491 ds->dpy_refresh = vga_save_dpy_refresh;
2492 ds->depth = 32;
2493
2494 s->ds = ds;
2495 s->graphic_mode = -1;
2496 vga_update_display();
2497
2498 if (ds->data) {
2499 ppm_save(filename, ds->data, vga_save_w, vga_save_h,
2500 s->ds->linesize);
2501 qemu_free(ds->data);
2502 }
2503 s->ds = saved_ds;
2504}
2505#endif /* !VBOX */
2506
2507
2508#if 0 //def VBOX
2509/* copy the vga display contents to the given buffer. the size of the buffer
2510 must be sufficient to store the screen copy (see below). the width and height
2511 parameters determine the required dimensions of the copy. If they differ
2512 from the actual screen dimensions, then the returned copy is shrinked or
2513 stretched accordingly. The copy is always a 32-bit image, so the size of
2514 the buffer supplied must be at least (((width * 32 + 31) / 32) * 4) * height,
2515 i.e. dword-aligned. returns zero if the operation was successfull and -1
2516 otherwise. */
2517
2518static int vga_copy_screen_to(PVGASTATE s, uint8_t *buf, int width, int height)
2519{
2520 DisplayState *saved_ds, ds1, *ds = &ds1;
2521 if (!buf || width <= 0 || height <= 0)
2522 return -1;
2523
2524 /* XXX: this is a little hackish */
2525 vga_invalidate_display(s);
2526 saved_ds = s->ds;
2527
2528 memset(ds, 0, sizeof(DisplayState));
2529 ds->dpy_update = vga_save_dpy_update;
2530 ds->dpy_resize = vga_save_dpy_resize;
2531 ds->dpy_refresh = vga_save_dpy_refresh;
2532 ds->depth = 32;
2533 ds->data = buf;
2534 ds->pvVgaState = s;
2535
2536 s->ds = ds;
2537 s->graphic_mode = -1;
2538 vga_update_display(s);
2539
2540//@@TODO (dmik): implement stretching/shrinking!
2541
2542 s->ds = saved_ds;
2543 return 0;
2544}
2545
2546/* copy the given buffer to the vga display. width and height define the
2547 dimensions of the image in the buffer. x and y define the point on the
2548 vga display to copy the image to. the buffer is assumed to contain a 32-bit
2549 image, so the size of one scanline must be ((width * 32 + 31) / 32) * 4),
2550 i.e. dword-aligned. returns zero if the operation was successfull and -1
2551 otherwise. */
2552static int vga_copy_screen_from(PVGASTATE s, uint8_t *buf, int x, int y, int width, int height)
2553{
2554 int bpl = ((width * 32 + 31) / 32) * 4;
2555 int linesize = s->ds->linesize;
2556 uint8_t *dst;
2557 uint8_t *src;
2558 int bpp;
2559 vga_draw_line_func *vga_draw_line;
2560
2561 if (!buf || x < 0 || y < 0 || width <= 0 || height <= 0
2562 || x + width > s->ds->width || y + height > s->ds->height)
2563 return -1;
2564
2565 vga_draw_line = vga_draw_line_table[VGA_DRAW_LINE32 * 4 + get_depth_index(s->ds->depth)];
2566 switch (s->ds->depth) {
2567 case 8: bpp = 1; break;
2568 case 15:
2569 case 16: bpp = 2; break;
2570 case 32: bpp = 4; break;
2571 default: return -1;
2572 }
2573
2574 dst = s->ds->data + y * linesize + x * bpp;
2575 src = buf;
2576 for (y = 0; y < height; y ++)
2577 {
2578 vga_draw_line(s, dst, src, width);
2579 dst += linesize;
2580 src += bpl;
2581 }
2582
2583 return 0;
2584}
2585#endif
2586
2587#endif /* !VBOX || !IN_GC || !IN_RING0 */
2588
2589
2590
2591#ifdef VBOX /* InnoTek code start */
2592
2593
2594/* -=-=-=-=-=- all contexts -=-=-=-=-=- */
2595
2596/**
2597 * Port I/O Handler for VGA OUT operations.
2598 *
2599 * @returns VBox status code.
2600 *
2601 * @param pDevIns The device instance.
2602 * @param pvUser User argument - ignored.
2603 * @param Port Port number used for the IN operation.
2604 * @param u32 The value to output.
2605 * @param cb The value size in bytes.
2606 */
2607PDMBOTHCBDECL(int) vgaIOPortWrite(PPDMDEVINS pDevIns, void *pvUser, RTIOPORT Port, uint32_t u32, unsigned cb)
2608{
2609 NOREF(pvUser);
2610 if (cb == 1)
2611 vga_ioport_write(PDMINS2DATA(pDevIns, PVGASTATE), Port, u32);
2612 else if (cb == 2)
2613 {
2614 vga_ioport_write(PDMINS2DATA(pDevIns, PVGASTATE), Port, u32 & 0xff);
2615 vga_ioport_write(PDMINS2DATA(pDevIns, PVGASTATE), Port + 1, u32 >> 8);
2616 }
2617 return VINF_SUCCESS;
2618}
2619
2620
2621/**
2622 * Port I/O Handler for VGA IN operations.
2623 *
2624 * @returns VBox status code.
2625 *
2626 * @param pDevIns The device instance.
2627 * @param pvUser User argument - ignored.
2628 * @param Port Port number used for the IN operation.
2629 * @param pu32 Where to store the result.
2630 * @param cb Number of bytes read.
2631 */
2632PDMBOTHCBDECL(int) vgaIOPortRead(PPDMDEVINS pDevIns, void *pvUser, RTIOPORT Port, uint32_t *pu32, unsigned cb)
2633{
2634 NOREF(pvUser);
2635 if (cb == 1)
2636 {
2637 *pu32 = vga_ioport_read(PDMINS2DATA(pDevIns, PVGASTATE), Port);
2638 return VINF_SUCCESS;
2639 }
2640 else if (cb == 2)
2641 {
2642 *pu32 = vga_ioport_read(PDMINS2DATA(pDevIns, PVGASTATE), Port)
2643 | (vga_ioport_read(PDMINS2DATA(pDevIns, PVGASTATE), Port + 1) << 8);
2644 return VINF_SUCCESS;
2645 }
2646 return VERR_IOM_IOPORT_UNUSED;
2647}
2648
2649
2650/**
2651 * Port I/O Handler for VBE OUT operations.
2652 *
2653 * @returns VBox status code.
2654 *
2655 * @param pDevIns The device instance.
2656 * @param pvUser User argument - ignored.
2657 * @param Port Port number used for the IN operation.
2658 * @param u32 The value to output.
2659 * @param cb The value size in bytes.
2660 */
2661PDMBOTHCBDECL(int) vgaIOPortWriteVBEData(PPDMDEVINS pDevIns, void *pvUser, RTIOPORT Port, uint32_t u32, unsigned cb)
2662{
2663 VGAState *s = PDMINS2DATA(pDevIns, PVGASTATE);
2664
2665 NOREF(pvUser);
2666
2667#ifdef IN_GC
2668 /*
2669 * The VBE_DISPI_INDEX_ENABLE has to be done on the host in order to call pfnLFBModeChange callback.
2670 */
2671 if (s->vbe_index == VBE_DISPI_INDEX_ENABLE)
2672 {
2673 Log(("vgaIOPortWriteVBEData: VBE_DISPI_INDEX_ENABLE - Switching to host...\n"));
2674 return VINF_IOM_HC_IOPORT_WRITE;
2675 }
2676#endif
2677#ifdef VBE_BYTEWISE_IO
2678 if (cb == 1)
2679 {
2680 if (!s->fWriteVBEData)
2681 {
2682 if ( (s->vbe_index == VBE_DISPI_INDEX_ENABLE)
2683 && (u32 & VBE_DISPI_ENABLED))
2684 {
2685 s->fWriteVBEData = false;
2686 vbe_ioport_write_data(s, Port, u32 & 0xFF);
2687 return VINF_SUCCESS;
2688 }
2689 else
2690 {
2691 s->cbWriteVBEData = u32 & 0xFF;
2692 s->fWriteVBEData = true;
2693 return VINF_SUCCESS;
2694 }
2695 }
2696 else
2697 {
2698 u32 = (s->cbWriteVBEData << 8) | (u32 & 0xFF);
2699 s->fWriteVBEData = false;
2700 cb = 2;
2701 }
2702 }
2703#endif
2704 if (cb == 2)
2705 {
2706//#ifdef IN_GC
2707// /*
2708// * The VBE_DISPI_INDEX_ENABLE memsets the entire frame buffer.
2709// * Since we're not mapping the entire framebuffer any longer that
2710// * has to be done on the host.
2711// */
2712// if ( (s->vbe_index == VBE_DISPI_INDEX_ENABLE)
2713// && (u32 & VBE_DISPI_ENABLED))
2714// {
2715// Log(("vgaIOPortWriteVBEData: VBE_DISPI_INDEX_ENABLE & VBE_DISPI_ENABLED - Switching to host...\n"));
2716// return VINF_IOM_HC_IOPORT_WRITE;
2717// }
2718//#endif
2719 vbe_ioport_write_data(s, Port, u32);
2720 }
2721 else
2722 AssertMsgFailed(("vgaIOPortWriteVBEData: Port=%#x cb=%d u32=%#x\n", Port, cb, u32));
2723 return VINF_SUCCESS;
2724}
2725
2726
2727/**
2728 * Port I/O Handler for VBE OUT operations.
2729 *
2730 * @returns VBox status code.
2731 *
2732 * @param pDevIns The device instance.
2733 * @param pvUser User argument - ignored.
2734 * @param Port Port number used for the IN operation.
2735 * @param u32 The value to output.
2736 * @param cb The value size in bytes.
2737 */
2738PDMBOTHCBDECL(int) vgaIOPortWriteVBEIndex(PPDMDEVINS pDevIns, void *pvUser, RTIOPORT Port, uint32_t u32, unsigned cb)
2739{
2740 NOREF(pvUser);
2741#ifdef VBE_BYTEWISE_IO
2742 if (cb == 1)
2743 {
2744 VGAState *s = PDMINS2DATA(pDevIns, PVGASTATE);
2745 if (!s->fWriteVBEIndex)
2746 {
2747 s->cbWriteVBEIndex = u32 & 0x00FF;
2748 s->fWriteVBEIndex = true;
2749 return VINF_SUCCESS;
2750 }
2751 else
2752 {
2753 s->fWriteVBEIndex = false;
2754 vbe_ioport_write_index(s, Port, (s->cbWriteVBEIndex << 8) | (u32 & 0x00FF));
2755 return VINF_SUCCESS;
2756 }
2757 }
2758 else
2759#endif
2760 if (cb == 2)
2761 vbe_ioport_write_index(PDMINS2DATA(pDevIns, PVGASTATE), Port, u32);
2762 else
2763 AssertMsgFailed(("vgaIOPortWriteVBEIndex: Port=%#x cb=%d u32=%#x\n", Port, cb, u32));
2764 return VINF_SUCCESS;
2765}
2766
2767
2768/**
2769 * Port I/O Handler for VBE IN operations.
2770 *
2771 * @returns VBox status code.
2772 *
2773 * @param pDevIns The device instance.
2774 * @param pvUser User argument - ignored.
2775 * @param Port Port number used for the IN operation.
2776 * @param pu32 Where to store the result.
2777 * @param cb Number of bytes to read.
2778 */
2779PDMBOTHCBDECL(int) vgaIOPortReadVBEData(PPDMDEVINS pDevIns, void *pvUser, RTIOPORT Port, uint32_t *pu32, unsigned cb)
2780{
2781 NOREF(pvUser);
2782#ifdef VBE_BYTEWISE_IO
2783 if (cb == 1)
2784 {
2785 VGAState *s = PDMINS2DATA(pDevIns, PVGASTATE);
2786
2787 if (!s->fReadVBEData)
2788 {
2789 *pu32 = (vbe_ioport_read_data(s, Port) >> 8) & 0xFF;
2790 s->fReadVBEData = true;
2791 return VINF_SUCCESS;
2792 }
2793 else
2794 {
2795 *pu32 = vbe_ioport_read_data(s, Port) & 0xFF;
2796 s->fReadVBEData = false;
2797 return VINF_SUCCESS;
2798 }
2799 }
2800 else
2801#endif
2802 if (cb == 2)
2803 {
2804 *pu32 = vbe_ioport_read_data(PDMINS2DATA(pDevIns, PVGASTATE), Port);
2805 return VINF_SUCCESS;
2806 }
2807 else if (cb == 4)
2808 {
2809 /* Quick hack for getting the vram size. */
2810 VGAState *s = PDMINS2DATA(pDevIns, PVGASTATE);
2811 *pu32 = s->vram_size;
2812 return VINF_SUCCESS;
2813 }
2814 AssertMsgFailed(("vgaIOPortReadVBEData: Port=%#x cb=%d\n", Port, cb));
2815 return VERR_IOM_IOPORT_UNUSED;
2816}
2817
2818
2819/**
2820 * Port I/O Handler for VBE IN operations.
2821 *
2822 * @returns VBox status code.
2823 *
2824 * @param pDevIns The device instance.
2825 * @param pvUser User argument - ignored.
2826 * @param Port Port number used for the IN operation.
2827 * @param pu32 Where to store the result.
2828 * @param cb Number of bytes to read.
2829 */
2830PDMBOTHCBDECL(int) vgaIOPortReadVBEIndex(PPDMDEVINS pDevIns, void *pvUser, RTIOPORT Port, uint32_t *pu32, unsigned cb)
2831{
2832 NOREF(pvUser);
2833#ifdef VBE_BYTEWISE_IO
2834 if (cb == 1)
2835 {
2836 VGAState *s = PDMINS2DATA(pDevIns, PVGASTATE);
2837
2838 if (!s->fReadVBEIndex)
2839 {
2840 *pu32 = (vbe_ioport_read_index(s, Port) >> 8) & 0xFF;
2841 s->fReadVBEIndex = true;
2842 return VINF_SUCCESS;
2843 }
2844 else
2845 {
2846 *pu32 = vbe_ioport_read_index(s, Port) & 0xFF;
2847 s->fReadVBEIndex = false;
2848 return VINF_SUCCESS;
2849 }
2850 }
2851 else
2852#endif
2853 if (cb == 2)
2854 {
2855 *pu32 = vbe_ioport_read_index(PDMINS2DATA(pDevIns, PVGASTATE), Port);
2856 return VINF_SUCCESS;
2857 }
2858 AssertMsgFailed(("vgaIOPortReadVBEIndex: Port=%#x cb=%d\n", Port, cb));
2859 return VERR_IOM_IOPORT_UNUSED;
2860}
2861
2862
2863
2864
2865
2866/* -=-=-=-=-=- Guest Context -=-=-=-=-=- */
2867
2868/*
2869 * Internal. For use inside VGAGCMemoryFillWrite only.
2870 * Macro for apply logical operation and bit mask.
2871 */
2872#define APPLY_LOGICAL_AND_MASK(s, val, bit_mask) \
2873 /* apply logical operation */ \
2874 switch(s->gr[3] >> 3) \
2875 { \
2876 case 0: \
2877 default: \
2878 /* nothing to do */ \
2879 break; \
2880 case 1: \
2881 /* and */ \
2882 val &= s->latch; \
2883 break; \
2884 case 2: \
2885 /* or */ \
2886 val |= s->latch; \
2887 break; \
2888 case 3: \
2889 /* xor */ \
2890 val ^= s->latch; \
2891 break; \
2892 } \
2893 /* apply bit mask */ \
2894 val = (val & bit_mask) | (s->latch & ~bit_mask)
2895
2896/**
2897 * Legacy VGA memory (0xa0000 - 0xbffff) write hook, to be called from IOM and from the inside of VGADeviceGC.cpp.
2898 * This is the advanced version of vga_mem_writeb function.
2899 *
2900 * @returns VBox status code.
2901 * @param pDevIns Pointer device instance.
2902 * @param pvUser User argument - ignored.
2903 * @param GCPhysAddr Physical address of memory to write.
2904 * @param u32Item Data to write, up to 4 bytes.
2905 * @param cbItem Size of data Item, only 1/2/4 bytes is allowed for now.
2906 * @param cItems Number of data items to write.
2907 */
2908PDMBOTHCBDECL(int) vgaMMIOFill(PPDMDEVINS pDevIns, void *pvUser, RTGCPHYS GCPhysAddr, uint32_t u32Item, unsigned cbItem, unsigned cItems)
2909{
2910 PVGASTATE pData = PDMINS2DATA(pDevIns, PVGASTATE);
2911 uint32_t b;
2912 uint32_t write_mask, bit_mask, set_mask;
2913 uint32_t aVal[4]; /** @todo r=bird: Why is this an 32-bit array? */
2914 unsigned i;
2915 NOREF(pvUser);
2916 for (i = 0; i < cbItem; i++)
2917 {
2918 aVal[i] = u32Item & 0xff;
2919 u32Item >>= 8;
2920 }
2921
2922 /* convert to VGA memory offset */
2923 /// @todo add check for the end of region
2924 GCPhysAddr &= 0x1ffff;
2925 switch((pData->gr[6] >> 2) & 3) {
2926 case 0:
2927 break;
2928 case 1:
2929 if (GCPhysAddr >= 0x10000)
2930 return VINF_SUCCESS;
2931 GCPhysAddr += pData->bank_offset;
2932 break;
2933 case 2:
2934 GCPhysAddr -= 0x10000;
2935 if (GCPhysAddr >= 0x8000)
2936 return VINF_SUCCESS;
2937 break;
2938 default:
2939 case 3:
2940 GCPhysAddr -= 0x18000;
2941 if (GCPhysAddr >= 0x8000)
2942 return VINF_SUCCESS;
2943 break;
2944 }
2945
2946 if (pData->sr[4] & 0x08) {
2947 /* chain 4 mode : simplest access */
2948#ifdef IN_GC
2949 if (GCPhysAddr + cItems * cbItem >= VGA_MAPPING_SIZE)
2950 return VINF_IOM_HC_MMIO_WRITE;
2951#else
2952 if (GCPhysAddr + cItems * cbItem >= pData->vram_size)
2953 {
2954 AssertMsgFailed(("GCPhysAddr=%VGp cItems=%#x cbItem=%d\n", GCPhysAddr, cItems, cbItem));
2955 return VINF_SUCCESS;
2956 }
2957#endif
2958
2959 while (cItems-- > 0)
2960 for (i = 0; i < cbItem; i++)
2961 {
2962 if (pData->sr[2] & (1 << (GCPhysAddr & 3)))
2963 {
2964 CTXSUFF(pData->vram_ptr)[GCPhysAddr] = aVal[i];
2965 vga_set_dirty(pData, GCPhysAddr);
2966 }
2967 GCPhysAddr++;
2968 }
2969 } else if (pData->gr[5] & 0x10) {
2970 /* odd/even mode (aka text mode mapping) */
2971#ifdef IN_GC
2972 if (GCPhysAddr * 2 + cItems * cbItem >= VGA_MAPPING_SIZE)
2973 return VINF_IOM_HC_MMIO_WRITE;
2974#else
2975 if (GCPhysAddr * 2 + cItems * cbItem >= pData->vram_size)
2976 {
2977 AssertMsgFailed(("GCPhysAddr=%VGp cItems=%#x cbItem=%d\n", GCPhysAddr, cItems, cbItem));
2978 return VINF_SUCCESS;
2979 }
2980#endif
2981 while (cItems-- > 0)
2982 for (i = 0; i < cbItem; i++)
2983 {
2984 unsigned plane = (pData->gr[4] & 2) | (GCPhysAddr & 1);
2985 if (pData->sr[2] & (1 << plane)) {
2986 RTGCPHYS PhysAddr2 = ((GCPhysAddr & ~1) << 1) | plane;
2987 CTXSUFF(pData->vram_ptr)[PhysAddr2] = aVal[i];
2988 vga_set_dirty(pData, PhysAddr2);
2989 }
2990 GCPhysAddr++;
2991 }
2992 } else {
2993#ifdef IN_GC
2994 if (GCPhysAddr + cItems * cbItem >= VGA_MAPPING_SIZE)
2995 return VINF_IOM_HC_MMIO_WRITE;
2996#else
2997 if (GCPhysAddr + cItems * cbItem >= pData->vram_size)
2998 {
2999 AssertMsgFailed(("GCPhysAddr=%VGp cItems=%#x cbItem=%d\n", GCPhysAddr, cItems, cbItem));
3000 return VINF_SUCCESS;
3001 }
3002#endif
3003
3004 /* standard VGA latched access */
3005 switch(pData->gr[5] & 3) {
3006 default:
3007 case 0:
3008 /* rotate */
3009 b = pData->gr[3] & 7;
3010 bit_mask = pData->gr[8];
3011 bit_mask |= bit_mask << 8;
3012 bit_mask |= bit_mask << 16;
3013 set_mask = mask16[pData->gr[1]];
3014
3015 for (i = 0; i < cbItem; i++)
3016 {
3017 aVal[i] = ((aVal[i] >> b) | (aVal[i] << (8 - b))) & 0xff;
3018 aVal[i] |= aVal[i] << 8;
3019 aVal[i] |= aVal[i] << 16;
3020
3021 /* apply set/reset mask */
3022 aVal[i] = (aVal[i] & ~set_mask) | (mask16[pData->gr[0]] & set_mask);
3023
3024 APPLY_LOGICAL_AND_MASK(pData, aVal[i], bit_mask);
3025 }
3026 break;
3027 case 1:
3028 for (i = 0; i < cbItem; i++)
3029 aVal[i] = pData->latch;
3030 break;
3031 case 2:
3032 bit_mask = pData->gr[8];
3033 bit_mask |= bit_mask << 8;
3034 bit_mask |= bit_mask << 16;
3035 for (i = 0; i < cbItem; i++)
3036 {
3037 aVal[i] = mask16[aVal[i] & 0x0f];
3038
3039 APPLY_LOGICAL_AND_MASK(pData, aVal[i], bit_mask);
3040 }
3041 break;
3042 case 3:
3043 /* rotate */
3044 b = pData->gr[3] & 7;
3045
3046 for (i = 0; i < cbItem; i++)
3047 {
3048 aVal[i] = (aVal[i] >> b) | (aVal[i] << (8 - b));
3049 bit_mask = pData->gr[8] & aVal[i];
3050 bit_mask |= bit_mask << 8;
3051 bit_mask |= bit_mask << 16;
3052 aVal[i] = mask16[pData->gr[0]];
3053
3054 APPLY_LOGICAL_AND_MASK(pData, aVal[i], bit_mask);
3055 }
3056 break;
3057 }
3058
3059 /* mask data according to sr[2] */
3060 write_mask = mask16[pData->sr[2]];
3061
3062 /* actually write data */
3063 if (cbItem == 1)
3064 {
3065 /* The most frequently case is 1 byte I/O. */
3066 while (cItems-- > 0)
3067 {
3068 ((uint32_t *)pData->CTXSUFF(vram_ptr))[GCPhysAddr] = (((uint32_t *)pData->CTXSUFF(vram_ptr))[GCPhysAddr] & ~write_mask) | (aVal[0] & write_mask);
3069 vga_set_dirty(pData, GCPhysAddr << 2);
3070 GCPhysAddr++;
3071 }
3072 }
3073 else if (cbItem == 2)
3074 {
3075 /* The second case is 2 bytes I/O. */
3076 while (cItems-- > 0)
3077 {
3078 ((uint32_t *)pData->CTXSUFF(vram_ptr))[GCPhysAddr] = (((uint32_t *)pData->CTXSUFF(vram_ptr))[GCPhysAddr] & ~write_mask) | (aVal[0] & write_mask);
3079 vga_set_dirty(pData, GCPhysAddr << 2);
3080 GCPhysAddr++;
3081
3082 ((uint32_t *)pData->CTXSUFF(vram_ptr))[GCPhysAddr] = (((uint32_t *)pData->CTXSUFF(vram_ptr))[GCPhysAddr] & ~write_mask) | (aVal[1] & write_mask);
3083 vga_set_dirty(pData, GCPhysAddr << 2);
3084 GCPhysAddr++;
3085 }
3086 }
3087 else
3088 {
3089 /* And the rest is 4 bytes. */
3090 Assert(cbItem == 4);
3091 while (cItems-- > 0)
3092 for (i = 0; i < cbItem; i++)
3093 {
3094 ((uint32_t *)pData->CTXSUFF(vram_ptr))[GCPhysAddr] = (((uint32_t *)pData->CTXSUFF(vram_ptr))[GCPhysAddr] & ~write_mask) | (aVal[i] & write_mask);
3095 vga_set_dirty(pData, GCPhysAddr << 2);
3096 GCPhysAddr++;
3097 }
3098 }
3099 }
3100 return VINF_SUCCESS;
3101}
3102#undef APPLY_LOGICAL_AND_MASK
3103
3104
3105/**
3106 * Legacy VGA memory (0xa0000 - 0xbffff) read hook, to be called from IOM.
3107 *
3108 * @returns VBox status code.
3109 * @param pDevIns Pointer device instance.
3110 * @param pvUser User argument - ignored.
3111 * @param GCPhysAddr Physical address of memory to read.
3112 * @param pv Where to store readed data.
3113 * @param cb Bytes to read.
3114 */
3115PDMBOTHCBDECL(int) vgaMMIORead(PPDMDEVINS pDevIns, void *pvUser, RTGCPHYS GCPhysAddr, void *pv, unsigned cb)
3116{
3117 PVGASTATE pData = PDMINS2DATA(pDevIns, PVGASTATE);
3118 STAM_PROFILE_START(&pData->StatGCMemoryRead, a);
3119 NOREF(pvUser);
3120 switch (cb)
3121 {
3122 case 1:
3123 *(uint8_t *)pv = vga_mem_readb(pData, GCPhysAddr); break;
3124 case 2:
3125 *(uint16_t *)pv = vga_mem_readb(pData, GCPhysAddr)
3126 | (vga_mem_readb(pData, GCPhysAddr + 1) << 8);
3127 break;
3128 case 4:
3129 *(uint32_t *)pv = vga_mem_readb(pData, GCPhysAddr)
3130 | (vga_mem_readb(pData, GCPhysAddr + 1) << 8)
3131 | (vga_mem_readb(pData, GCPhysAddr + 2) << 16)
3132 | (vga_mem_readb(pData, GCPhysAddr + 3) << 24);
3133 break;
3134
3135 default:
3136 {
3137 uint8_t *pu8Data = (uint8_t *)pv;
3138 while (cb-- > 0)
3139 *pu8Data++ = vga_mem_readb(pData, GCPhysAddr++);
3140 }
3141 }
3142 STAM_PROFILE_STOP(&pData->StatGCMemoryRead, a);
3143 return VINF_SUCCESS;
3144}
3145
3146/**
3147 * Legacy VGA memory (0xa0000 - 0xbffff) write hook, to be called from IOM.
3148 *
3149 * @returns VBox status code.
3150 * @param pDevIns Pointer device instance.
3151 * @param pvUser User argument - ignored.
3152 * @param GCPhysAddr Physical address of memory to write.
3153 * @param pv Pointer to data.
3154 * @param cb Bytes to write.
3155 */
3156PDMBOTHCBDECL(int) vgaMMIOWrite(PPDMDEVINS pDevIns, void *pvUser, RTGCPHYS GCPhysAddr, void *pv, unsigned cb)
3157{
3158 PVGASTATE pData = PDMINS2DATA(pDevIns, PVGASTATE);
3159 uint8_t *pu8 = (uint8_t *)pv;
3160 int rc = VINF_SUCCESS;
3161 STAM_PROFILE_START(&pData->StatGCMemoryWrite, a);
3162
3163 switch (cb)
3164 {
3165 case 1:
3166 rc = vga_mem_writeb(pData, GCPhysAddr, *pu8);
3167 break;
3168#if 1
3169 case 2:
3170 rc = vga_mem_writeb(pData, GCPhysAddr + 0, pu8[0]);
3171 if (RT_LIKELY(rc == VINF_SUCCESS))
3172 rc = vga_mem_writeb(pData, GCPhysAddr + 1, pu8[1]);
3173 break;
3174 case 4:
3175 rc = vga_mem_writeb(pData, GCPhysAddr + 0, pu8[0]);
3176 if (RT_LIKELY(rc == VINF_SUCCESS))
3177 rc = vga_mem_writeb(pData, GCPhysAddr + 1, pu8[1]);
3178 if (RT_LIKELY(rc == VINF_SUCCESS))
3179 rc = vga_mem_writeb(pData, GCPhysAddr + 2, pu8[2]);
3180 if (RT_LIKELY(rc == VINF_SUCCESS))
3181 rc = vga_mem_writeb(pData, GCPhysAddr + 3, pu8[3]);
3182 break;
3183#else
3184 case 2:
3185 rc = vgaMMIOFill(pDevIns, GCPhysAddr, *(uint16_t *)pv, 2, 1);
3186 break;
3187 case 4:
3188 rc = vgaMMIOFill(pDevIns, GCPhysAddr, *(uint32_t *)pv, 4, 1);
3189 break;
3190#endif
3191 default:
3192 while (cb-- > 0 && rc == VINF_SUCCESS)
3193 rc = vga_mem_writeb(pData, GCPhysAddr++, *pu8++);
3194 break;
3195
3196 }
3197 STAM_PROFILE_STOP(&pData->StatGCMemoryWrite, a);
3198 return rc;
3199}
3200
3201
3202/**
3203 * Handle LFB access.
3204 * @returns VBox status code.
3205 * @param pVM VM handle.
3206 * @param pData VGA device instance data.
3207 * @param GCPhys The access physical address.
3208 * @param GCPtr The access virtual address (only GC).
3209 */
3210static int vgaLFBAccess(PVM pVM, PVGASTATE pData, RTGCPHYS GCPhys, RTGCPTR GCPtr)
3211{
3212 int rc;
3213
3214 /*
3215 * Set page dirty bit.
3216 */
3217 vga_set_dirty(pData, GCPhys - pData->GCPhysVRAM);
3218 pData->fLFBUpdated = true;
3219
3220 /*
3221 * Turn of the write handler for this particular page and make it R/W.
3222 * Then return telling the caller to restart the guest instruction.
3223 * ASSUME: the guest always maps video memory RW.
3224 */
3225 rc = PGMHandlerPhysicalPageTempOff(pVM, pData->GCPhysVRAM, GCPhys);
3226 if (VBOX_SUCCESS(rc))
3227 {
3228#ifndef IN_RING3
3229 rc = PGMShwModifyPage(pVM, GCPtr, 1, X86_PTE_RW, ~(uint64_t)X86_PTE_RW);
3230 if (VBOX_SUCCESS(rc))
3231 return VINF_SUCCESS;
3232 else
3233 AssertMsgFailed(("PGMShwModifyPage -> rc=%d\n", rc));
3234#else /* IN_RING3 : We don't have any virtual page address of the access here. */
3235 Assert(GCPtr == 0);
3236 return VINF_SUCCESS;
3237#endif
3238 }
3239 else
3240 AssertMsgFailed(("PGMHandlerPhysicalPageTempOff -> rc=%d\n", rc));
3241
3242 return rc;
3243}
3244
3245
3246#ifdef IN_GC
3247/**
3248 * #PF Handler for VBE LFB access.
3249 *
3250 * @returns VBox status code (appropriate for GC return).
3251 * @param pVM VM Handle.
3252 * @param uErrorCode CPU Error code.
3253 * @param pRegFrame Trap register frame.
3254 * @param pvFault The fault address (cr2).
3255 * @param GCPhysFault The GC physical address corresponding to pvFault.
3256 * @param pvUser User argument, ignored.
3257 */
3258PDMBOTHCBDECL(int) vgaGCLFBAccessHandler(PVM pVM, RTGCUINT uErrorCode, PCPUMCTXCORE pRegFrame, RTGCPTR pvFault, RTGCPHYS GCPhysFault, void *pvUser)
3259{
3260 PVGASTATE pData = (PVGASTATE)pvUser;
3261 Assert(pData);
3262 Assert(GCPhysFault >= pData->GCPhysVRAM);
3263 AssertMsg(uErrorCode & X86_TRAP_PF_RW, ("uErrorCode=%#x\n", uErrorCode));
3264
3265 return vgaLFBAccess(pVM, pData, GCPhysFault, pvFault);
3266}
3267
3268#elif IN_RING0
3269
3270/**
3271 * #PF Handler for VBE LFB access.
3272 *
3273 * @returns VBox status code (appropriate for GC return).
3274 * @param pVM VM Handle.
3275 * @param uErrorCode CPU Error code.
3276 * @param pRegFrame Trap register frame.
3277 * @param pvFault The fault address (cr2).
3278 * @param GCPhysFault The GC physical address corresponding to pvFault.
3279 * @param pvUser User argument, ignored.
3280 */
3281PDMBOTHCBDECL(int) vgaR0LFBAccessHandler(PVM pVM, RTGCUINT uErrorCode, PCPUMCTXCORE pRegFrame, RTGCPTR pvFault, RTGCPHYS GCPhysFault, void *pvUser)
3282{
3283 PVGASTATE pData = (PVGASTATE)pvUser;
3284 Assert(pData);
3285 Assert(GCPhysFault >= pData->GCPhysVRAM);
3286 AssertMsg(uErrorCode & X86_TRAP_PF_RW, ("uErrorCode=%#x\n", uErrorCode));
3287
3288 return vgaLFBAccess(pVM, pData, GCPhysFault, pvFault);
3289}
3290
3291#else /* IN_RING3 */
3292
3293/**
3294 * HC access handler for the LFB.
3295 *
3296 * @returns VINF_SUCCESS if the handler have carried out the operation.
3297 * @returns VINF_PGM_HANDLER_DO_DEFAULT if the caller should carry out the access operation.
3298 * @param pVM VM Handle.
3299 * @param GCPhys The physical address the guest is writing to.
3300 * @param pvPhys The HC mapping of that address.
3301 * @param pvBuf What the guest is reading/writing.
3302 * @param cbBuf How much it's reading/writing.
3303 * @param enmAccessType The access type.
3304 * @param pvUser User argument.
3305 */
3306static DECLCALLBACK(int) vgaR3LFBAccessHandler(PVM pVM, RTGCPHYS GCPhys, void *pvPhys, void *pvBuf, size_t cbBuf, PGMACCESSTYPE enmAccessType, void *pvUser)
3307{
3308 PVGASTATE pData = (PVGASTATE)pvUser;
3309 int rc;
3310 Assert(pData);
3311 Assert(GCPhys >= pData->GCPhysVRAM);
3312 rc = vgaLFBAccess(pVM, pData, GCPhys, 0);
3313 if (VBOX_SUCCESS(rc))
3314 return VINF_PGM_HANDLER_DO_DEFAULT;
3315 AssertMsg(rc <= VINF_SUCCESS, ("rc=%Vrc\n", rc));
3316 return rc;
3317}
3318#endif /* IN_RING3 */
3319
3320
3321/* -=-=-=-=-=- Ring 3 -=-=-=-=-=- */
3322
3323#ifdef IN_RING3
3324
3325# ifdef VBE_NEW_DYN_LIST
3326/**
3327 * Port I/O Handler for VBE Extra OUT operations.
3328 *
3329 * @returns VBox status code.
3330 *
3331 * @param pDevIns The device instance.
3332 * @param pvUser User argument - ignored.
3333 * @param Port Port number used for the IN operation.
3334 * @param u32 The value to output.
3335 * @param cb The value size in bytes.
3336 */
3337PDMBOTHCBDECL(int) vbeIOPortWriteVBEExtra(PPDMDEVINS pDevIns, void *pvUser, RTIOPORT Port, uint32_t u32, unsigned cb)
3338{
3339 PVGASTATE pData = PDMINS2DATA(pDevIns, PVGASTATE);
3340 NOREF(pvUser);
3341 NOREF(Port);
3342
3343 if (cb == 2)
3344 {
3345 Log(("vbeIOPortWriteVBEExtra: addr=%#RX32\n", u32));
3346 pData->u16VBEExtraAddress = u32;
3347 return VINF_SUCCESS;
3348 }
3349
3350 Log(("vbeIOPortWriteVBEExtra: Ignoring invalid cb=%d writes to the VBE Extra port!!!\n", cb));
3351 return VINF_SUCCESS;
3352}
3353
3354
3355/**
3356 * Port I/O Handler for VBE Extra IN operations.
3357 *
3358 * @returns VBox status code.
3359 *
3360 * @param pDevIns The device instance.
3361 * @param pvUser User argument - ignored.
3362 * @param Port Port number used for the IN operation.
3363 * @param pu32 Where to store the result.
3364 * @param cb Number of bytes read.
3365 */
3366PDMBOTHCBDECL(int) vbeIOPortReadVBEExtra(PPDMDEVINS pDevIns, void *pvUser, RTIOPORT Port, uint32_t *pu32, unsigned cb)
3367{
3368 PVGASTATE pData = PDMINS2DATA(pDevIns, PVGASTATE);
3369 NOREF(pvUser);
3370 NOREF(Port);
3371
3372 if (pData->u16VBEExtraAddress == 0xffff)
3373 {
3374 Log(("vbeIOPortReadVBEExtra: Requested number of 64k video banks\n"));
3375 *pu32 = pData->vram_size / _64K;
3376 return VINF_SUCCESS;
3377 }
3378
3379 if ( pData->u16VBEExtraAddress >= pData->cbVBEExtraData
3380 || pData->u16VBEExtraAddress + cb > pData->cbVBEExtraData)
3381 {
3382 *pu32 = 0;
3383 Log(("vbeIOPortReadVBEExtra: Requested address is out of VBE data!!! Address=%#x(%d) cbVBEExtraData=%#x(%d)\n",
3384 pData->u16VBEExtraAddress, pData->u16VBEExtraAddress, pData->cbVBEExtraData, pData->cbVBEExtraData));
3385 return VINF_SUCCESS;
3386 }
3387
3388 if (cb == 1)
3389 {
3390 *pu32 = pData->pu8VBEExtraData[pData->u16VBEExtraAddress] & 0xFF;
3391
3392 Log(("vbeIOPortReadVBEExtra: cb=%#x %.*Vhxs\n", cb, cb, pu32));
3393 return VINF_SUCCESS;
3394 }
3395
3396 if (cb == 2)
3397 {
3398 *pu32 = pData->pu8VBEExtraData[pData->u16VBEExtraAddress]
3399 | pData->pu8VBEExtraData[pData->u16VBEExtraAddress + 1] << 8;
3400
3401 Log(("vbeIOPortReadVBEExtra: cb=%#x %.*Vhxs\n", cb, cb, pu32));
3402 return VINF_SUCCESS;
3403 }
3404 Log(("vbeIOPortReadVBEExtra: Invalid cb=%d read from the VBE Extra port!!!\n", cb));
3405 return VERR_IOM_IOPORT_UNUSED;
3406}
3407# endif /* VBE_NEW_DYN_LIST */
3408
3409
3410
3411
3412/* -=-=-=-=-=- Ring 3: VGA BIOS I/Os -=-=-=-=-=- */
3413
3414/**
3415 * Port I/O Handler for VGA BIOS IN operations.
3416 *
3417 * @returns VBox status code.
3418 *
3419 * @param pDevIns The device instance.
3420 * @param pvUser User argument - ignored.
3421 * @param Port Port number used for the IN operation.
3422 * @param pu32 Where to store the result.
3423 * @param cb Number of bytes read.
3424 */
3425static DECLCALLBACK(int) vgaIOPortReadBIOS(PPDMDEVINS pDevIns, void *pvUser, RTIOPORT Port, uint32_t *pu32, unsigned cb)
3426{
3427 NOREF(pDevIns);
3428 NOREF(pvUser);
3429 NOREF(Port);
3430 NOREF(pu32);
3431 NOREF(cb);
3432 return VERR_IOM_IOPORT_UNUSED;
3433}
3434
3435/**
3436 * Port I/O Handler for VGA BIOS OUT operations.
3437 *
3438 * @returns VBox status code.
3439 *
3440 * @param pDevIns The device instance.
3441 * @param pvUser User argument - ignored.
3442 * @param Port Port number used for the IN operation.
3443 * @param u32 The value to output.
3444 * @param cb The value size in bytes.
3445 */
3446static DECLCALLBACK(int) vgaIOPortWriteBIOS(PPDMDEVINS pDevIns, void *pvUser, RTIOPORT Port, uint32_t u32, unsigned cb)
3447{
3448 static int lastWasNotNewline = 0; /* We are only called in a single-threaded way */
3449 /*
3450 * VGA BIOS char printing.
3451 */
3452 if ( cb == 1
3453 && Port == VBE_PRINTF_PORT)
3454 {
3455#if 0
3456 switch (u32)
3457 {
3458 case '\r': Log(("vgabios: <return>\n")); break;
3459 case '\n': Log(("vgabios: <newline>\n")); break;
3460 case '\t': Log(("vgabios: <tab>\n")); break;
3461 default:
3462 Log(("vgabios: %c\n", u32));
3463 }
3464#else
3465 if (lastWasNotNewline == 0)
3466 Log(("vgabios: "));
3467 if (u32 != '\r') /* return - is only sent in conjunction with '\n' */
3468 Log(("%c", u32));
3469 if (u32 == '\n')
3470 lastWasNotNewline = 0;
3471 else
3472 lastWasNotNewline = 1;
3473#endif
3474 return VINF_SUCCESS;
3475 }
3476
3477 /* not in use. */
3478 return VINF_SUCCESS;
3479}
3480
3481
3482/* -=-=-=-=-=- Ring 3: IBase -=-=-=-=-=- */
3483
3484/**
3485 * Queries an interface to the driver.
3486 *
3487 * @returns Pointer to interface.
3488 * @returns NULL if the interface was not supported by the driver.
3489 * @param pInterface Pointer to this interface structure.
3490 * @param enmInterface The requested interface identification.
3491 * @thread Any thread.
3492 */
3493static DECLCALLBACK(void *) vgaPortQueryInterface(PPDMIBASE pInterface, PDMINTERFACE enmInterface)
3494{
3495 PVGASTATE pData = (PVGASTATE)((uintptr_t)pInterface - RT_OFFSETOF(VGASTATE, Base));
3496 switch (enmInterface)
3497 {
3498 case PDMINTERFACE_BASE:
3499 return &pData->Base;
3500 case PDMINTERFACE_DISPLAY_PORT:
3501 return &pData->Port;
3502 default:
3503 return NULL;
3504 }
3505}
3506
3507
3508/* -=-=-=-=-=- Ring 3: Dummy IDisplayConnector -=-=-=-=-=- */
3509
3510/**
3511 * Resize the display.
3512 * This is called when the resolution changes. This usually happens on
3513 * request from the guest os, but may also happen as the result of a reset.
3514 *
3515 * @param pInterface Pointer to this interface.
3516 * @param cx New display width.
3517 * @param cy New display height
3518 * @thread The emulation thread.
3519 */
3520static DECLCALLBACK(void) vgaDummyResize(PPDMIDISPLAYCONNECTOR pInterface, uint32_t bpp, void *pvVRAM, uint32_t cbLine, uint32_t cx, uint32_t cy)
3521{
3522}
3523
3524
3525/**
3526 * Update a rectangle of the display.
3527 * PDMIDISPLAYPORT::pfnUpdateDisplay is the caller.
3528 *
3529 * @param pInterface Pointer to this interface.
3530 * @param x The upper left corner x coordinate of the rectangle.
3531 * @param y The upper left corner y coordinate of the rectangle.
3532 * @param cx The width of the rectangle.
3533 * @param cy The height of the rectangle.
3534 * @thread The emulation thread.
3535 */
3536static DECLCALLBACK(void) vgaDummyUpdateRect(PPDMIDISPLAYCONNECTOR pInterface, uint32_t x, uint32_t y, uint32_t cx, uint32_t cy)
3537{
3538}
3539
3540
3541/**
3542 * Refresh the display.
3543 *
3544 * The interval between these calls is set by
3545 * PDMIDISPLAYPORT::pfnSetRefreshRate(). The driver should call
3546 * PDMIDISPLAYPORT::pfnUpdateDisplay() if it wishes to refresh the
3547 * display. PDMIDISPLAYPORT::pfnUpdateDisplay calls pfnUpdateRect with
3548 * the changed rectangles.
3549 *
3550 * @param pInterface Pointer to this interface.
3551 * @thread The emulation thread.
3552 */
3553static DECLCALLBACK(void) vgaDummyRefresh(PPDMIDISPLAYCONNECTOR pInterface)
3554{
3555}
3556
3557
3558/* -=-=-=-=-=- Ring 3: IDisplayPort -=-=-=-=-=- */
3559
3560/** Converts a display port interface pointer to a vga state pointer. */
3561#define IDISPLAYPORT_2_VGASTATE(pInterface) ( (PVGASTATE)((uintptr_t)pInterface - RT_OFFSETOF(VGASTATE, Port)) )
3562
3563
3564/**
3565 * Update the display with any changed regions.
3566 *
3567 * @param pInterface Pointer to this interface.
3568 * @see PDMIKEYBOARDPORT::pfnUpdateDisplay() for details.
3569 */
3570static DECLCALLBACK(void) vgaPortUpdateDisplay(PPDMIDISPLAYPORT pInterface)
3571{
3572 PVGASTATE pData = IDISPLAYPORT_2_VGASTATE(pInterface);
3573 PDMDEV_ASSERT_EMT(VGASTATE2DEVINS(pData));
3574
3575#ifdef DEBUG_sunlover
3576 LogFlow(("vgaPortUpdateDisplay\n"));
3577#endif /* DEBUG_sunlover */
3578
3579 /* This should be called only in non VBVA mode. */
3580
3581 vga_update_display(pData);
3582
3583 if (pData->fHaveDirtyBits)
3584 {
3585 PPDMDEVINS pDevIns = pData->pDevInsHC;
3586 PGMHandlerPhysicalReset(PDMDevHlpGetVM(pDevIns), pData->GCPhysVRAM);
3587 pData->fHaveDirtyBits = false;
3588 }
3589}
3590
3591
3592/**
3593 * Update the entire display.
3594 *
3595 * @param pInterface Pointer to this interface.
3596 * @see PDMIKEYBOARDPORT::pfnUpdateDisplayAll() for details.
3597 */
3598static DECLCALLBACK(void) vgaPortUpdateDisplayAll(PPDMIDISPLAYPORT pInterface)
3599{
3600 PVGASTATE pData = IDISPLAYPORT_2_VGASTATE(pInterface);
3601 PDMDEV_ASSERT_EMT(VGASTATE2DEVINS(pData));
3602
3603 /* This is called both in VBVA mode and normal modes. */
3604
3605#ifdef DEBUG_sunlover
3606 LogFlow(("vgaPortUpdateDisplayAll\n"));
3607#endif /* DEBUG_sunlover */
3608
3609 pData->graphic_mode = -1; /* force full update */
3610 vga_update_display(pData);
3611}
3612
3613
3614/**
3615 * Sets the refresh rate and restart the timer.
3616 *
3617 * @returns VBox status code.
3618 * @param pInterface Pointer to this interface.
3619 * @param cMilliesInterval Number of millies between two refreshes.
3620 * @see PDMIKEYBOARDPORT::pfnSetRefreshRate() for details.
3621 */
3622static DECLCALLBACK(int) vgaPortSetRefreshRate(PPDMIDISPLAYPORT pInterface, uint32_t cMilliesInterval)
3623{
3624 PVGASTATE pData = IDISPLAYPORT_2_VGASTATE(pInterface);
3625
3626 pData->cMilliesRefreshInterval = cMilliesInterval;
3627 if (cMilliesInterval)
3628 return TMTimerSetMillies(pData->RefreshTimer, cMilliesInterval);
3629 return TMTimerStop(pData->RefreshTimer);
3630}
3631
3632
3633/** @copydoc PDMIDISPLAYPORT::pfnQueryColorDepth */
3634static DECLCALLBACK(int) vgaPortQueryColorDepth(PPDMIDISPLAYPORT pInterface, uint32_t *pcBits)
3635{
3636 PVGASTATE pData = IDISPLAYPORT_2_VGASTATE(pInterface);
3637
3638 if (!pcBits)
3639 return VERR_INVALID_PARAMETER;
3640 *pcBits = vga_get_bpp(pData);
3641 return VINF_SUCCESS;
3642}
3643
3644
3645/**
3646 * Create a 32-bbp snapshot of the display.
3647 *
3648 * @param pInterface Pointer to this interface.
3649 * @param pvData Pointer the buffer to copy the bits to.
3650 * @param cbData Size of the buffer.
3651 * @param pcx Where to store the width of the bitmap. (optional)
3652 * @param pcy Where to store the height of the bitmap. (optional)
3653 * @param pcbData Where to store the actual size of the bitmap. (optional)
3654 * @see PDMIKEYBOARDPORT::pfnSnapshot() for details.
3655 */
3656static DECLCALLBACK(int) vgaPortSnapshot(PPDMIDISPLAYPORT pInterface, void *pvData, size_t cbData, uint32_t *pcx, uint32_t *pcy, size_t *pcbData)
3657{
3658 PPDMIDISPLAYCONNECTOR pConnector;
3659 PDMIDISPLAYCONNECTOR Connector;
3660 int32_t graphic_mode;
3661 size_t cbRequired;
3662 PVGASTATE pData = IDISPLAYPORT_2_VGASTATE(pInterface);
3663 PDMDEV_ASSERT_EMT(VGASTATE2DEVINS(pData));
3664 LogFlow(("vgaPortSnapshot: pvData=%p cbData=%d pcx=%p pcy=%p pcbData=%p\n", pvData, cbData, pcx, pcy, pcbData));
3665
3666 /*
3667 * Validate input.
3668 */
3669 if (!pvData)
3670 return VERR_INVALID_PARAMETER;
3671
3672 /*
3673 * Do a regular refresh first to resolve any pending resize issues.
3674 *
3675 * 20060317 It used to be pfnUpdateDisplay, but by VBVA design
3676 * only pfnUpdateDisplayAll is allowed to be called in VBVA mode.
3677 * Also since the goal here is to have updated display for screenshot,
3678 * the UpdateDisplayAll is even more logical to call. (sunlover)
3679 */
3680 pInterface->pfnUpdateDisplayAll(pInterface);
3681
3682 /*
3683 * Validate the buffer size.
3684 */
3685 cbRequired = RT_ALIGN_Z(pData->last_scr_width, 4) * pData->last_scr_height * 4;
3686 if (cbRequired > cbData)
3687 {
3688 Log(("vgaPortSnapshot: %d bytes are required, a buffer of %d bytes is profiled.\n", cbRequired, cbData));
3689 return VERR_BUFFER_OVERFLOW;
3690 }
3691
3692 /*
3693 * Temporarily replace the display connector interface with a fake one.
3694 */
3695 Connector.pu8Data = (uint8_t*)pvData;
3696 Connector.cBits = 32;
3697 Connector.cx = pData->pDrv->cx;
3698 Connector.cy = pData->pDrv->cy;
3699 Connector.cbScanline = RT_ALIGN_32(Connector.cx, 4) * 4;
3700 Connector.pfnRefresh = vgaDummyRefresh;
3701 Connector.pfnResize = vgaDummyResize;
3702 Connector.pfnUpdateRect = vgaDummyUpdateRect;
3703
3704 /* save & replace state data. */
3705 pConnector = pData->pDrv;
3706 pData->pDrv = &Connector;
3707 graphic_mode = pData->graphic_mode;
3708 pData->graphic_mode = -1; /* force a full refresh. */
3709
3710 /* make the snapshot. */
3711 vga_update_display(pData);
3712
3713 /* restore */
3714 pData->pDrv = pConnector;
3715 pData->graphic_mode = graphic_mode;
3716
3717 /*
3718 * Return the result.
3719 */
3720 if (pcx)
3721 *pcx = Connector.cx;
3722 if (pcy)
3723 *pcy = Connector.cy;
3724 if (pcbData)
3725 *pcbData = cbRequired;
3726 LogFlow(("vgaPortSnapshot: returns VINF_SUCCESS (cx=%d cy=%d cbData=%d)\n", Connector.cx, Connector.cy, cbRequired));
3727 return VINF_SUCCESS;
3728}
3729
3730
3731/**
3732 * Copy bitmap to the display.
3733 *
3734 * @param pInterface Pointer to this interface.
3735 * @param pvData Pointer to the bitmap bits.
3736 * @param x The upper left corner x coordinate of the destination rectangle.
3737 * @param y The upper left corner y coordinate of the destination rectangle.
3738 * @param cx The width of the source and destination rectangles.
3739 * @param cy The height of the source and destination rectangles.
3740 * @see PDMIDISPLAYPORT::pfnDisplayBlt() for details.
3741 */
3742static DECLCALLBACK(int) vgaPortDisplayBlt(PPDMIDISPLAYPORT pInterface, const void *pvData, uint32_t x, uint32_t y, uint32_t cx, uint32_t cy)
3743{
3744 PVGASTATE pData = IDISPLAYPORT_2_VGASTATE(pInterface);
3745 int rc = VINF_SUCCESS;
3746 PDMDEV_ASSERT_EMT(VGASTATE2DEVINS(pData));
3747 LogFlow(("vgaPortDisplayBlt: pvData=%p x=%d y=%d cx=%d cy=%d\n", pvData, x, y, cx, cy));
3748
3749 /*
3750 * Validate input.
3751 */
3752 if ( pvData
3753 && x < pData->pDrv->cx
3754 && cx <= pData->pDrv->cx
3755 && cx + x <= pData->pDrv->cx
3756 && y < pData->pDrv->cy
3757 && cy <= pData->pDrv->cy
3758 && cy + y <= pData->pDrv->cy)
3759 {
3760 /*
3761 * Determin bytes per pixel in the destination buffer.
3762 */
3763 size_t cbPixelDst = 0;
3764 switch (pData->pDrv->cBits)
3765 {
3766 case 8:
3767 cbPixelDst = 1;
3768 break;
3769 case 15:
3770 case 16:
3771 cbPixelDst = 2;
3772 break;
3773 case 24:
3774 cbPixelDst = 3;
3775 break;
3776 case 32:
3777 cbPixelDst = 4;
3778 break;
3779 default:
3780 rc = VERR_INVALID_PARAMETER;
3781 break;
3782 }
3783 if (VBOX_SUCCESS(rc))
3784 {
3785 /*
3786 * The blitting loop.
3787 */
3788 size_t cbLineSrc = RT_ALIGN_Z(cx, 4) * 4;
3789 uint8_t *pu8Src = (uint8_t *)pvData;
3790 size_t cbLineDst = pData->pDrv->cbScanline;
3791 uint8_t *pu8Dst = pData->pDrv->pu8Data + y * cbLineDst + x * cbPixelDst;
3792 uint32_t cyLeft = cy;
3793 vga_draw_line_func *pfnVgaDrawLine = vga_draw_line_table[VGA_DRAW_LINE32 * 4 + get_depth_index(pData->pDrv->cBits)];
3794 Assert(pfnVgaDrawLine);
3795 while (cyLeft-- > 0)
3796 {
3797 pfnVgaDrawLine(pData, pu8Dst, pu8Src, cx);
3798 pu8Dst += cbLineDst;
3799 pu8Src += cbLineSrc;
3800 }
3801
3802 /*
3803 * Invalidate the area.
3804 */
3805 pData->pDrv->pfnUpdateRect(pData->pDrv, x, y, cx, cy);
3806 }
3807 }
3808 else
3809 rc = VERR_INVALID_PARAMETER;
3810
3811 LogFlow(("vgaPortDisplayBlt: returns %Vrc\n", rc));
3812 return rc;
3813}
3814
3815static DECLCALLBACK(void) vgaPortUpdateDisplayRect (PPDMIDISPLAYPORT pInterface, int32_t x, int32_t y, uint32_t w, uint32_t h)
3816{
3817 uint32_t v;
3818 vga_draw_line_func *vga_draw_line;
3819
3820 uint32_t cbPixelDst;
3821 uint32_t cbLineDst;
3822 uint8_t *pu8Dst;
3823
3824 uint32_t cbPixelSrc;
3825 uint32_t cbLineSrc;
3826 uint8_t *pu8Src;
3827
3828 uint32_t u32OffsetSrc, u32Dummy;
3829
3830 PVGASTATE s = IDISPLAYPORT_2_VGASTATE(pInterface);
3831
3832#ifdef DEBUG_sunlover
3833 LogFlow(("vgaPortUpdateDisplayRect: %d,%d %dx%d\n", x, y, w, h));
3834#endif /* DEBUG_sunlover */
3835
3836 Assert(pInterface);
3837 Assert(s->pDrv);
3838 Assert(s->pDrv->pu8Data);
3839
3840 /* Check if there is something to do at all. */
3841 if (s->pvExtVRAMHC == s->pDrv->pu8Data)
3842 {
3843 /* The framebuffer uses the guest VRAM directly. */
3844#ifdef DEBUG_sunlover
3845 LogFlow(("vgaPortUpdateDisplayRect: nothing to do: s->pvExtVRAMHC %p, s->pDrv->pu8Data %p\n", s->pvExtVRAMHC, s->pDrv->pu8Data));
3846#endif /* DEBUG_sunlover */
3847 return;
3848 }
3849
3850 /* Correct negative x and y coordinates. */
3851 if (x < 0)
3852 {
3853 x += w; /* Compute xRight which is also the new width. */
3854 w = (x < 0) ? 0 : x;
3855 x = 0;
3856 }
3857
3858 if (y < 0)
3859 {
3860 y += h; /* Compute yBottom, which is also the new height. */
3861 h = (y < 0) ? 0 : y;
3862 y = 0;
3863 }
3864
3865 /* Also check if coords are greater than the display resolution. */
3866 if (x + w > s->pDrv->cx)
3867 {
3868#ifndef VBOX
3869 w = s->pDrv->cx > x? s->pDrv->cx - x: 0;
3870#else
3871 // x < 0 is not possible here
3872 w = s->pDrv->cx > (uint32_t)x? s->pDrv->cx - x: 0;
3873#endif
3874 }
3875
3876 if (y + h > s->pDrv->cy)
3877 {
3878#ifndef VBOX
3879 h = s->pDrv->cy > y? s->pDrv->cy - y: 0;
3880#else
3881 // y < 0 is not possible here
3882 h = s->pDrv->cy > (uint32_t)y? s->pDrv->cy - y: 0;
3883#endif
3884 }
3885
3886#ifdef DEBUG_sunlover
3887 LogFlow(("vgaPortUpdateDisplayRect: %d,%d %dx%d (corrected coords)\n", x, y, w, h));
3888#endif /* DEBUG_sunlover */
3889
3890 /* Check if there is something to do at all. */
3891 if (w == 0 || h == 0)
3892 {
3893 /* Empty rectangle. */
3894#ifdef DEBUG_sunlover
3895 LogFlow(("vgaPortUpdateDisplayRect: nothing to do: %dx%d\n", w, h));
3896#endif /* DEBUG_sunlover */
3897 return;
3898 }
3899
3900 /** @todo This method should be made universal and not only for VBVA.
3901 * VGA_DRAW_LINE* must be selected and src/dst address calculation
3902 * changed.
3903 */
3904
3905 /* Choose the rendering function. */
3906 switch(s->get_bpp(s))
3907 {
3908 default:
3909 case 0:
3910 AssertMsgFailed(("Unsupported BPP %d\n", s->get_bpp (s)));
3911 return;
3912 case 8:
3913 v = VGA_DRAW_LINE8;
3914 break;
3915 case 15:
3916 v = VGA_DRAW_LINE15;
3917 break;
3918 case 16:
3919 v = VGA_DRAW_LINE16;
3920 break;
3921 case 24:
3922 v = VGA_DRAW_LINE24;
3923 break;
3924 case 32:
3925 v = VGA_DRAW_LINE32;
3926 break;
3927 }
3928
3929 vga_draw_line = vga_draw_line_table[v * 4 + get_depth_index(s->pDrv->cBits)];
3930
3931 /* Compute source and destination addresses and pitches. */
3932 cbPixelDst = (s->pDrv->cBits + 7) / 8;
3933 cbLineDst = s->pDrv->cbScanline;
3934 pu8Dst = s->pDrv->pu8Data + y * cbLineDst + x * cbPixelDst;
3935
3936 cbPixelSrc = (s->get_bpp(s) + 7) / 8;
3937 s->get_offsets (s, &cbLineSrc, &u32OffsetSrc, &u32Dummy);
3938
3939 /* Assume that rendering is performed only on visible part of VRAM.
3940 * This is true because coordinates were verified.
3941 */
3942 pu8Src = s->pvExtVRAMHC? (uint8_t *)s->pvExtVRAMHC: s->vram_ptrHC;
3943 pu8Src += u32OffsetSrc + y * cbLineSrc + x * cbPixelSrc;
3944
3945 /* Render VRAM to framebuffer. */
3946
3947#ifdef DEBUG_sunlover
3948 LogFlow(("vgaPortUpdateDisplayRect: dst: %p, %d, %d. src: %p, %d, %d\n", pu8Dst, cbLineDst, cbPixelDst, pu8Src, cbLineSrc, cbPixelSrc));
3949#endif /* DEBUG_sunlover */
3950
3951 while (h-- > 0)
3952 {
3953 vga_draw_line (s, pu8Dst, pu8Src, w);
3954 pu8Dst += cbLineDst;
3955 pu8Src += cbLineSrc;
3956 }
3957
3958#ifdef DEBUG_sunlover
3959 LogFlow(("vgaPortUpdateDisplayRect: completed.\n"));
3960#endif /* DEBUG_sunlover */
3961}
3962
3963static DECLCALLBACK(int) vgaPortSetupVRAM (PPDMIDISPLAYPORT pInterface, void *pvBuffer, uint32_t cbBuffer)
3964{
3965 int rc = VINF_SUCCESS;
3966 PVGASTATE s = IDISPLAYPORT_2_VGASTATE(pInterface);
3967 PPDMDEVINS pDevIns;
3968 PVM pVM;
3969
3970 uint32_t u32OverlayOffset = 0;
3971
3972 LogFlow(("vgaPortSetupVRAM: pvBuffer = %p, cbBuffer = 0x%08X, s->vram_ptrHC = %p\n", pvBuffer, cbBuffer, s->vram_ptrHC));
3973
3974 /* Check input values, Main::Display rely on that. */
3975 if (ALIGNP(pvBuffer, PAGE_SIZE) != pvBuffer)
3976 {
3977 LogFlow(("vgaPortSetupVRAM: Not aligned address.\n"));
3978 return VERR_INVALID_PARAMETER;
3979 }
3980
3981 pDevIns = s->pDevInsHC;
3982 Assert(pDevIns);
3983
3984 pVM = PDMDevHlpGetVM(pDevIns);
3985 Assert(pVM);
3986
3987 if (s->pvExtVRAMHC != NULL)
3988 {
3989 if (s->pvExtVRAMHC != s->vram_ptrHC)
3990 {
3991 /* @todo */
3992 AssertMsgFailed(("Too early to call SetupVRAM with external VRAM\n"));
3993
3994 /* Remove existing overlay mapping. */
3995// MMR3PPhysUnregisterOverlay (pVM, s->GCPhysVRAM, u32OverlayOffset, s->pvExtVRAMHC, s->cbExtVRAM);
3996
3997 /* Copy the content of the external VRAM to the device VRAM. */
3998 memcpy (s->vram_ptrHC + u32OverlayOffset, s->pvExtVRAMHC, s->cbExtVRAM);
3999 }
4000
4001 s->pvExtVRAMHC = NULL;
4002 s->cbExtVRAM = 0;
4003 }
4004
4005 if (pvBuffer == NULL)
4006 {
4007 /* Resetting VRAM to use only vga device allocated memory. */
4008 Assert (cbBuffer == 0);
4009 return VINF_SUCCESS;
4010 }
4011
4012 if (pvBuffer == s->vram_ptrHC)
4013 {
4014 /* Caller will use the guest VRAM directly. Do nothing. Ignore cbBuffer. */
4015 cbBuffer = 0;
4016 rc = VINF_SUCCESS;
4017 }
4018 else
4019 {
4020 /* Register new overlay. */
4021
4022 /* @todo */
4023 AssertMsgFailed(("Too early to call SetupVRAM with external VRAM\n"));
4024
4025 cbBuffer = RT_ALIGN_32(cbBuffer, PAGE_SIZE);
4026
4027// rc = MMR3PhysRegisterOverlay(pVM, s->GCPhysVRAM, u32OverlayOffset, pvBuffer, cbBuffer, "FBVRam");
4028 }
4029
4030 if (VBOX_SUCCESS (rc))
4031 {
4032 s->pvExtVRAMHC = pvBuffer;
4033 s->cbExtVRAM = cbBuffer;
4034 }
4035
4036 return rc;
4037}
4038
4039
4040static DECLCALLBACK(void) vgaTimerRefresh(PPDMDEVINS pDevIns, PTMTIMER pTimer)
4041{
4042 PVGASTATE pData = PDMINS2DATA(pDevIns, PVGASTATE);
4043 if (pData->pDrv)
4044 pData->pDrv->pfnRefresh(pData->pDrv);
4045 if (pData->cMilliesRefreshInterval)
4046 TMTimerSetMillies(pTimer, pData->cMilliesRefreshInterval);
4047}
4048
4049
4050/* -=-=-=-=-=- Ring 3: PCI Device -=-=-=-=-=- */
4051
4052/**
4053 * Callback function for mapping an PCI I/O region.
4054 *
4055 * @return VBox status code.
4056 * @param pPciDev Pointer to PCI device. Use pPciDev->pDevIns to get the device instance.
4057 * @param iRegion The region number.
4058 * @param GCPhysAddress Physical address of the region. If iType is PCI_ADDRESS_SPACE_IO, this is an
4059 * I/O port, else it's a physical address.
4060 * This address is *NOT* relative to pci_mem_base like earlier!
4061 * @param enmType One of the PCI_ADDRESS_SPACE_* values.
4062 */
4063static DECLCALLBACK(int) vgaR3IORegionMap(PPCIDEVICE pPciDev, /*unsigned*/ int iRegion, RTGCPHYS GCPhysAddress, uint32_t cb, PCIADDRESSSPACE enmType)
4064{
4065 int rc;
4066 PVGASTATE pData = PDMINS2DATA(pPciDev->pDevIns, PVGASTATE);
4067 LogFlow(("vgaR3IORegionMap: iRegion=%d GCPhysAddress=%VGp cb=%#x enmType=%d\n", iRegion, GCPhysAddress, cb, enmType));
4068
4069 /*
4070 * VRam mapping.
4071 */
4072 if (iRegion == 0 && enmType == PCI_ADDRESS_SPACE_MEM_PREFETCH)
4073 {
4074 /*
4075 * Register and lock the VRAM.
4076 *
4077 * Windows usually re-initializes the PCI devices, so we have to check whether the memory was
4078 * already registered before trying to do that all over again.
4079 */
4080 PVM pVM = PDMDevHlpGetVM(pPciDev->pDevIns);
4081 if (pData->GCPhysVRAM)
4082 {
4083 AssertMsg(pData->GCPhysVRAM == GCPhysAddress,
4084 ("The Guest OS relocated our LFB! old GCPhysVRAM=%VGp new GCPhysAddress=%VGp\n",
4085 pData->GCPhysVRAM, GCPhysAddress));
4086 rc = VINF_SUCCESS;
4087 }
4088 else
4089 {
4090 /*
4091 * Register and lock the VRAM.
4092 */
4093 rc = MMR3PhysRegister(pVM, pData->vram_ptrHC, GCPhysAddress, pData->vram_size, MM_RAM_FLAGS_MMIO2, "VRam");
4094 if (VBOX_SUCCESS(rc))
4095 {
4096 if (!pData->GCPhysVRAM)
4097 rc = PGMR3HandlerPhysicalRegister(pVM, PGMPHYSHANDLERTYPE_PHYSICAL_WRITE,
4098 GCPhysAddress, GCPhysAddress + (pData->vram_size - 1),
4099 vgaR3LFBAccessHandler, pData,
4100 g_DeviceVga.szR0Mod, "vgaR0LFBAccessHandler", pData->pDevInsHC->pvInstanceDataHC,
4101 g_DeviceVga.szGCMod, "vgaGCLFBAccessHandler", pData->pDevInsHC->pvInstanceDataGC,
4102 "VGA LFB");
4103 if (VBOX_SUCCESS(rc))
4104 {
4105 /*
4106 * Map the first 256KB of the VRAM into GC for GC VGA support.
4107 */
4108 RTGCPTR GCPtr;
4109 rc = MMR3HyperMapGCPhys(pVM, GCPhysAddress, VGA_MAPPING_SIZE, "VGA VRam", &GCPtr);
4110 if (VBOX_SUCCESS(rc))
4111 {
4112 MMR3HyperReserve(pVM, PAGE_SIZE, "fence", NULL);
4113
4114 pData->vram_ptrGC = GCPtr;
4115 pData->GCPhysVRAM = GCPhysAddress;
4116 return VINF_SUCCESS;
4117 }
4118 AssertMsgFailed(("MMR3HyperMapGCPhys failed, rc=%Vrc\n", rc));
4119 }
4120 else
4121 AssertMsgFailed(("Failed to register write handler for VRAM! rc=%Vrc\n", rc));
4122 }
4123 else
4124 AssertReleaseMsgFailed(("Failed to register VRAM! rc=%Vra\n", rc));
4125 }
4126 return rc;
4127 }
4128 else
4129 AssertReleaseMsgFailed(("Huh!?! iRegion=%d enmType=%d\n", iRegion, enmType));
4130 return VERR_INTERNAL_ERROR;
4131}
4132
4133
4134/* -=-=-=-=-=- Ring3: Misc Wrappers -=-=-=-=-=- */
4135
4136/**
4137 * Saves a state of the VGA device.
4138 *
4139 * @returns VBox status code.
4140 * @param pDevIns The device instance.
4141 * @param pSSMHandle The handle to save the state to.
4142 */
4143static DECLCALLBACK(int) vgaR3SaveExec(PPDMDEVINS pDevIns, PSSMHANDLE pSSMHandle)
4144{
4145 vga_save(pSSMHandle, PDMINS2DATA(pDevIns, PVGASTATE));
4146 return VINF_SUCCESS;
4147}
4148
4149
4150/**
4151 * Loads a saved VGA device state.
4152 *
4153 * @returns VBox status code.
4154 * @param pDevIns The device instance.
4155 * @param pSSMHandle The handle to the saved state.
4156 * @param u32Version The data unit version number.
4157 */
4158static DECLCALLBACK(int) vgaR3LoadExec(PPDMDEVINS pDevIns, PSSMHANDLE pSSMHandle, uint32_t u32Version)
4159{
4160 if (vga_load(pSSMHandle, PDMINS2DATA(pDevIns, PVGASTATE), u32Version))
4161 return VERR_SSM_UNSUPPORTED_DATA_UNIT_VERSION;
4162 return VINF_SUCCESS;
4163}
4164
4165
4166/* -=-=-=-=-=- Ring 3: Device callbacks -=-=-=-=-=- */
4167
4168/**
4169 * Reset notification.
4170 *
4171 * @returns VBox status.
4172 * @param pDevIns The device instance data.
4173 */
4174static DECLCALLBACK(void) vgaR3Reset(PPDMDEVINS pDevIns)
4175{
4176 PVGASTATE pData = PDMINS2DATA(pDevIns, PVGASTATE);
4177 char *pchStart;
4178 char *pchEnd;
4179 LogFlow(("vgaReset\n"));
4180
4181 /* Clear the VRAM ourselves. */
4182 if (pData->vram_ptrHC && pData->vram_size)
4183 {
4184#ifdef LOG_ENABLED /** @todo separate function. */
4185 /* First dump the textmode contents to the log; handy for capturing Windows blue screens. */
4186 uint8_t graphic_mode;
4187 VGAState *s = pData;
4188
4189 if (!(s->ar_index & 0x20)) {
4190 graphic_mode = GMODE_BLANK;
4191 } else {
4192 graphic_mode = s->gr[6] & 1;
4193 }
4194 switch(graphic_mode)
4195 case GMODE_TEXT:
4196 {
4197 int cw, height, width, cheight, cx_min, cx_max, cy, cx;
4198 int x_incr;
4199 uint8_t *s1, *src, ch, cattr;
4200 int line_offset;
4201 uint16_t ch_attr;
4202
4203 line_offset = s->line_offset;
4204 s1 = s->CTXSUFF(vram_ptr) + (s->start_addr * 4);
4205
4206 /* total width & height */
4207 cheight = (s->cr[9] & 0x1f) + 1;
4208 cw = 8;
4209 if (!(s->sr[1] & 0x01))
4210 cw = 9;
4211 if (s->sr[1] & 0x08)
4212 cw = 16; /* NOTE: no 18 pixel wide */
4213 x_incr = cw * ((s->pDrv->cBits + 7) >> 3);
4214 width = (s->cr[0x01] + 1);
4215 if (s->cr[0x06] == 100) {
4216 /* ugly hack for CGA 160x100x16 - explain me the logic */
4217 height = 100;
4218 } else {
4219 height = s->cr[0x12] |
4220 ((s->cr[0x07] & 0x02) << 7) |
4221 ((s->cr[0x07] & 0x40) << 3);
4222 height = (height + 1) / cheight;
4223 }
4224 if ((height * width) > CH_ATTR_SIZE) {
4225 /* better than nothing: exit if transient size is too big */
4226 break;
4227 }
4228 RTLogPrintf("VGA textmode BEGIN (%dx%d):\n\n", height, width);
4229 for(cy = 0; cy < height; cy++) {
4230 src = s1;
4231 cx_min = width;
4232 cx_max = -1;
4233 for(cx = 0; cx < width; cx++) {
4234 ch_attr = *(uint16_t *)src;
4235 if (cx < cx_min)
4236 cx_min = cx;
4237 if (cx > cx_max)
4238 cx_max = cx;
4239# ifdef WORDS_BIGENDIAN
4240 ch = ch_attr >> 8;
4241 cattr = ch_attr & 0xff;
4242# else
4243 ch = ch_attr & 0xff;
4244 cattr = ch_attr >> 8;
4245# endif
4246 RTLogPrintf("%c", ch);
4247
4248 src += 4;
4249 }
4250 if (cx_max != -1)
4251 RTLogPrintf("\n");
4252
4253 s1 += line_offset;
4254 }
4255 RTLogPrintf("VGA textmode END:\n\n");
4256 }
4257
4258#endif
4259 memset(pData->vram_ptrHC, 0, pData->vram_size);
4260 }
4261
4262 /*
4263 * Zero most of it.
4264 *
4265 * Unlike vga_reset we're leaving out a few members which believe must
4266 * remain unchanged....
4267 */
4268 /* 1st part. */
4269 pchStart = (char *)&pData->latch;
4270 pchEnd = (char *)&pData->invalidated_y_table;
4271 memset(pchStart, 0, pchEnd - pchStart);
4272
4273 /* 2nd part. */
4274 pchStart = (char *)&pData->last_palette;
4275 pchEnd = (char *)&pData->u32Marker;
4276 memset(pchStart, 0, pchEnd - pchStart);
4277
4278
4279 /*
4280 * Restore and re-init some bits.
4281 */
4282 pData->get_bpp = vga_get_bpp;
4283 pData->get_offsets = vga_get_offsets;
4284 pData->get_resolution = vga_get_resolution;
4285 pData->graphic_mode = -1; /* Force full update. */
4286#ifdef CONFIG_BOCHS_VBE
4287 pData->vbe_regs[VBE_DISPI_INDEX_ID] = VBE_DISPI_ID0;
4288 pData->vbe_bank_mask = ((pData->vram_size >> 16) - 1);
4289#endif /* CONFIG_BOCHS_VBE */
4290
4291 /*
4292 * Reset the LBF mapping.
4293 */
4294 pData->fLFBUpdated = false;
4295 if ( ( pData->fGCEnabled
4296 || pData->fR0Enabled)
4297 && pData->GCPhysVRAM)
4298 {
4299 int rc = PGMHandlerPhysicalReset(PDMDevHlpGetVM(pDevIns), pData->GCPhysVRAM);
4300 AssertRC(rc);
4301 }
4302
4303 /* notify port handler */
4304 if (pData->pDrv)
4305 pData->pDrv->pfnReset(pData->pDrv);
4306}
4307
4308
4309/**
4310 * Device relocation callback.
4311 *
4312 * @param pDevIns Pointer to the device instance.
4313 * @param offDelta The relocation delta relative to the old location.
4314 *
4315 * @see FNPDMDEVRELOCATE for details.
4316 */
4317static DECLCALLBACK(void) vgaR3Relocate(PPDMDEVINS pDevIns, RTGCINTPTR offDelta)
4318{
4319 if (offDelta)
4320 {
4321 PVGASTATE pData = PDMINS2DATA(pDevIns, PVGASTATE);
4322 LogFlow(("vgaRelocate: offDelta = %08X\n", offDelta));
4323
4324 pData->GCPtrLFBHandler += offDelta;
4325 pData->vram_ptrGC += offDelta;
4326 }
4327}
4328
4329
4330/**
4331 * Attach command.
4332 *
4333 * This is called to let the device attach to a driver for a specified LUN
4334 * during runtime. This is not called during VM construction, the device
4335 * constructor have to attach to all the available drivers.
4336 *
4337 * This is like plugging in the monitor after turning on the PC.
4338 *
4339 * @returns VBox status code.
4340 * @param pDevIns The device instance.
4341 * @param iLUN The logical unit which is being detached.
4342 */
4343static DECLCALLBACK(int) vgaAttach(PPDMDEVINS pDevIns, unsigned iLUN)
4344{
4345 PVGASTATE pData = PDMINS2DATA(pDevIns, PVGASTATE);
4346 switch (iLUN)
4347 {
4348 /* LUN #0: Display port. */
4349 case 0:
4350 {
4351 int rc = PDMDevHlpDriverAttach(pDevIns, iLUN, &pData->Base, &pData->pDrvBase, "Display Port");
4352 if (VBOX_SUCCESS(rc))
4353 {
4354 pData->pDrv = (PDMIDISPLAYCONNECTOR*)pData->pDrvBase->pfnQueryInterface(pData->pDrvBase, PDMINTERFACE_DISPLAY_CONNECTOR);
4355 if (pData->pDrv)
4356 {
4357 if ( pData->pDrv->pu8Data
4358 && pData->pDrv->pfnRefresh
4359 && pData->pDrv->pfnResize
4360 && pData->pDrv->pfnUpdateRect)
4361 rc = VINF_SUCCESS;
4362 else
4363 {
4364 Assert(pData->pDrv->pu8Data);
4365 Assert(pData->pDrv->pfnRefresh);
4366 Assert(pData->pDrv->pfnResize);
4367 Assert(pData->pDrv->pfnUpdateRect);
4368 pData->pDrv = NULL;
4369 pData->pDrvBase = NULL;
4370 rc = VERR_INTERNAL_ERROR;
4371 }
4372 }
4373 else
4374 {
4375 AssertMsgFailed(("LUN #0 doesn't have a display connector interface! rc=%Vrc\n", rc));
4376 pData->pDrvBase = NULL;
4377 rc = VERR_PDM_MISSING_INTERFACE;
4378 }
4379 }
4380 else if (rc == VERR_PDM_NO_ATTACHED_DRIVER)
4381 {
4382 Log(("%s/%d: warning: no driver attached to LUN #0!\n", pDevIns->pDevReg->szDeviceName, pDevIns->iInstance));
4383 rc = VINF_SUCCESS;
4384 }
4385 else
4386 AssertMsgFailed(("Failed to attach LUN #0! rc=%Vrc\n", rc));
4387 return rc;
4388 }
4389
4390 default:
4391 AssertMsgFailed(("Invalid LUN #%d\n", iLUN));
4392 return VERR_PDM_NO_SUCH_LUN;
4393 }
4394}
4395
4396
4397/**
4398 * Detach notification.
4399 *
4400 * This is called when a driver is detaching itself from a LUN of the device.
4401 * The device should adjust it's state to reflect this.
4402 *
4403 * This is like unplugging the monitor while the PC is still running.
4404 *
4405 * @param pDevIns The device instance.
4406 * @param iLUN The logical unit which is being detached.
4407 */
4408static DECLCALLBACK(void) vgaDetach(PPDMDEVINS pDevIns, unsigned iLUN)
4409{
4410 /*
4411 * Reset the interfaces and update the controller state.
4412 */
4413 PVGASTATE pData = PDMINS2DATA(pDevIns, PVGASTATE);
4414 switch (iLUN)
4415 {
4416 /* LUN #0: Display port. */
4417 case 0:
4418 pData->pDrv = NULL;
4419 pData->pDrvBase = NULL;
4420 break;
4421
4422 default:
4423 AssertMsgFailed(("Invalid LUN #%d\n", iLUN));
4424 break;
4425 }
4426}
4427
4428
4429
4430/**
4431 * Construct a VGA device instance for a VM.
4432 *
4433 * @returns VBox status.
4434 * @param pDevIns The device instance data.
4435 * If the registration structure is needed, pDevIns->pDevReg points to it.
4436 * @param iInstance Instance number. Use this to figure out which registers and such to use.
4437 * The device number is also found in pDevIns->iInstance, but since it's
4438 * likely to be freqently used PDM passes it as parameter.
4439 * @param pCfgHandle Configuration node handle for the device. Use this to obtain the configuration
4440 * of the device instance. It's also found in pDevIns->pCfgHandle, but like
4441 * iInstance it's expected to be used a bit in this function.
4442 */
4443static DECLCALLBACK(int) vgaR3Construct(PPDMDEVINS pDevIns, int iInstance, PCFGMNODE pCfgHandle)
4444{
4445 static bool fExpandDone = false;
4446 bool f;
4447 int rc;
4448 unsigned i;
4449 PVGASTATE pData = PDMINS2DATA(pDevIns, PVGASTATE);
4450 PVM pVM = PDMDevHlpGetVM(pDevIns);
4451#ifdef VBE_NEW_DYN_LIST
4452 uint32_t cCustomModes;
4453 uint32_t cyReduction;
4454 PVBEHEADER pVBEDataHdr;
4455 ModeInfoListItem *pCurMode;
4456 unsigned cb;
4457#endif
4458 Assert(iInstance == 0);
4459 Assert(pVM);
4460
4461 /*
4462 * Init static data.
4463 */
4464 if (!fExpandDone)
4465 {
4466 fExpandDone = true;
4467 vga_init_expand();
4468 }
4469
4470 /*
4471 * Validate configuration.
4472 */
4473 if (!CFGMR3AreValuesValid(pCfgHandle, "VRamSize\0"
4474 "MonitorCount\0"
4475 "GCEnabled\0"
4476 "R0Enabled\0"
4477 "CustomVideoModes\0"
4478 "HeightReduction\0"
4479 "CustomVideoMode1\0"
4480 "CustomVideoMode2\0"
4481 "CustomVideoMode3\0"
4482 "CustomVideoMode4\0"
4483 "CustomVideoMode5\0"
4484 "CustomVideoMode6\0"
4485 "CustomVideoMode7\0"
4486 "CustomVideoMode8\0"
4487 "CustomVideoMode9\0"
4488 "CustomVideoMode10\0"
4489 "CustomVideoMode11\0"
4490 "CustomVideoMode12\0"
4491 "CustomVideoMode13\0"
4492 "CustomVideoMode14\0"
4493 "CustomVideoMode15\0"
4494 "CustomVideoMode16"))
4495 return PDMDEV_SET_ERROR(pDevIns, VERR_PDM_DEVINS_UNKNOWN_CFG_VALUES,
4496 N_("Invalid configuration for vga device"));
4497
4498 /*
4499 * Init state data.
4500 */
4501 rc = CFGMR3QueryU32(pCfgHandle, "VRamSize", &pData->vram_size);
4502 if (VBOX_FAILURE(rc) || !pData->vram_size)
4503 pData->vram_size = VGA_VRAM_DEFAULT;
4504 else if (pData->vram_size > VGA_VRAM_MAX)
4505 {
4506 AssertMsgFailed(("vram_size=%d max=%d\n", pData->vram_size, VGA_VRAM_MAX));
4507 pData->vram_size = VGA_VRAM_MAX;
4508 }
4509 else if (pData->vram_size < VGA_VRAM_MIN)
4510 {
4511 AssertMsgFailed(("vram_size=%d min=%d\n", pData->vram_size, VGA_VRAM_MIN));
4512 pData->vram_size = RT_ALIGN_32(pData->vram_size, _1M);
4513 }
4514 Log(("VGA: VRamSize=%#x\n", pData->vram_size));
4515
4516 pData->fGCEnabled = true;
4517 rc = CFGMR3QueryBool(pCfgHandle, "GCEnabled", &f);
4518 if (VBOX_SUCCESS(rc) && !f)
4519 pData->fGCEnabled = false;
4520 Log(("VGA: fGCEnabled=%d\n", pData->fGCEnabled));
4521
4522 pData->fR0Enabled = true;
4523 rc = CFGMR3QueryBool(pCfgHandle, "R0Enabled", &f);
4524 if (VBOX_SUCCESS(rc) && !f)
4525 pData->fR0Enabled = false;
4526 Log(("VGA: fR0Enabled=%d\n", pData->fR0Enabled));
4527
4528 pData->pDevInsHC = pDevIns;
4529
4530 vgaR3Reset(pDevIns);
4531
4532 /* The PCI devices configuration. */
4533 pData->Dev.config[0x00] = 0xee; /* PCI vendor, just a free bogus value */
4534 pData->Dev.config[0x01] = 0x80;
4535
4536 pData->Dev.config[0x02] = 0xef; /* Device ID */
4537 pData->Dev.config[0x03] = 0xbe;
4538
4539 pData->Dev.config[0x0a] = 0x00; /* VGA controller */
4540 pData->Dev.config[0x0b] = 0x03;
4541 pData->Dev.config[0x0e] = 0x00; /* header_type */
4542
4543 /* The LBF access handler - error handling is better here than in the map function. */
4544 rc = PDMR3GetSymbolGCLazy(pVM, pDevIns->pDevReg->szGCMod, "vgaGCLFBAccessHandler", &pData->GCPtrLFBHandler);
4545 if (VBOX_FAILURE(rc))
4546 {
4547 AssertReleaseMsgFailed(("PDMR3GetSymbolGC(, %s, \"vgaGCLFBAccessHandler\",) -> %Vrc\n", pDevIns->pDevReg->szGCMod, rc));
4548 return rc;
4549 }
4550
4551 /* the interfaces. */
4552 pData->Base.pfnQueryInterface = vgaPortQueryInterface;
4553
4554 pData->Port.pfnUpdateDisplay = vgaPortUpdateDisplay;
4555 pData->Port.pfnUpdateDisplayAll = vgaPortUpdateDisplayAll;
4556 pData->Port.pfnQueryColorDepth = vgaPortQueryColorDepth;
4557 pData->Port.pfnSetRefreshRate = vgaPortSetRefreshRate;
4558 pData->Port.pfnSnapshot = vgaPortSnapshot;
4559 pData->Port.pfnDisplayBlt = vgaPortDisplayBlt;
4560 pData->Port.pfnUpdateDisplayRect= vgaPortUpdateDisplayRect;
4561 pData->Port.pfnSetupVRAM = vgaPortSetupVRAM;
4562
4563
4564 /*
4565 * Register I/O ports, ROM and save state.
4566 */
4567 rc = PDMDevHlpIOPortRegister(pDevIns, 0x3c0, 16, NULL, vgaIOPortWrite, vgaIOPortRead, NULL, NULL, "VGA - 3c0");
4568 if (VBOX_FAILURE(rc))
4569 return rc;
4570 rc = PDMDevHlpIOPortRegister(pDevIns, 0x3b4, 2, NULL, vgaIOPortWrite, vgaIOPortRead, NULL, NULL, "VGA - 3b4");
4571 if (VBOX_FAILURE(rc))
4572 return rc;
4573 rc = PDMDevHlpIOPortRegister(pDevIns, 0x3ba, 1, NULL, vgaIOPortWrite, vgaIOPortRead, NULL, NULL, "VGA - 3ba");
4574 if (VBOX_FAILURE(rc))
4575 return rc;
4576 rc = PDMDevHlpIOPortRegister(pDevIns, 0x3d4, 2, NULL, vgaIOPortWrite, vgaIOPortRead, NULL, NULL, "VGA - 3d4");
4577 if (VBOX_FAILURE(rc))
4578 return rc;
4579 rc = PDMDevHlpIOPortRegister(pDevIns, 0x3da, 1, NULL, vgaIOPortWrite, vgaIOPortRead, NULL, NULL, "VGA - 3da");
4580 if (VBOX_FAILURE(rc))
4581 return rc;
4582
4583#ifdef CONFIG_BOCHS_VBE
4584 rc = PDMDevHlpIOPortRegister(pDevIns, 0x1ce, 1, NULL, vgaIOPortWriteVBEIndex, vgaIOPortReadVBEIndex, NULL, NULL, "VGA/VBE - Index");
4585 if (VBOX_FAILURE(rc))
4586 return rc;
4587 rc = PDMDevHlpIOPortRegister(pDevIns, 0x1cf, 1, NULL, vgaIOPortWriteVBEData, vgaIOPortReadVBEData, NULL, NULL, "VGA/VBE - Data");
4588 if (VBOX_FAILURE(rc))
4589 return rc;
4590#if 0
4591 /* This now causes conflicts with Win2k & XP; it is not aware this range is taken
4592 and tries to map other devices there */
4593 /* Old Bochs. */
4594 rc = PDMDevHlpIOPortRegister(pDevIns, 0xff80, 1, NULL, vgaIOPortWriteVBEIndex, vgaIOPortReadVBEIndex, "VGA/VBE - Index Old");
4595 if (VBOX_FAILURE(rc))
4596 return rc;
4597 rc = PDMDevHlpIOPortRegister(pDevIns, 0xff81, 1, NULL, vgaIOPortWriteVBEData, vgaIOPortReadVBEData, "VGA/VBE - Data Old");
4598 if (VBOX_FAILURE(rc))
4599 return rc;
4600#endif
4601#endif /* CONFIG_BOCHS_VBE */
4602
4603 /* guest context extension */
4604 if (pData->fGCEnabled)
4605 {
4606 rc = PDMDevHlpIOPortRegisterGC(pDevIns, 0x3c0, 16, 0, "vgaIOPortWrite", "vgaIOPortRead", NULL, NULL, "VGA - 3c0 (GC)");
4607 if (VBOX_FAILURE(rc))
4608 return rc;
4609 rc = PDMDevHlpIOPortRegisterGC(pDevIns, 0x3b4, 2, 0, "vgaIOPortWrite", "vgaIOPortRead", NULL, NULL, "VGA - 3b4 (GC)");
4610 if (VBOX_FAILURE(rc))
4611 return rc;
4612 rc = PDMDevHlpIOPortRegisterGC(pDevIns, 0x3ba, 1, 0, "vgaIOPortWrite", "vgaIOPortRead", NULL, NULL, "VGA - 3ba (GC)");
4613 if (VBOX_FAILURE(rc))
4614 return rc;
4615 rc = PDMDevHlpIOPortRegisterGC(pDevIns, 0x3d4, 2, 0, "vgaIOPortWrite", "vgaIOPortRead", NULL, NULL, "VGA - 3d4 (GC)");
4616 if (VBOX_FAILURE(rc))
4617 return rc;
4618 rc = PDMDevHlpIOPortRegisterGC(pDevIns, 0x3da, 1, 0, "vgaIOPortWrite", "vgaIOPortRead", NULL, NULL, "VGA - 3da (GC)");
4619 if (VBOX_FAILURE(rc))
4620 return rc;
4621#ifdef CONFIG_BOCHS_VBE
4622 rc = PDMDevHlpIOPortRegisterGC(pDevIns, 0x1ce, 1, 0, "vgaIOPortWriteVBEIndex", "vgaIOPortReadVBEIndex", NULL, NULL, "VGA/VBE - Index (GC)");
4623 if (VBOX_FAILURE(rc))
4624 return rc;
4625 rc = PDMDevHlpIOPortRegisterGC(pDevIns, 0x1cf, 1, 0, "vgaIOPortWriteVBEData", "vgaIOPortReadVBEData", NULL, NULL, "VGA/VBE - Data (GC)");
4626 if (VBOX_FAILURE(rc))
4627 return rc;
4628
4629#if 0
4630 /* This now causes conflicts with Win2k & XP; it is not aware this range is taken
4631 and tries to map other devices there */
4632 /* Old Bochs. */
4633 rc = PDMDevHlpIOPortRegisterGC(pDevIns, 0xff80, 1, 0, "vgaIOPortWriteVBEIndex", "vgaIOPortReadVBEIndex", "VGA/VBE - Index Old (GC)");
4634 if (VBOX_FAILURE(rc))
4635 return rc;
4636 rc = PDMDevHlpIOPortRegisterGC(pDevIns, 0xff81, 1, 0, "vgaIOPortWriteVBEData", "vgaIOPortReadVBEData", "VGA/VBE - Index Old (GC)");
4637 if (VBOX_FAILURE(rc))
4638 return rc;
4639#endif
4640
4641#endif /* CONFIG_BOCHS_VBE */
4642 }
4643
4644 /* R0 context extension */
4645 if (pData->fR0Enabled)
4646 {
4647 rc = PDMDevHlpIOPortRegisterR0(pDevIns, 0x3c0, 16, 0, "vgaIOPortWrite", "vgaIOPortRead", NULL, NULL, "VGA - 3c0 (GC)");
4648 if (VBOX_FAILURE(rc))
4649 return rc;
4650 rc = PDMDevHlpIOPortRegisterR0(pDevIns, 0x3b4, 2, 0, "vgaIOPortWrite", "vgaIOPortRead", NULL, NULL, "VGA - 3b4 (GC)");
4651 if (VBOX_FAILURE(rc))
4652 return rc;
4653 rc = PDMDevHlpIOPortRegisterR0(pDevIns, 0x3ba, 1, 0, "vgaIOPortWrite", "vgaIOPortRead", NULL, NULL, "VGA - 3ba (GC)");
4654 if (VBOX_FAILURE(rc))
4655 return rc;
4656 rc = PDMDevHlpIOPortRegisterR0(pDevIns, 0x3d4, 2, 0, "vgaIOPortWrite", "vgaIOPortRead", NULL, NULL, "VGA - 3d4 (GC)");
4657 if (VBOX_FAILURE(rc))
4658 return rc;
4659 rc = PDMDevHlpIOPortRegisterR0(pDevIns, 0x3da, 1, 0, "vgaIOPortWrite", "vgaIOPortRead", NULL, NULL, "VGA - 3da (GC)");
4660 if (VBOX_FAILURE(rc))
4661 return rc;
4662#ifdef CONFIG_BOCHS_VBE
4663 rc = PDMDevHlpIOPortRegisterR0(pDevIns, 0x1ce, 1, 0, "vgaIOPortWriteVBEIndex", "vgaIOPortReadVBEIndex", NULL, NULL, "VGA/VBE - Index (GC)");
4664 if (VBOX_FAILURE(rc))
4665 return rc;
4666 rc = PDMDevHlpIOPortRegisterR0(pDevIns, 0x1cf, 1, 0, "vgaIOPortWriteVBEData", "vgaIOPortReadVBEData", NULL, NULL, "VGA/VBE - Data (GC)");
4667 if (VBOX_FAILURE(rc))
4668 return rc;
4669
4670#if 0
4671 /* This now causes conflicts with Win2k & XP; it is not aware this range is taken
4672 and tries to map other devices there */
4673 /* Old Bochs. */
4674 rc = PDMDevHlpIOPortRegisterR0(pDevIns, 0xff80, 1, 0, "vgaIOPortWriteVBEIndex", "vgaIOPortReadVBEIndex", "VGA/VBE - Index Old (GC)");
4675 if (VBOX_FAILURE(rc))
4676 return rc;
4677 rc = PDMDevHlpIOPortRegisterR0(pDevIns, 0xff81, 1, 0, "vgaIOPortWriteVBEData", "vgaIOPortReadVBEData", "VGA/VBE - Index Old (GC)");
4678 if (VBOX_FAILURE(rc))
4679 return rc;
4680#endif
4681
4682#endif /* CONFIG_BOCHS_VBE */
4683 }
4684
4685 /* vga mmio */
4686 rc = PDMDevHlpMMIORegister(pDevIns, 0x000a0000, 0x00020000, 0, vgaMMIOWrite, vgaMMIORead, vgaMMIOFill, "VGA - VGA Video Buffer");
4687 if (VBOX_FAILURE(rc))
4688 return rc;
4689 if (pData->fGCEnabled)
4690 {
4691 rc = PDMDevHlpMMIORegisterGC(pDevIns, 0x000a0000, 0x00020000, 0, "vgaMMIOWrite", "vgaMMIORead", "vgaMMIOFill", "VGA - VGA Video Buffer");
4692 if (VBOX_FAILURE(rc))
4693 return rc;
4694 }
4695 if (pData->fR0Enabled)
4696 {
4697 rc = PDMDevHlpMMIORegisterR0(pDevIns, 0x000a0000, 0x00020000, 0, "vgaMMIOWrite", "vgaMMIORead", "vgaMMIOFill", "VGA - VGA Video Buffer");
4698 if (VBOX_FAILURE(rc))
4699 return rc;
4700 }
4701
4702 /* vga bios */
4703 rc = PDMDevHlpIOPortRegister(pDevIns, VBE_PRINTF_PORT, 1, NULL, vgaIOPortWriteBIOS, vgaIOPortReadBIOS, NULL, NULL, "VGA BIOS debug/panic");
4704 if (VBOX_FAILURE(rc))
4705 return rc;
4706 AssertReleaseMsg(g_cbVgaBiosBinary <= _64K && g_cbVgaBiosBinary >= 32*_1K, ("g_cbVgaBiosBinary=%#x\n", g_cbVgaBiosBinary));
4707 AssertReleaseMsg(RT_ALIGN_Z(g_cbVgaBiosBinary, PAGE_SIZE) == g_cbVgaBiosBinary, ("g_cbVgaBiosBinary=%#x\n", g_cbVgaBiosBinary));
4708 rc = PDMDevHlpROMRegister(pDevIns, 0x000c0000, g_cbVgaBiosBinary, &g_abVgaBiosBinary[0], "VGA BIOS");
4709 if (VBOX_FAILURE(rc))
4710 return rc;
4711
4712 /* save */
4713 rc = PDMDevHlpSSMRegister(pDevIns, pDevIns->pDevReg->szDeviceName, iInstance, 1 /* version */, sizeof(*pData),
4714 NULL, vgaR3SaveExec, NULL,
4715 NULL, vgaR3LoadExec, NULL);
4716 if (VBOX_FAILURE(rc))
4717 return rc;
4718
4719 /* PCI */
4720 rc = PDMDevHlpPCIRegister(pDevIns, &pData->Dev);
4721 if (VBOX_FAILURE(rc))
4722 return rc;
4723 /*AssertMsg(pData->Dev.devfn == 16 || iInstance != 0, ("pData->Dev.devfn=%d\n", pData->Dev.devfn));*/
4724 if (pData->Dev.devfn != 16 && iInstance == 0)
4725 Log(("!!WARNING!!: pData->dev.devfn=%d (ignore if testcase or no started by Main)\n", pData->Dev.devfn));
4726 rc = PDMDevHlpPCIIORegionRegister(pDevIns, 0, pData->vram_size, PCI_ADDRESS_SPACE_MEM_PREFETCH, vgaR3IORegionMap);
4727 if (VBOX_FAILURE(rc))
4728 return rc;
4729
4730 /*
4731 * Create the refresh timer.
4732 */
4733 rc = PDMDevHlpTMTimerCreate(pDevIns, TMCLOCK_REAL, vgaTimerRefresh, "VGA Refresh Timer", &pData->RefreshTimer);
4734 if (VBOX_FAILURE(rc))
4735 return rc;
4736
4737 /*
4738 * Attach to the display.
4739 */
4740 rc = vgaAttach(pDevIns, 0 /* display LUN # */);
4741 if (VBOX_FAILURE(rc))
4742 return rc;
4743
4744 /*
4745 * Allocate the VRAM.
4746 */
4747 rc = SUPPageAlloc(pData->vram_size >> PAGE_SHIFT, (void **)&pData->vram_ptrHC);
4748 if (VBOX_FAILURE(rc))
4749 {
4750 AssertMsgFailed(("SUPPageAlloc(%#x,) -> %d\n", pData->vram_size, rc));
4751 return rc;
4752 }
4753
4754#ifdef VBE_NEW_DYN_LIST
4755 /*
4756 * Compute buffer size for the VBE BIOS Extra Data.
4757 */
4758 cb = sizeof(mode_info_list) + sizeof(ModeInfoListItem);
4759
4760 rc = CFGMR3QueryU32(pCfgHandle, "HeightReduction", &cyReduction);
4761 if (VBOX_SUCCESS(rc) && cyReduction)
4762 cb *= 2; /* Default mode list will be twice long */
4763 else
4764 cyReduction = 0;
4765
4766 rc = CFGMR3QueryU32(pCfgHandle, "CustomVideoModes", &cCustomModes);
4767 if (VBOX_SUCCESS(rc) && cCustomModes)
4768 cb += sizeof(ModeInfoListItem) * cCustomModes;
4769 else
4770 cCustomModes = 0;
4771
4772 /*
4773 * Allocate and initialize buffer for the VBE BIOS Extra Data.
4774 */
4775 pData->cbVBEExtraData = sizeof(VBEHEADER) + cb;
4776 pData->pu8VBEExtraData = (uint8_t *)PDMDevHlpMMHeapAllocZ(pDevIns, pData->cbVBEExtraData);
4777 if (!pData->pu8VBEExtraData)
4778 return VERR_NO_MEMORY;
4779
4780 pVBEDataHdr = (PVBEHEADER)pData->pu8VBEExtraData;
4781 pVBEDataHdr->u16Signature = VBEHEADER_MAGIC;
4782 pVBEDataHdr->cbData = cb;
4783
4784#ifndef VRAM_SIZE_FIX
4785 pCurMode = memcpy(pVBEDataHdr + 1, &mode_info_list, sizeof(mode_info_list));
4786 pCurMode = (ModeInfoListItem *)((uintptr_t)pCurMode + sizeof(mode_info_list));
4787#else /* VRAM_SIZE_FIX defined */
4788 pCurMode = (ModeInfoListItem *)(pVBEDataHdr + 1);
4789 for (i = 0; i < MODE_INFO_SIZE; i++)
4790 {
4791 uint32_t pixelWidth, reqSize;
4792 if (mode_info_list[i].info.MemoryModel == VBE_MEMORYMODEL_TEXT_MODE)
4793 pixelWidth = 2;
4794 else
4795 pixelWidth = mode_info_list[i].info.BitsPerPixel / 8;
4796 reqSize = mode_info_list[i].info.XResolution
4797 * mode_info_list[i].info.YResolution
4798 * pixelWidth;
4799 if (reqSize >= pData->vram_size)
4800 continue;
4801 *pCurMode = mode_info_list[i];
4802 pCurMode++;
4803 }
4804#endif /* VRAM_SIZE_FIX defined */
4805
4806 /*
4807 * Copy default modes with subtractred YResolution.
4808 */
4809 if (cyReduction)
4810 {
4811 ModeInfoListItem *pDefMode = mode_info_list;
4812 Log(("vgaR3Construct: cyReduction=%u\n", cyReduction));
4813#ifndef VRAM_SIZE_FIX
4814 for (i = 0; i < MODE_INFO_SIZE; i++, pCurMode++, pDefMode++)
4815 {
4816 *pCurMode = *pDefMode;
4817 pCurMode->mode += 0x30;
4818 pCurMode->info.YResolution -= cyReduction;
4819 }
4820#else /* VRAM_SIZE_FIX defined */
4821 for (i = 0; i < MODE_INFO_SIZE; i++, pDefMode++)
4822 {
4823 uint32_t pixelWidth, reqSize;
4824 if (pDefMode->info.MemoryModel == VBE_MEMORYMODEL_TEXT_MODE)
4825 pixelWidth = 2;
4826 else
4827 pixelWidth = pDefMode->info.BitsPerPixel / 8;
4828 reqSize = pDefMode->info.XResolution * pDefMode->info.YResolution * pixelWidth;
4829 if (reqSize >= pData->vram_size)
4830 continue;
4831 *pCurMode = *pDefMode;
4832 pCurMode->mode += 0x30;
4833 pCurMode->info.YResolution -= cyReduction;
4834 pCurMode++;
4835 }
4836#endif /* VRAM_SIZE_FIX defined */
4837 }
4838
4839
4840 /*
4841 * Add custom modes.
4842 */
4843 if (cCustomModes)
4844 {
4845 uint16_t u16CurMode = 0x160;
4846 for (i = 1; i <= cCustomModes; i++)
4847 {
4848 char szExtraDataKey[sizeof("CustomVideoModeXX")];
4849 char *pszExtraData = NULL;
4850
4851 /* query and decode the custom mode string. */
4852 RTStrPrintf(szExtraDataKey, sizeof(szExtraDataKey), "CustomVideoMode%d", i);
4853 rc = CFGMR3QueryStringAlloc(pCfgHandle, szExtraDataKey, &pszExtraData);
4854 if (VBOX_SUCCESS(rc))
4855 {
4856 ModeInfoListItem *pDefMode = mode_info_list;
4857 unsigned int cx, cy, cBits, cParams;
4858 uint16_t u16DefMode;
4859
4860 cParams = sscanf(pszExtraData, "%ux%ux%u", &cx, &cy, &cBits);
4861 if ( cParams != 3
4862 || (cBits != 16 && cBits != 24 && cBits != 32))
4863 {
4864 AssertMsgFailed(("Configuration error: Invalid mode data '%s' for '%s'! cBits=%d\n", pszExtraData, szExtraDataKey, cBits));
4865 return VERR_VGA_INVALID_CUSTOM_MODE;
4866 }
4867#ifdef VRAM_SIZE_FIX
4868 if (cx * cy * cBits / 8 >= pData->vram_size)
4869 {
4870 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",
4871 cx, cy, cBits, pData->vram_size / _1M));
4872 return VERR_VGA_INVALID_CUSTOM_MODE;
4873 }
4874#endif /* VRAM_SIZE_FIX defined */
4875 MMR3HeapFree(pszExtraData);
4876
4877 /* Use defaults from max@bpp mode. */
4878 switch (cBits)
4879 {
4880 case 16:
4881 u16DefMode = VBE_VESA_MODE_1024X768X565;
4882 break;
4883
4884 case 24:
4885 u16DefMode = VBE_VESA_MODE_1024X768X888;
4886 break;
4887
4888 case 32:
4889 u16DefMode = VBE_OWN_MODE_1024X768X8888;
4890 break;
4891
4892 default: /* gcc, shut up! */
4893 AssertMsgFailed(("gone postal!\n"));
4894 continue;
4895 }
4896
4897 while ( pDefMode->mode != u16DefMode
4898 && pDefMode->mode != VBE_VESA_MODE_END_OF_LIST)
4899 pDefMode++;
4900 Assert(pDefMode->mode != VBE_VESA_MODE_END_OF_LIST);
4901
4902 *pCurMode = *pDefMode;
4903 pCurMode->mode = u16CurMode++;
4904
4905 /* adjust defaults */
4906 pCurMode->info.XResolution = cx;
4907 pCurMode->info.YResolution = cy;
4908
4909 switch (cBits)
4910 {
4911 case 16:
4912 pCurMode->info.BytesPerScanLine = cx * 2;
4913 pCurMode->info.LinBytesPerScanLine = cx * 2;
4914 break;
4915
4916 case 24:
4917 pCurMode->info.BytesPerScanLine = cx * 3;
4918 pCurMode->info.LinBytesPerScanLine = cx * 3;
4919 break;
4920
4921 case 32:
4922 pCurMode->info.BytesPerScanLine = cx * 4;
4923 pCurMode->info.LinBytesPerScanLine = cx * 4;
4924 break;
4925 }
4926
4927 /* commit it */
4928 pCurMode++;
4929 }
4930 else if (rc != VERR_CFGM_VALUE_NOT_FOUND)
4931 {
4932 AssertMsgFailed(("CFGMR3QueryStringAlloc(,'%s',) -> %Vrc\n", szExtraDataKey, rc));
4933 return rc;
4934 }
4935 } /* foreach custom mode key */
4936 }
4937
4938 /*
4939 * Add the "End of list" mode.
4940 */
4941 memset(pCurMode, 0, sizeof(*pCurMode));
4942 pCurMode->mode = VBE_VESA_MODE_END_OF_LIST;
4943
4944 /*
4945 * Register I/O Port for the VBE BIOS Extra Data.
4946 */
4947 rc = PDMDevHlpIOPortRegister(pDevIns, VBE_EXTRA_PORT, 1, NULL, vbeIOPortWriteVBEExtra, vbeIOPortReadVBEExtra, NULL, NULL, "VBE BIOS Extra Data");
4948 if (VBOX_FAILURE(rc))
4949 return rc;
4950#endif
4951
4952 /*
4953 * Statistics.
4954 */
4955 STAM_REG(pVM, &pData->StatGCMemoryRead, STAMTYPE_PROFILE, "/Devices/VGA/GC/Memory/Read", STAMUNIT_TICKS_PER_CALL, "Profiling of the VGAGCMemoryRead() body.");
4956 STAM_REG(pVM, &pData->StatGCMemoryWrite, STAMTYPE_PROFILE, "/Devices/VGA/GC/Memory/Write", STAMUNIT_TICKS_PER_CALL, "Profiling of the VGAGCMemoryWrite() body.");
4957 STAM_REG(pVM, &pData->StatGCIOPortRead, STAMTYPE_PROFILE, "/Devices/VGA/GC/IOPort/Read", STAMUNIT_TICKS_PER_CALL, "Profiling of the VGAGCIOPortRead() body.");
4958 STAM_REG(pVM, &pData->StatGCIOPortWrite, STAMTYPE_PROFILE, "/Devices/VGA/GC/IOPort/Write", STAMUNIT_TICKS_PER_CALL, "Profiling of the VGAGCIOPortWrite() body.");
4959
4960 return VINF_SUCCESS;
4961}
4962
4963
4964/**
4965 * Destruct a device instance.
4966 *
4967 * Most VM resources are freed by the VM. This callback is provided so that any non-VM
4968 * resources can be freed correctly.
4969 *
4970 * @param pDevIns The device instance data.
4971 */
4972static DECLCALLBACK(int) vgaR3Destruct(PPDMDEVINS pDevIns)
4973{
4974#ifdef VBE_NEW_DYN_LIST
4975 PVGASTATE pData = PDMINS2DATA(pDevIns, PVGASTATE);
4976 LogFlow(("vgaR3Destruct:\n"));
4977
4978 /*
4979 * Free MM heap pointers.
4980 */
4981 if (pData->pu8VBEExtraData)
4982 {
4983 MMR3HeapFree(pData->pu8VBEExtraData);
4984 pData->pu8VBEExtraData = NULL;
4985 }
4986#endif
4987
4988 /*
4989 * Allocate the VRAM.
4990 */
4991 int rc = SUPPageFree(pData->vram_ptrHC, pData->vram_size >> PAGE_SHIFT);
4992 if (VBOX_FAILURE(rc))
4993 {
4994 AssertMsgFailed(("SUPPageFree(%p, %#x) -> %d\n", pData->vram_ptrHC, pData->vram_size, rc));
4995 return rc;
4996 }
4997 pData->vram_ptrHC = NULL;
4998
4999 return VINF_SUCCESS;
5000}
5001
5002
5003/**
5004 * The device registration structure.
5005 */
5006const PDMDEVREG g_DeviceVga =
5007{
5008 /* u32Version */
5009 PDM_DEVREG_VERSION,
5010 /* szDeviceName */
5011 "vga",
5012 /* szGCMod */
5013 "VBoxDDGC.gc",
5014 /* szR0Mod */
5015 "VBoxDDR0.r0",
5016 /* pszDescription */
5017 "VGA Adaptor with VESA extensions.",
5018 /* fFlags */
5019 PDM_DEVREG_FLAGS_HOST_BITS_DEFAULT | PDM_DEVREG_FLAGS_GUEST_BITS_DEFAULT | PDM_DEVREG_FLAGS_GC | PDM_DEVREG_FLAGS_R0,
5020 /* fClass */
5021 PDM_DEVREG_CLASS_GRAPHICS,
5022 /* cMaxInstances */
5023 1,
5024 /* cbInstance */
5025 sizeof(VGASTATE),
5026 /* pfnConstruct */
5027 vgaR3Construct,
5028 /* pfnDestruct */
5029 vgaR3Destruct,
5030 /* pfnRelocate */
5031 vgaR3Relocate,
5032 /* pfnIOCtl */
5033 NULL,
5034 /* pfnPowerOn */
5035 NULL,
5036 /* pfnReset */
5037 vgaR3Reset,
5038 /* pfnSuspend */
5039 NULL,
5040 /* pfnResume */
5041 NULL,
5042 /* pfnAttach */
5043 vgaAttach,
5044 /* pfnDetach */
5045 vgaDetach,
5046 /* pfnQueryInterface */
5047 NULL,
5048 /* pfnInitComplete */
5049 NULL
5050};
5051
5052#endif /* !IN_RING3 */
5053#endif /* VBOX */
5054#endif /* !VBOX_DEVICE_STRUCT_TESTCASE */
5055
5056/*
5057 * Local Variables:
5058 * nuke-trailing-whitespace-p:nil
5059 * End:
5060 */
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