VirtualBox

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

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

introduced PDMDevHlpGetVM()

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