Bochs/bochs/iodev/vga.cc

1511 lines
51 KiB
C++

/////////////////////////////////////////////////////////////////////////
// $Id$
/////////////////////////////////////////////////////////////////////////
//
// Copyright (C) 2002-2012 The Bochs Project
// PCI VGA dummy adapter Copyright (C) 2002,2003 Mike Nordell
//
// This library is free software; you can redistribute it and/or
// modify it under the terms of the GNU Lesser General Public
// License as published by the Free Software Foundation; either
// version 2 of the License, or (at your option) any later version.
//
// This library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
// Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public
// License along with this library; if not, write to the Free Software
// Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
//
/////////////////////////////////////////////////////////////////////////
// Standard VGA emulation + Bochs VBE support + PCI VGA interface
// NOTE from the original pcivga code:
// This "driver" was created for the SOLE PURPOSE of getting BeOS
// to boot. It currently does NOTHING more than presenting a generic VGA
// device on the PCI bus. ALL gfx in/out-put is still handled by the VGA code.
// Furthermore, almost all of the PCI registers are currently acting like RAM.
// Define BX_PLUGGABLE in files that can be compiled into plugins. For
// platforms that require a special tag on exported symbols, BX_PLUGGABLE
// is used to know when we are exporting symbols and when we are importing.
#define BX_PLUGGABLE
#include "iodev.h"
#include "vgacore.h"
#include "vga.h"
#include "virt_timer.h"
#define LOG_THIS theVga->
#define VGA_TRACE_FEATURE
// Only reference the array if the tile numbers are within the bounds
// of the array. If out of bounds, do nothing.
#define SET_TILE_UPDATED(xtile, ytile, value) \
do { \
if (((xtile) < BX_VGA_THIS s.num_x_tiles) && ((ytile) < BX_VGA_THIS s.num_y_tiles)) \
BX_VGA_THIS s.vga_tile_updated[(xtile)+(ytile)*BX_VGA_THIS s.num_x_tiles] = value; \
} while (0)
// Only reference the array if the tile numbers are within the bounds
// of the array. If out of bounds, return 0.
#define GET_TILE_UPDATED(xtile,ytile) \
((((xtile) < BX_VGA_THIS s.num_x_tiles) && ((ytile) < BX_VGA_THIS s.num_y_tiles))? \
BX_VGA_THIS s.vga_tile_updated[(xtile)+(ytile)*BX_VGA_THIS s.num_x_tiles] \
: 0)
static const Bit16u charmap_offset[8] = {
0x0000, 0x4000, 0x8000, 0xc000,
0x2000, 0x6000, 0xa000, 0xe000
};
static const Bit8u ccdat[16][4] = {
{ 0x00, 0x00, 0x00, 0x00 },
{ 0xff, 0x00, 0x00, 0x00 },
{ 0x00, 0xff, 0x00, 0x00 },
{ 0xff, 0xff, 0x00, 0x00 },
{ 0x00, 0x00, 0xff, 0x00 },
{ 0xff, 0x00, 0xff, 0x00 },
{ 0x00, 0xff, 0xff, 0x00 },
{ 0xff, 0xff, 0xff, 0x00 },
{ 0x00, 0x00, 0x00, 0xff },
{ 0xff, 0x00, 0x00, 0xff },
{ 0x00, 0xff, 0x00, 0xff },
{ 0xff, 0xff, 0x00, 0xff },
{ 0x00, 0x00, 0xff, 0xff },
{ 0xff, 0x00, 0xff, 0xff },
{ 0x00, 0xff, 0xff, 0xff },
{ 0xff, 0xff, 0xff, 0xff },
};
bx_vga_c *theVga = NULL;
unsigned old_MSL = 0;
int libvga_LTX_plugin_init(plugin_t *plugin, plugintype_t type, int argc, char *argv[])
{
if (type == PLUGTYPE_CORE) {
theVga = new bx_vga_c();
bx_devices.pluginVgaDevice = theVga;
BX_REGISTER_DEVICE_DEVMODEL(plugin, type, theVga, BX_PLUGIN_VGA);
return 0; // Success
} else {
return -1;
}
}
void libvga_LTX_plugin_fini(void)
{
delete theVga;
}
bx_vga_c::bx_vga_c() : bx_vgacore_c()
{
put("VGA");
}
bx_vga_c::~bx_vga_c()
{
BX_DEBUG(("Exit"));
}
void bx_vga_c::init_vga_extension(void)
{
unsigned addr;
Bit16u max_xres, max_yres, max_bpp;
Bit8u devfunc = 0x00;
unsigned i;
BX_VGA_THIS init_iohandlers(read_handler, write_handler);
BX_VGA_THIS init_systemtimer(timer_handler, vga_param_handler);
BX_VGA_THIS pci_enabled = DEV_is_pci_device("pcivga");
// The following is for the VBE display extension
BX_VGA_THIS vbe_present = 0;
BX_VGA_THIS vbe.enabled = 0;
BX_VGA_THIS vbe.dac_8bit = 0;
BX_VGA_THIS vbe.base_address = 0x0000;
if (!strcmp(SIM->get_param_string(BXPN_VGA_EXTENSION)->getptr(), "vbe")) {
BX_VGA_THIS put("BXVGA");
for (addr=VBE_DISPI_IOPORT_INDEX; addr<=VBE_DISPI_IOPORT_DATA; addr++) {
DEV_register_ioread_handler(this, vbe_read_handler, addr, "vga video", 7);
DEV_register_iowrite_handler(this, vbe_write_handler, addr, "vga video", 7);
}
if (!BX_VGA_THIS pci_enabled) {
BX_VGA_THIS vbe.base_address = VBE_DISPI_LFB_PHYSICAL_ADDRESS;
DEV_register_memory_handlers(theVga, mem_read_handler, mem_write_handler,
BX_VGA_THIS vbe.base_address,
BX_VGA_THIS vbe.base_address + VBE_DISPI_TOTAL_VIDEO_MEMORY_BYTES - 1);
}
if (BX_VGA_THIS s.memory == NULL)
BX_VGA_THIS s.memory = new Bit8u[VBE_DISPI_TOTAL_VIDEO_MEMORY_BYTES];
memset(BX_VGA_THIS s.memory, 0, VBE_DISPI_TOTAL_VIDEO_MEMORY_BYTES);
BX_VGA_THIS s.memsize = VBE_DISPI_TOTAL_VIDEO_MEMORY_BYTES;
BX_VGA_THIS vbe.cur_dispi=VBE_DISPI_ID0;
BX_VGA_THIS vbe.xres=640;
BX_VGA_THIS vbe.yres=480;
BX_VGA_THIS vbe.bpp=8;
BX_VGA_THIS vbe.bank=0;
BX_VGA_THIS vbe.curindex=0;
BX_VGA_THIS vbe.offset_x=0;
BX_VGA_THIS vbe.offset_y=0;
BX_VGA_THIS vbe.virtual_xres=640;
BX_VGA_THIS vbe.virtual_yres=480;
BX_VGA_THIS vbe.bpp_multiplier=1;
BX_VGA_THIS vbe.virtual_start=0;
BX_VGA_THIS vbe.lfb_enabled=0;
BX_VGA_THIS vbe.get_capabilities=0;
bx_gui->get_capabilities(&max_xres, &max_yres, &max_bpp);
if (max_xres > VBE_DISPI_MAX_XRES) {
BX_VGA_THIS vbe.max_xres=VBE_DISPI_MAX_XRES;
} else {
BX_VGA_THIS vbe.max_xres=max_xres;
}
if (max_yres > VBE_DISPI_MAX_YRES) {
BX_VGA_THIS vbe.max_yres=VBE_DISPI_MAX_YRES;
} else {
BX_VGA_THIS vbe.max_yres=max_yres;
}
if (max_bpp > VBE_DISPI_MAX_BPP) {
BX_VGA_THIS vbe.max_bpp=VBE_DISPI_MAX_BPP;
} else {
BX_VGA_THIS vbe.max_bpp=max_bpp;
}
BX_VGA_THIS s.max_xres = BX_VGA_THIS vbe.max_xres;
BX_VGA_THIS s.max_yres = BX_VGA_THIS vbe.max_yres;
BX_VGA_THIS vbe_present = 1;
BX_VGA_THIS extension_init = 1;
BX_INFO(("VBE Bochs Display Extension Enabled"));
}
#if BX_SUPPORT_PCI
if (BX_VGA_THIS pci_enabled) {
DEV_register_pci_handlers(this, &devfunc, "pcivga", "Experimental PCI VGA");
for (i = 0; i < 256; i++) {
BX_VGA_THIS pci_conf[i] = 0x0;
}
// readonly registers
static const struct init_vals_t {
unsigned addr;
unsigned char val;
} init_vals[] = {
// Note that the values for vendor and device id are selected at random!
// There might actually be "real" values for "experimental" vendor and
// device that should be used!
{ 0x00, 0x34 }, { 0x01, 0x12 }, // 0x1234 - experimental vendor
{ 0x02, 0x11 }, { 0x03, 0x11 }, // 0x1111 - experimental device
{ 0x0a, 0x00 }, // class_sub VGA controller
{ 0x0b, 0x03 }, // class_base display
{ 0x0e, 0x00 } // header_type_generic
};
for (i = 0; i < sizeof(init_vals) / sizeof(*init_vals); ++i) {
BX_VGA_THIS pci_conf[init_vals[i].addr] = init_vals[i].val;
}
if (BX_VGA_THIS vbe_present) {
BX_VGA_THIS pci_conf[0x10] = 0x08;
BX_VGA_THIS pci_base_address[0] = 0;
}
BX_VGA_THIS pci_rom_address = 0;
BX_VGA_THIS load_pci_rom(SIM->get_param_string(BXPN_VGA_ROM_PATH)->getptr());
}
#endif
}
void bx_vga_c::reset(unsigned type)
{
#if BX_SUPPORT_PCI
if (BX_VGA_THIS pci_enabled) {
static const struct reset_vals_t {
unsigned addr;
unsigned char val;
} reset_vals[] = {
{ 0x04, 0x03 }, { 0x05, 0x00 }, // command_io + command_mem
{ 0x06, 0x00 }, { 0x07, 0x02 } // status_devsel_medium
};
for (unsigned i = 0; i < sizeof(reset_vals) / sizeof(*reset_vals); ++i) {
BX_VGA_THIS pci_conf[reset_vals[i].addr] = reset_vals[i].val;
}
}
#endif
}
void bx_vga_c::register_state(void)
{
bx_list_c *list = new bx_list_c(SIM->get_bochs_root(), "vga", "VGA Adapter State", 3);
bx_vgacore_c::register_state(list);
#if BX_SUPPORT_PCI
if (BX_VGA_THIS pci_enabled) {
register_pci_state(list);
}
#endif
// register state for Bochs VBE
if (!strcmp(SIM->get_param_string(BXPN_VGA_EXTENSION)->getptr(), "vbe")) {
bx_list_c *vbe = new bx_list_c(list, "vbe", 18);
new bx_shadow_num_c(vbe, "cur_dispi", &BX_VGA_THIS vbe.cur_dispi, BASE_HEX);
new bx_shadow_num_c(vbe, "xres", &BX_VGA_THIS vbe.xres);
new bx_shadow_num_c(vbe, "yres", &BX_VGA_THIS vbe.yres);
new bx_shadow_num_c(vbe, "bpp", &BX_VGA_THIS vbe.bpp);
new bx_shadow_num_c(vbe, "bank", &BX_VGA_THIS vbe.bank);
new bx_shadow_bool_c(vbe, "enabled", &BX_VGA_THIS vbe.enabled);
new bx_shadow_num_c(vbe, "curindex", &BX_VGA_THIS vbe.curindex);
new bx_shadow_num_c(vbe, "visible_screen_size", &BX_VGA_THIS vbe.visible_screen_size);
new bx_shadow_num_c(vbe, "offset_x", &BX_VGA_THIS vbe.offset_x);
new bx_shadow_num_c(vbe, "offset_y", &BX_VGA_THIS vbe.offset_y);
new bx_shadow_num_c(vbe, "virtual_xres", &BX_VGA_THIS vbe.virtual_xres);
new bx_shadow_num_c(vbe, "virtual_yres", &BX_VGA_THIS vbe.virtual_yres);
new bx_shadow_num_c(vbe, "virtual_start", &BX_VGA_THIS vbe.virtual_start);
new bx_shadow_num_c(vbe, "bpp_multiplier", &BX_VGA_THIS vbe.bpp_multiplier);
new bx_shadow_bool_c(vbe, "lfb_enabled", &BX_VGA_THIS vbe.lfb_enabled);
new bx_shadow_bool_c(vbe, "get_capabilities", &BX_VGA_THIS vbe.get_capabilities);
new bx_shadow_bool_c(vbe, "dac_8bit", &BX_VGA_THIS vbe.dac_8bit);
}
}
void bx_vga_c::after_restore_state(void)
{
#if BX_SUPPORT_PCI
if (BX_VGA_THIS pci_enabled) {
if (BX_VGA_THIS vbe_present) {
if (BX_VGA_THIS vbe_set_base_addr(&BX_VGA_THIS pci_base_address[0],
&BX_VGA_THIS pci_conf[0x10])) {
BX_INFO(("new base address: 0x%08x", BX_VGA_THIS pci_base_address[0]));
}
}
if (DEV_pci_set_base_mem(this, mem_read_handler, mem_write_handler,
&BX_VGA_THIS pci_rom_address,
&BX_VGA_THIS pci_conf[0x30],
BX_VGA_THIS pci_rom_size)) {
BX_INFO(("new ROM address: 0x%08x", BX_VGA_THIS pci_rom_address));
}
}
#endif
if (BX_VGA_THIS vbe.enabled) {
bx_gui->dimension_update(BX_VGA_THIS vbe.xres, BX_VGA_THIS vbe.yres, 0, 0,
BX_VGA_THIS vbe.bpp);
}
bx_vgacore_c::after_restore_state();
}
// static IO port write callback handler
// redirects to non-static class handler to avoid virtual functions
void bx_vga_c::write_handler(void *this_ptr, Bit32u address, Bit32u value, unsigned io_len)
{
#if BX_USE_VGA_SMF == 0
bx_vga_c *class_ptr = (bx_vga_c *) this_ptr;
class_ptr->write(address, value, io_len, 0);
#else
UNUSED(this_ptr);
theVga->write(address, value, io_len, 0);
#endif
}
#if BX_USE_VGA_SMF
void bx_vga_c::write_handler_no_log(void *this_ptr, Bit32u address, Bit32u value, unsigned io_len)
{
UNUSED(this_ptr);
theVga->write(address, value, io_len, 1);
}
#endif
void bx_vga_c::write(Bit32u address, Bit32u value, unsigned io_len, bx_bool no_log)
{
bx_bool needs_update = 0;
if (io_len == 2) {
#if BX_USE_VGA_SMF
bx_vga_c::write_handler_no_log(0, address, value & 0xff, 1);
bx_vga_c::write_handler_no_log(0, address+1, (value >> 8) & 0xff, 1);
#else
bx_vga_c::write(address, value & 0xff, 1, 1);
bx_vga_c::write(address+1, (value >> 8) & 0xff, 1, 1);
#endif
return;
}
if ((address >= 0x03b0) && (address <= 0x03bf) &&
(BX_VGA_THIS s.misc_output.color_emulation))
return;
if ((address >= 0x03d0) && (address <= 0x03df) &&
(BX_VGA_THIS s.misc_output.color_emulation==0))
return;
switch (address) {
case 0x03b5: /* CRTC Registers (monochrome emulation modes) */
case 0x03d5: /* CRTC Registers (color emulation modes) */
if (BX_VGA_THIS s.CRTC.address > 0x18) {
BX_DEBUG(("write: invalid CRTC register 0x%02x ignored",
(unsigned) BX_VGA_THIS s.CRTC.address));
return;
}
if (value != BX_VGA_THIS s.CRTC.reg[BX_VGA_THIS s.CRTC.address]) {
switch (BX_VGA_THIS s.CRTC.address) {
case 0x13:
case 0x14:
case 0x17:
BX_VGA_THIS s.CRTC.reg[BX_VGA_THIS s.CRTC.address] = value;
if (!BX_VGA_THIS vbe.enabled || (BX_VGA_THIS vbe.bpp == VBE_DISPI_BPP_4)) {
// Line offset change
BX_VGA_THIS s.line_offset = BX_VGA_THIS s.CRTC.reg[0x13] << 1;
if (BX_VGA_THIS s.CRTC.reg[0x14] & 0x40) BX_VGA_THIS s.line_offset <<= 2;
else if ((BX_VGA_THIS s.CRTC.reg[0x17] & 0x40) == 0) BX_VGA_THIS s.line_offset <<= 1;
needs_update = 1;
break;
}
default:
bx_vgacore_c::write(address, value, io_len, no_log);
}
}
break;
default:
bx_vgacore_c::write(address, value, io_len, no_log);
}
if (needs_update) {
// Mark all video as updated so the changes will go through
BX_VGA_THIS redraw_area(0, 0, BX_VGA_THIS s.last_xres, BX_VGA_THIS s.last_yres);
}
}
Bit64s bx_vga_c::vga_param_handler(bx_param_c *param, int set, Bit64s val)
{
Bit32u interval;
// handler for runtime parameter 'vga: update_freq'
if (set) {
interval = (Bit32u)(1000000 / val);
BX_INFO(("Changing timer interval to %d", interval));
BX_VGA_THIS timer_handler(theVga);
bx_virt_timer.activate_timer(BX_VGA_THIS timer_id, interval, 1);
if (interval < 300000) {
BX_VGA_THIS s.blink_counter = 300000 / (unsigned)interval;
} else {
BX_VGA_THIS s.blink_counter = 1;
}
}
return val;
}
void bx_vga_c::timer_handler(void *this_ptr)
{
#if BX_USE_VGA_SMF == 0
bx_vga_c *class_ptr = (bx_vga_c *) this_ptr;
class_ptr->timer();
}
void bx_vga_c::timer(void)
{
#else
UNUSED(this_ptr);
#endif
update();
bx_gui->flush();
}
void bx_vga_c::update(void)
{
unsigned iHeight, iWidth;
/* no screen update necessary */
if ((BX_VGA_THIS s.vga_mem_updated==0) && BX_VGA_THIS s.graphics_ctrl.graphics_alpha)
return;
/* skip screen update when vga/video is disabled or the sequencer is in reset mode */
if (!BX_VGA_THIS s.vga_enabled || !BX_VGA_THIS s.attribute_ctrl.video_enabled
|| !BX_VGA_THIS s.sequencer.reset2 || !BX_VGA_THIS s.sequencer.reset1
|| (BX_VGA_THIS s.sequencer.reg1 & 0x20))
return;
/* skip screen update if the vertical retrace is in progress
(using 72 Hz vertical frequency) */
if ((bx_pc_system.time_usec() % 13888) < 70)
return;
if ((BX_VGA_THIS vbe.enabled) && (BX_VGA_THIS vbe.bpp != VBE_DISPI_BPP_4)) {
// specific VBE code display update code
unsigned pitch;
unsigned xc, yc, xti, yti;
unsigned r, c, w, h;
int i;
unsigned long red, green, blue, colour;
Bit8u * vid_ptr, * vid_ptr2;
Bit8u * tile_ptr, * tile_ptr2;
bx_svga_tileinfo_t info;
Bit8u dac_size = BX_VGA_THIS vbe.dac_8bit ? 8 : 6;
iWidth=BX_VGA_THIS vbe.xres;
iHeight=BX_VGA_THIS vbe.yres;
pitch = BX_VGA_THIS s.line_offset;
Bit8u *disp_ptr = &BX_VGA_THIS s.memory[BX_VGA_THIS vbe.virtual_start];
if (bx_gui->graphics_tile_info(&info)) {
if (info.is_indexed) {
switch (BX_VGA_THIS vbe.bpp) {
case 4:
case 15:
case 16:
case 24:
case 32:
BX_ERROR(("current guest pixel format is unsupported on indexed colour host displays"));
break;
case 8:
for (yc=0, yti = 0; yc<iHeight; yc+=Y_TILESIZE, yti++) {
for (xc=0, xti = 0; xc<iWidth; xc+=X_TILESIZE, xti++) {
if (GET_TILE_UPDATED (xti, yti)) {
vid_ptr = disp_ptr + (yc * pitch + xc);
tile_ptr = bx_gui->graphics_tile_get(xc, yc, &w, &h);
for (r=0; r<h; r++) {
vid_ptr2 = vid_ptr;
tile_ptr2 = tile_ptr;
for (c=0; c<w; c++) {
colour = 0;
for (i=0; i<(int)BX_VGA_THIS vbe.bpp; i+=8) {
colour |= *(vid_ptr2++) << i;
}
if (info.is_little_endian) {
for (i=0; i<info.bpp; i+=8) {
*(tile_ptr2++) = (Bit8u)(colour >> i);
}
}
else {
for (i=info.bpp-8; i>-8; i-=8) {
*(tile_ptr2++) = (Bit8u)(colour >> i);
}
}
}
vid_ptr += pitch;
tile_ptr += info.pitch;
}
bx_gui->graphics_tile_update_in_place(xc, yc, w, h);
SET_TILE_UPDATED (xti, yti, 0);
}
}
}
break;
}
}
else {
switch (BX_VGA_THIS vbe.bpp) {
case 4:
BX_ERROR(("cannot draw 4bpp SVGA"));
break;
case 8:
for (yc=0, yti = 0; yc<iHeight; yc+=Y_TILESIZE, yti++) {
for (xc=0, xti = 0; xc<iWidth; xc+=X_TILESIZE, xti++) {
if (GET_TILE_UPDATED (xti, yti)) {
vid_ptr = disp_ptr + (yc * pitch + xc);
tile_ptr = bx_gui->graphics_tile_get(xc, yc, &w, &h);
for (r=0; r<h; r++) {
vid_ptr2 = vid_ptr;
tile_ptr2 = tile_ptr;
for (c=0; c<w; c++) {
colour = *(vid_ptr2++);
colour = MAKE_COLOUR(
BX_VGA_THIS s.pel.data[colour].red, dac_size, info.red_shift, info.red_mask,
BX_VGA_THIS s.pel.data[colour].green, dac_size, info.green_shift, info.green_mask,
BX_VGA_THIS s.pel.data[colour].blue, dac_size, info.blue_shift, info.blue_mask);
if (info.is_little_endian) {
for (i=0; i<info.bpp; i+=8) {
*(tile_ptr2++) = (Bit8u)(colour >> i);
}
}
else {
for (i=info.bpp-8; i>-8; i-=8) {
*(tile_ptr2++) = (Bit8u)(colour >> i);
}
}
}
vid_ptr += pitch;
tile_ptr += info.pitch;
}
bx_gui->graphics_tile_update_in_place(xc, yc, w, h);
SET_TILE_UPDATED (xti, yti, 0);
}
}
}
break;
case 15:
for (yc=0, yti = 0; yc<iHeight; yc+=Y_TILESIZE, yti++) {
for (xc=0, xti = 0; xc<iWidth; xc+=X_TILESIZE, xti++) {
if (GET_TILE_UPDATED (xti, yti)) {
vid_ptr = disp_ptr + (yc * pitch + (xc<<1));
tile_ptr = bx_gui->graphics_tile_get(xc, yc, &w, &h);
for (r=0; r<h; r++) {
vid_ptr2 = vid_ptr;
tile_ptr2 = tile_ptr;
for (c=0; c<w; c++) {
colour = *(vid_ptr2++);
colour |= *(vid_ptr2++) << 8;
colour = MAKE_COLOUR(
colour & 0x001f, 5, info.blue_shift, info.blue_mask,
colour & 0x03e0, 10, info.green_shift, info.green_mask,
colour & 0x7c00, 15, info.red_shift, info.red_mask);
if (info.is_little_endian) {
for (i=0; i<info.bpp; i+=8) {
*(tile_ptr2++) = (Bit8u)(colour >> i);
}
}
else {
for (i=info.bpp-8; i>-8; i-=8) {
*(tile_ptr2++) = (Bit8u)(colour >> i);
}
}
}
vid_ptr += pitch;
tile_ptr += info.pitch;
}
bx_gui->graphics_tile_update_in_place(xc, yc, w, h);
SET_TILE_UPDATED (xti, yti, 0);
}
}
}
break;
case 16:
for (yc=0, yti = 0; yc<iHeight; yc+=Y_TILESIZE, yti++) {
for (xc=0, xti = 0; xc<iWidth; xc+=X_TILESIZE, xti++) {
if (GET_TILE_UPDATED (xti, yti)) {
vid_ptr = disp_ptr + (yc * pitch + (xc<<1));
tile_ptr = bx_gui->graphics_tile_get(xc, yc, &w, &h);
for (r=0; r<h; r++) {
vid_ptr2 = vid_ptr;
tile_ptr2 = tile_ptr;
for (c=0; c<w; c++) {
colour = *(vid_ptr2++);
colour |= *(vid_ptr2++) << 8;
colour = MAKE_COLOUR(
colour & 0x001f, 5, info.blue_shift, info.blue_mask,
colour & 0x07e0, 11, info.green_shift, info.green_mask,
colour & 0xf800, 16, info.red_shift, info.red_mask);
if (info.is_little_endian) {
for (i=0; i<info.bpp; i+=8) {
*(tile_ptr2++) = (Bit8u)(colour >> i);
}
}
else {
for (i=info.bpp-8; i>-8; i-=8) {
*(tile_ptr2++) = (Bit8u)(colour >> i);
}
}
}
vid_ptr += pitch;
tile_ptr += info.pitch;
}
bx_gui->graphics_tile_update_in_place(xc, yc, w, h);
SET_TILE_UPDATED (xti, yti, 0);
}
}
}
break;
case 24:
for (yc=0, yti = 0; yc<iHeight; yc+=Y_TILESIZE, yti++) {
for (xc=0, xti = 0; xc<iWidth; xc+=X_TILESIZE, xti++) {
if (GET_TILE_UPDATED (xti, yti)) {
vid_ptr = disp_ptr + (yc * pitch + 3*xc);
tile_ptr = bx_gui->graphics_tile_get(xc, yc, &w, &h);
for (r=0; r<h; r++) {
vid_ptr2 = vid_ptr;
tile_ptr2 = tile_ptr;
for (c=0; c<w; c++) {
blue = *(vid_ptr2++);
green = *(vid_ptr2++);
red = *(vid_ptr2++);
colour = MAKE_COLOUR(
red, 8, info.red_shift, info.red_mask,
green, 8, info.green_shift, info.green_mask,
blue, 8, info.blue_shift, info.blue_mask);
if (info.is_little_endian) {
for (i=0; i<info.bpp; i+=8) {
*(tile_ptr2++) = (Bit8u)(colour >> i);
}
}
else {
for (i=info.bpp-8; i>-8; i-=8) {
*(tile_ptr2++) = (Bit8u)(colour >> i);
}
}
}
vid_ptr += pitch;
tile_ptr += info.pitch;
}
bx_gui->graphics_tile_update_in_place(xc, yc, w, h);
SET_TILE_UPDATED (xti, yti, 0);
}
}
}
break;
case 32:
for (yc=0, yti = 0; yc<iHeight; yc+=Y_TILESIZE, yti++) {
for (xc=0, xti = 0; xc<iWidth; xc+=X_TILESIZE, xti++) {
if (GET_TILE_UPDATED (xti, yti)) {
vid_ptr = disp_ptr + (yc * pitch + (xc<<2));
tile_ptr = bx_gui->graphics_tile_get(xc, yc, &w, &h);
for (r=0; r<h; r++) {
vid_ptr2 = vid_ptr;
tile_ptr2 = tile_ptr;
for (c=0; c<w; c++) {
blue = *(vid_ptr2++);
green = *(vid_ptr2++);
red = *(vid_ptr2++);
vid_ptr2++;
colour = MAKE_COLOUR(
red, 8, info.red_shift, info.red_mask,
green, 8, info.green_shift, info.green_mask,
blue, 8, info.blue_shift, info.blue_mask);
if (info.is_little_endian) {
for (i=0; i<info.bpp; i+=8) {
*(tile_ptr2++) = (Bit8u)(colour >> i);
}
}
else {
for (i=info.bpp-8; i>-8; i-=8) {
*(tile_ptr2++) = (Bit8u)(colour >> i);
}
}
}
vid_ptr += pitch;
tile_ptr += info.pitch;
}
bx_gui->graphics_tile_update_in_place(xc, yc, w, h);
SET_TILE_UPDATED (xti, yti, 0);
}
}
}
break;
}
}
BX_VGA_THIS s.last_xres = iWidth;
BX_VGA_THIS s.last_yres = iHeight;
BX_VGA_THIS s.vga_mem_updated = 0;
}
else {
BX_PANIC(("cannot get svga tile info"));
}
} else if ((BX_VGA_THIS vbe.enabled) && (BX_VGA_THIS vbe.bpp == VBE_DISPI_BPP_4)) {
unsigned r, c, x, y;
unsigned xc, yc, xti, yti;
Bit8u *plane[4];
BX_VGA_THIS determine_screen_dimensions(&iHeight, &iWidth);
if ((iWidth != BX_VGA_THIS s.last_xres) || (iHeight != BX_VGA_THIS s.last_yres) ||
(BX_VGA_THIS s.last_bpp > 8))
{
bx_gui->dimension_update(iWidth, iHeight);
BX_VGA_THIS s.last_xres = iWidth;
BX_VGA_THIS s.last_yres = iHeight;
BX_VGA_THIS s.last_bpp = 8;
}
plane[0] = &BX_VGA_THIS s.memory[0<<VBE_DISPI_4BPP_PLANE_SHIFT];
plane[1] = &BX_VGA_THIS s.memory[1<<VBE_DISPI_4BPP_PLANE_SHIFT];
plane[2] = &BX_VGA_THIS s.memory[2<<VBE_DISPI_4BPP_PLANE_SHIFT];
plane[3] = &BX_VGA_THIS s.memory[3<<VBE_DISPI_4BPP_PLANE_SHIFT];
for (yc=0, yti=0; yc<iHeight; yc+=Y_TILESIZE, yti++) {
for (xc=0, xti=0; xc<iWidth; xc+=X_TILESIZE, xti++) {
if (GET_TILE_UPDATED (xti, yti)) {
for (r=0; r<Y_TILESIZE; r++) {
y = yc + r;
if (BX_VGA_THIS s.y_doublescan) y >>= 1;
for (c=0; c<X_TILESIZE; c++) {
x = xc + c;
BX_VGA_THIS s.tile[r*X_TILESIZE + c] =
BX_VGA_THIS get_vga_pixel(x, y, BX_VGA_THIS vbe.virtual_start, 0xffff, plane);
}
}
SET_TILE_UPDATED (xti, yti, 0);
bx_gui->graphics_tile_update(BX_VGA_THIS s.tile, xc, yc);
}
}
}
} else {
BX_VGA_THIS bx_vgacore_c::update();
}
}
bx_bool bx_vga_c::mem_read_handler(bx_phy_address addr, unsigned len, void *data, void *param)
{
Bit8u *data_ptr;
#ifdef BX_LITTLE_ENDIAN
data_ptr = (Bit8u *) data;
#else // BX_BIG_ENDIAN
data_ptr = (Bit8u *) data + (len - 1);
#endif
for (unsigned i = 0; i < len; i++) {
*data_ptr = theVga->mem_read(addr);
addr++;
#ifdef BX_LITTLE_ENDIAN
data_ptr++;
#else // BX_BIG_ENDIAN
data_ptr--;
#endif
}
return 1;
}
Bit8u bx_vga_c::mem_read(bx_phy_address addr)
{
#if BX_SUPPORT_PCI
if ((BX_VGA_THIS pci_enabled) && (BX_VGA_THIS pci_rom_size > 0)) {
Bit32u mask = (BX_VGA_THIS pci_rom_size - 1);
if ((addr & ~mask) == BX_VGA_THIS pci_rom_address) {
if (BX_VGA_THIS pci_conf[0x30] & 0x01) {
return BX_VGA_THIS pci_rom[addr & mask];
} else {
return 0xff;
}
}
}
#endif
// if in a vbe enabled mode, read from the vbe_memory
if ((BX_VGA_THIS vbe.enabled) && (BX_VGA_THIS vbe.bpp != VBE_DISPI_BPP_4)) {
return vbe_mem_read(addr);
} else if ((BX_VGA_THIS vbe.base_address != 0) && (addr >= BX_VGA_THIS vbe.base_address)) {
return 0xff;
}
return bx_vgacore_c::mem_read(addr);
}
bx_bool bx_vga_c::mem_write_handler(bx_phy_address addr, unsigned len, void *data, void *param)
{
Bit8u *data_ptr;
#ifdef BX_LITTLE_ENDIAN
data_ptr = (Bit8u *) data;
#else // BX_BIG_ENDIAN
data_ptr = (Bit8u *) data + (len - 1);
#endif
for (unsigned i = 0; i < len; i++) {
theVga->mem_write(addr, *data_ptr);
addr++;
#ifdef BX_LITTLE_ENDIAN
data_ptr++;
#else // BX_BIG_ENDIAN
data_ptr--;
#endif
}
return 1;
}
void bx_vga_c::mem_write(bx_phy_address addr, Bit8u value)
{
// if in a vbe enabled mode, write to the vbe_memory
if ((BX_VGA_THIS vbe.enabled) && (BX_VGA_THIS vbe.bpp != VBE_DISPI_BPP_4)) {
vbe_mem_write(addr, value);
return;
} else if ((BX_VGA_THIS vbe.base_address != 0) && (addr >= BX_VGA_THIS vbe.base_address)) {
return;
}
bx_vgacore_c::mem_write(addr, value);
}
int bx_vga_c::get_snapshot_mode()
{
if ((BX_VGA_THIS vbe.enabled) && (BX_VGA_THIS vbe.bpp >= 8)) {
return BX_GUI_SNAPSHOT_GFX;
} else {
return bx_vgacore_c::get_snapshot_mode();
}
}
Bit32u bx_vga_c::get_gfx_snapshot(Bit8u **snapshot_ptr, Bit8u **palette_ptr,
unsigned *iHeight, unsigned *iWidth, unsigned *iDepth)
{
Bit32u len, len1;
unsigned i, shift;
Bit8u *dst_ptr, *src_ptr;
if ((BX_VGA_THIS vbe.enabled) && (BX_VGA_THIS vbe.bpp >= 8)) {
*iHeight = BX_VGA_THIS vbe.yres;
*iWidth = BX_VGA_THIS vbe.xres;
*iDepth = BX_VGA_THIS vbe.bpp;
len1 = BX_VGA_THIS vbe.xres * BX_VGA_THIS vbe.bpp_multiplier;
len = len1 * BX_VGA_THIS vbe.yres;
*snapshot_ptr = (Bit8u*)malloc(len);
if (snapshot_ptr == NULL)
return 0;
src_ptr = BX_VGA_THIS s.memory + BX_VGA_THIS vbe.virtual_start;
dst_ptr = *snapshot_ptr;
for (i = 0; i < BX_VGA_THIS vbe.yres; i++) {
memcpy(dst_ptr, src_ptr, len1);
src_ptr += BX_VGA_THIS s.line_offset;
dst_ptr += len1;
}
if (*iDepth == 8) {
if (BX_VGA_THIS vbe.dac_8bit) {
shift = 0;
} else {
shift = 2;
}
BX_VGA_THIS get_dac_palette(palette_ptr, shift);
}
return len;
} else {
return bx_vgacore_c::get_gfx_snapshot(snapshot_ptr, palette_ptr, iHeight, iWidth, iDepth);
}
}
void bx_vga_c::redraw_area(unsigned x0, unsigned y0, unsigned width,
unsigned height)
{
unsigned xti, yti, xt0, xt1, yt0, yt1, xmax, ymax;
if (width == 0 || height == 0) {
return;
}
BX_VGA_THIS s.vga_mem_updated = 1;
if (BX_VGA_THIS vbe.enabled) {
xmax = BX_VGA_THIS vbe.xres;
ymax = BX_VGA_THIS vbe.yres;
xt0 = x0 / X_TILESIZE;
yt0 = y0 / Y_TILESIZE;
if (x0 < xmax) {
xt1 = (x0 + width - 1) / X_TILESIZE;
} else {
xt1 = (xmax - 1) / X_TILESIZE;
}
if (y0 < ymax) {
yt1 = (y0 + height - 1) / Y_TILESIZE;
} else {
yt1 = (ymax - 1) / Y_TILESIZE;
}
for (yti=yt0; yti<=yt1; yti++) {
for (xti=xt0; xti<=xt1; xti++) {
SET_TILE_UPDATED(xti, yti, 1);
}
}
} else {
bx_vgacore_c::redraw_area(x0, y0, width, height);
}
}
bx_bool bx_vga_c::vbe_set_base_addr(Bit32u *addr, Bit8u *pci_conf)
{
if (DEV_pci_set_base_mem(BX_VGA_THIS_PTR, mem_read_handler,
mem_write_handler,
addr, pci_conf, VBE_DISPI_TOTAL_VIDEO_MEMORY_BYTES)) {
BX_VGA_THIS vbe.base_address = *addr;
return 1;
}
return 0;
}
Bit8u BX_CPP_AttrRegparmN(1)
bx_vga_c::vbe_mem_read(bx_phy_address addr)
{
Bit32u offset;
if (addr >= BX_VGA_THIS vbe.base_address) {
// LFB read
offset = (Bit32u)(addr - BX_VGA_THIS vbe.base_address);
}
else {
// banked mode read
offset = (Bit32u)(BX_VGA_THIS vbe.bank*65536 + addr - 0xA0000);
}
// check for out of memory read
if (offset > VBE_DISPI_TOTAL_VIDEO_MEMORY_BYTES)
return 0;
return (BX_VGA_THIS s.memory[offset]);
}
void BX_CPP_AttrRegparmN(2)
bx_vga_c::vbe_mem_write(bx_phy_address addr, Bit8u value)
{
Bit32u offset;
unsigned x_tileno, y_tileno;
if (BX_VGA_THIS vbe.lfb_enabled)
{
if (addr >= BX_VGA_THIS vbe.base_address) {
// LFB write
offset = (Bit32u)(addr - BX_VGA_THIS vbe.base_address);
}
else {
// banked mode write while in LFB mode -> ignore
return;
}
}
else
{
if (addr < BX_VGA_THIS vbe.base_address) {
// banked mode write
offset = (Bit32u)(BX_VGA_THIS vbe.bank*65536 + (addr - 0xA0000));
}
else {
// LFB write while in banked mode -> ignore
return;
}
}
// check for out of memory write
if (offset < VBE_DISPI_TOTAL_VIDEO_MEMORY_BYTES)
{
BX_VGA_THIS s.memory[offset]=value;
}
else
{
// make sure we don't flood the logfile
static int count=0;
if (count<100)
{
count ++;
BX_INFO(("VBE_mem_write out of video memory write at %x",offset));
}
}
offset-=BX_VGA_THIS vbe.virtual_start;
// only update the UI when writing 'onscreen'
if (offset < BX_VGA_THIS vbe.visible_screen_size)
{
y_tileno = ((offset / BX_VGA_THIS vbe.bpp_multiplier) / BX_VGA_THIS vbe.virtual_xres) / Y_TILESIZE;
x_tileno = ((offset / BX_VGA_THIS vbe.bpp_multiplier) % BX_VGA_THIS vbe.virtual_xres) / X_TILESIZE;
if ((y_tileno < BX_VGA_THIS s.num_y_tiles) && (x_tileno < BX_VGA_THIS s.num_x_tiles))
{
BX_VGA_THIS s.vga_mem_updated = 1;
SET_TILE_UPDATED (x_tileno, y_tileno, 1);
}
}
}
Bit32u bx_vga_c::vbe_read_handler(void *this_ptr, Bit32u address, unsigned io_len)
{
#if BX_USE_VGA_SMF == 0
bx_vga_c *class_ptr = (bx_vga_c *) this_ptr;
return class_ptr->vbe_read(address, io_len);
}
Bit32u bx_vga_c::vbe_read(Bit32u address, unsigned io_len)
{
#else
UNUSED(this_ptr);
#endif // BX_USE_VGA_SMF == 0
Bit16u retval;
// BX_INFO(("VBE_read %x (len %x)", address, io_len));
if (address==VBE_DISPI_IOPORT_INDEX)
{
// index register
return (Bit32u) BX_VGA_THIS vbe.curindex;
}
else
{
// data register read
switch (BX_VGA_THIS vbe.curindex)
{
case VBE_DISPI_INDEX_ID: // Display Interface ID check
return BX_VGA_THIS vbe.cur_dispi;
case VBE_DISPI_INDEX_XRES: // x resolution
if (BX_VGA_THIS vbe.get_capabilities) {
return BX_VGA_THIS vbe.max_xres;
} else {
return BX_VGA_THIS vbe.xres;
}
case VBE_DISPI_INDEX_YRES: // y resolution
if (BX_VGA_THIS vbe.get_capabilities) {
return BX_VGA_THIS vbe.max_yres;
} else {
return BX_VGA_THIS vbe.yres;
}
case VBE_DISPI_INDEX_BPP: // bpp
if (BX_VGA_THIS vbe.get_capabilities) {
return BX_VGA_THIS vbe.max_bpp;
} else {
return BX_VGA_THIS vbe.bpp;
}
case VBE_DISPI_INDEX_ENABLE: // vbe enabled
retval = BX_VGA_THIS vbe.enabled;
if (BX_VGA_THIS vbe.get_capabilities)
retval |= VBE_DISPI_GETCAPS;
if (BX_VGA_THIS vbe.dac_8bit)
retval |= VBE_DISPI_8BIT_DAC;
return retval;
case VBE_DISPI_INDEX_BANK: // current bank
return BX_VGA_THIS vbe.bank;
case VBE_DISPI_INDEX_X_OFFSET:
return BX_VGA_THIS vbe.offset_x;
case VBE_DISPI_INDEX_Y_OFFSET:
return BX_VGA_THIS vbe.offset_y;
case VBE_DISPI_INDEX_VIRT_WIDTH:
return BX_VGA_THIS vbe.virtual_xres;
case VBE_DISPI_INDEX_VIRT_HEIGHT:
return BX_VGA_THIS vbe.virtual_yres;
case VBE_DISPI_INDEX_VIDEO_MEMORY_64K:
return (VBE_DISPI_TOTAL_VIDEO_MEMORY_KB >> 6);
default:
BX_PANIC(("VBE unknown data read index 0x%x",BX_VGA_THIS vbe.curindex));
break;
}
}
BX_PANIC(("VBE_read shouldn't reach this"));
return 0; /* keep compiler happy */
}
void bx_vga_c::vbe_write_handler(void *this_ptr, Bit32u address, Bit32u value, unsigned io_len)
{
#if BX_USE_VGA_SMF == 0
bx_vga_c *class_ptr = (bx_vga_c *) this_ptr;
class_ptr->vbe_write(address, value, io_len);
}
Bit32u bx_vga_c::vbe_write(Bit32u address, Bit32u value, unsigned io_len)
{
#else
UNUSED(this_ptr);
#endif
bx_bool new_vbe_8bit_dac;
bx_bool needs_update = 0;
unsigned i;
// BX_INFO(("VBE_write %x = %x (len %x)", address, value, io_len));
switch(address)
{
// index register
case VBE_DISPI_IOPORT_INDEX:
BX_VGA_THIS vbe.curindex = (Bit16u) value;
break;
// data register
// FIXME: maybe do some 'sanity' checks on received data?
case VBE_DISPI_IOPORT_DATA:
switch (BX_VGA_THIS vbe.curindex)
{
case VBE_DISPI_INDEX_ID: // Display Interface ID check
{
if ((value == VBE_DISPI_ID0) ||
(value == VBE_DISPI_ID1) ||
(value == VBE_DISPI_ID2) ||
(value == VBE_DISPI_ID3) ||
(value == VBE_DISPI_ID4) ||
(value == VBE_DISPI_ID5))
{
// allow backwards compatible with previous dispi bioses
BX_VGA_THIS vbe.cur_dispi=value;
}
else
{
BX_PANIC(("VBE unknown Display Interface %x", value));
}
// make sure we don't flood the logfile
static int count=0;
if (count < 100)
{
count++;
BX_INFO(("VBE known Display Interface %x", value));
}
} break;
case VBE_DISPI_INDEX_XRES: // set xres
{
// check that we don't set xres during vbe enabled
if (!BX_VGA_THIS vbe.enabled)
{
// check for within max xres range
if (value <= VBE_DISPI_MAX_XRES)
{
BX_VGA_THIS vbe.xres=(Bit16u) value;
BX_INFO(("VBE set xres (%d)", value));
}
else
{
BX_INFO(("VBE set xres more then max xres (%d)", value));
}
}
else
{
BX_ERROR(("VBE set xres during vbe enabled!"));
}
} break;
case VBE_DISPI_INDEX_YRES: // set yres
{
// check that we don't set yres during vbe enabled
if (!BX_VGA_THIS vbe.enabled)
{
// check for within max yres range
if (value <= VBE_DISPI_MAX_YRES)
{
BX_VGA_THIS vbe.yres=(Bit16u) value;
BX_INFO(("VBE set yres (%d)", value));
}
else
{
BX_INFO(("VBE set yres more then max yres (%d)", value));
}
}
else
{
BX_ERROR(("VBE set yres during vbe enabled!"));
}
} break;
case VBE_DISPI_INDEX_BPP: // set bpp
{
// check that we don't set bpp during vbe enabled
if (!BX_VGA_THIS vbe.enabled)
{
// for backward compatiblity
if (value == 0) value = VBE_DISPI_BPP_8;
// check for correct bpp range
if ((value == VBE_DISPI_BPP_4) || (value == VBE_DISPI_BPP_8) || (value == VBE_DISPI_BPP_15) ||
(value == VBE_DISPI_BPP_16) || (value == VBE_DISPI_BPP_24) || (value == VBE_DISPI_BPP_32))
{
BX_VGA_THIS vbe.bpp=(Bit16u) value;
BX_INFO(("VBE set bpp (%d)", value));
}
else
{
BX_ERROR(("VBE set bpp with unknown bpp (%d)", value));
}
}
else
{
BX_ERROR(("VBE set bpp during vbe enabled!"));
}
} break;
case VBE_DISPI_INDEX_BANK: // set bank
{
value=value & 0xff; // FIXME lobyte = vbe bank A?
unsigned divider = (BX_VGA_THIS vbe.bpp!=VBE_DISPI_BPP_4)?64:256;
// check for max bank nr
if (value < (VBE_DISPI_TOTAL_VIDEO_MEMORY_KB / divider))
{
if (!BX_VGA_THIS vbe.lfb_enabled)
{
BX_DEBUG(("VBE set bank to %d", value));
BX_VGA_THIS vbe.bank = value;
BX_VGA_THIS s.plane_offset = (BX_VGA_THIS vbe.bank << 16);
}
else
{
BX_ERROR(("VBE set bank in LFB mode ignored"));
}
}
else
{
BX_ERROR(("VBE set invalid bank (%d)", value));
}
} break;
case VBE_DISPI_INDEX_ENABLE: // enable video
{
if ((value & VBE_DISPI_ENABLED) && !BX_VGA_THIS vbe.enabled)
{
unsigned depth=0;
// setup virtual resolution to be the same as current reso
BX_VGA_THIS vbe.virtual_yres=BX_VGA_THIS vbe.yres;
BX_VGA_THIS vbe.virtual_xres=BX_VGA_THIS vbe.xres;
// reset offset
BX_VGA_THIS vbe.offset_x=0;
BX_VGA_THIS vbe.offset_y=0;
BX_VGA_THIS vbe.virtual_start=0;
switch((BX_VGA_THIS vbe.bpp))
{
// Default pixel sizes
case VBE_DISPI_BPP_8:
BX_VGA_THIS vbe.bpp_multiplier = 1;
BX_VGA_THIS s.line_offset = BX_VGA_THIS vbe.virtual_xres;
depth=8;
break;
case VBE_DISPI_BPP_4:
BX_VGA_THIS vbe.bpp_multiplier = 1;
BX_VGA_THIS s.line_offset = (BX_VGA_THIS vbe.virtual_xres >> 3);
depth=4;
break;
case VBE_DISPI_BPP_15:
BX_VGA_THIS vbe.bpp_multiplier = 2;
BX_VGA_THIS s.line_offset = BX_VGA_THIS vbe.virtual_xres * 2;
depth=15;
break;
case VBE_DISPI_BPP_16:
BX_VGA_THIS vbe.bpp_multiplier = 2;
BX_VGA_THIS s.line_offset = BX_VGA_THIS vbe.virtual_xres * 2;
depth=16;
break;
case VBE_DISPI_BPP_24:
BX_VGA_THIS vbe.bpp_multiplier = 3;
BX_VGA_THIS s.line_offset = BX_VGA_THIS vbe.virtual_xres * 3;
depth=24;
break;
case VBE_DISPI_BPP_32:
BX_VGA_THIS vbe.bpp_multiplier = 4;
BX_VGA_THIS s.line_offset = BX_VGA_THIS vbe.virtual_xres << 2;
depth=32;
break;
}
BX_VGA_THIS vbe.visible_screen_size = BX_VGA_THIS s.line_offset * BX_VGA_THIS vbe.yres;
BX_INFO(("VBE enabling x %d, y %d, bpp %d, %u bytes visible", BX_VGA_THIS vbe.xres, BX_VGA_THIS vbe.yres, BX_VGA_THIS vbe.bpp, BX_VGA_THIS vbe.visible_screen_size));
if (depth > 4) {
BX_VGA_THIS vbe.lfb_enabled = (bx_bool)(value & VBE_DISPI_LFB_ENABLED);
if ((value & VBE_DISPI_NOCLEARMEM) == 0) {
memset(BX_VGA_THIS s.memory, 0, BX_VGA_THIS vbe.visible_screen_size);
}
bx_gui->dimension_update(BX_VGA_THIS vbe.xres, BX_VGA_THIS vbe.yres, 0, 0, depth);
BX_VGA_THIS s.last_bpp = depth;
} else {
BX_VGA_THIS s.plane_shift = VBE_DISPI_4BPP_PLANE_SHIFT;
BX_VGA_THIS s.plane_offset = (BX_VGA_THIS vbe.bank << 16);
}
} else if (((value & VBE_DISPI_ENABLED) == 0) && BX_VGA_THIS vbe.enabled) {
BX_INFO(("VBE disabling"));
BX_VGA_THIS vbe.lfb_enabled = 0;
BX_VGA_THIS s.plane_shift = 16;
BX_VGA_THIS s.plane_offset = 0;
}
BX_VGA_THIS vbe.enabled = (bx_bool)(value & VBE_DISPI_ENABLED);
BX_VGA_THIS vbe.get_capabilities = (bx_bool)((value & VBE_DISPI_GETCAPS) != 0);
new_vbe_8bit_dac = (bx_bool)((value & VBE_DISPI_8BIT_DAC) != 0);
if (new_vbe_8bit_dac != BX_VGA_THIS vbe.dac_8bit) {
if (new_vbe_8bit_dac) {
for (i=0; i<256; i++) {
BX_VGA_THIS s.pel.data[i].red <<= 2;
BX_VGA_THIS s.pel.data[i].green <<= 2;
BX_VGA_THIS s.pel.data[i].blue <<= 2;
}
BX_INFO(("DAC in 8 bit mode"));
} else {
for (i=0; i<256; i++) {
BX_VGA_THIS s.pel.data[i].red >>= 2;
BX_VGA_THIS s.pel.data[i].green >>= 2;
BX_VGA_THIS s.pel.data[i].blue >>= 2;
}
BX_INFO(("DAC in standard mode"));
}
BX_VGA_THIS vbe.dac_8bit = new_vbe_8bit_dac;
BX_VGA_THIS s.dac_shift = new_vbe_8bit_dac ? 0 : 2;
needs_update = 1;
}
} break;
case VBE_DISPI_INDEX_X_OFFSET:
{
BX_DEBUG(("VBE offset x %d", value));
BX_VGA_THIS vbe.offset_x=(Bit16u)value;
BX_VGA_THIS vbe.virtual_start = BX_VGA_THIS vbe.offset_y * BX_VGA_THIS s.line_offset;
if (BX_VGA_THIS vbe.bpp != VBE_DISPI_BPP_4) {
BX_VGA_THIS vbe.virtual_start += (BX_VGA_THIS vbe.offset_x * BX_VGA_THIS vbe.bpp_multiplier);
} else {
BX_VGA_THIS vbe.virtual_start += (BX_VGA_THIS vbe.offset_x >> 3);
}
needs_update = 1;
} break;
case VBE_DISPI_INDEX_Y_OFFSET:
{
BX_DEBUG(("VBE offset y %d", value));
Bit32u new_screen_start = value * BX_VGA_THIS s.line_offset;
if (BX_VGA_THIS vbe.bpp != VBE_DISPI_BPP_4) {
if ((new_screen_start + BX_VGA_THIS vbe.visible_screen_size) > VBE_DISPI_TOTAL_VIDEO_MEMORY_BYTES)
{
BX_PANIC(("VBE offset y %d out of bounds", value));
break;
}
new_screen_start += (BX_VGA_THIS vbe.offset_x * BX_VGA_THIS vbe.bpp_multiplier);
} else {
if ((new_screen_start + BX_VGA_THIS vbe.visible_screen_size) > (VBE_DISPI_TOTAL_VIDEO_MEMORY_BYTES / 4))
{
BX_PANIC(("VBE offset y %d out of bounds", value));
break;
}
new_screen_start += (BX_VGA_THIS vbe.offset_x >> 3);
}
BX_VGA_THIS vbe.virtual_start = new_screen_start;
BX_VGA_THIS vbe.offset_y = (Bit16u)value;
needs_update = 1;
} break;
case VBE_DISPI_INDEX_VIRT_WIDTH:
{
BX_INFO(("VBE requested virtual width %d", value));
// calculate virtual width & height dimensions
// req:
// virt_width > xres
// virt_height >=yres
// virt_width*virt_height < MAX_VIDEO_MEMORY
// basicly 2 situations
// situation 1:
// MAX_VIDEO_MEMORY / virt_width >= yres
// adjust result height
// else
// adjust result width based upon virt_height=yres
Bit16u new_width=value;
Bit16u new_height;
if (BX_VGA_THIS vbe.bpp != VBE_DISPI_BPP_4) {
new_height=(VBE_DISPI_TOTAL_VIDEO_MEMORY_BYTES / BX_VGA_THIS vbe.bpp_multiplier) / new_width;
} else {
new_height=(VBE_DISPI_TOTAL_VIDEO_MEMORY_BYTES * 2) / new_width;
}
if (new_height >=BX_VGA_THIS vbe.yres)
{
// we have a decent virtual width & new_height
BX_INFO(("VBE decent virtual height %d",new_height));
}
else
{
// no decent virtual height: adjust width & height
new_height=BX_VGA_THIS vbe.yres;
if (BX_VGA_THIS vbe.bpp != VBE_DISPI_BPP_4) {
new_width=(VBE_DISPI_TOTAL_VIDEO_MEMORY_BYTES / BX_VGA_THIS vbe.bpp_multiplier) / new_height;
} else {
new_width=(VBE_DISPI_TOTAL_VIDEO_MEMORY_BYTES * 2) / new_height;
}
BX_INFO(("VBE recalc virtual width %d height %d",new_width, new_height));
}
BX_VGA_THIS vbe.virtual_xres=new_width;
BX_VGA_THIS vbe.virtual_yres=new_height;
if (BX_VGA_THIS vbe.bpp != VBE_DISPI_BPP_4) {
BX_VGA_THIS s.line_offset = BX_VGA_THIS vbe.virtual_xres * BX_VGA_THIS vbe.bpp_multiplier;
} else {
BX_VGA_THIS s.line_offset = BX_VGA_THIS vbe.virtual_xres >> 3;
}
BX_VGA_THIS vbe.visible_screen_size = BX_VGA_THIS s.line_offset * BX_VGA_THIS vbe.yres;
} break;
case VBE_DISPI_INDEX_VIRT_HEIGHT:
BX_ERROR(("VBE: write to virtual height register ignored"));
break;
default:
BX_ERROR(("VBE: write unsupported register at index 0x%x",BX_VGA_THIS vbe.curindex));
break;
}
if (needs_update) {
BX_VGA_THIS s.vga_mem_updated = 1;
for (unsigned xti = 0; xti < BX_VGA_THIS s.num_x_tiles; xti++) {
for (unsigned yti = 0; yti < BX_VGA_THIS s.num_y_tiles; yti++) {
SET_TILE_UPDATED (xti, yti, 1);
}
}
}
break;
} // end switch address
}
#if BX_SUPPORT_PCI
// pci configuration space read callback handler
Bit32u bx_vga_c::pci_read_handler(Bit8u address, unsigned io_len)
{
Bit32u value = 0;
for (unsigned i=0; i<io_len; i++) {
value |= (BX_VGA_THIS pci_conf[address+i] << (i*8));
}
if (io_len == 1)
BX_DEBUG(("read PCI register 0x%02x value 0x%02x", address, value));
else if (io_len == 2)
BX_DEBUG(("read PCI register 0x%02x value 0x%04x", address, value));
else if (io_len == 4)
BX_DEBUG(("read PCI register 0x%02x value 0x%08x", address, value));
return value;
}
// static pci configuration space write callback handler
void bx_vga_c::pci_write_handler(Bit8u address, Bit32u value, unsigned io_len)
{
bx_bool baseaddr_change = 0, romaddr_change = 0;
if (io_len == 1)
BX_DEBUG(("write PCI register 0x%02x value 0x%02x", address, value));
else if (io_len == 2)
BX_DEBUG(("write PCI register 0x%02x value 0x%04x", address, value));
else if (io_len == 4)
BX_DEBUG(("write PCI register 0x%02x value 0x%08x", address, value));
if ((address >= 0x14) && (address < 0x30))
return;
if (address == 0x30) {
value = value & 0xfffffc01;
romaddr_change = 1;
}
for (unsigned i = 0; i < io_len; i++) {
unsigned write_addr = address + i;
Bit8u old_value = BX_VGA_THIS pci_conf[write_addr];
Bit8u new_value = (Bit8u)(value & 0xff);
switch (write_addr) {
case 0x04: // disallowing write to command
case 0x06: // disallowing write to status lo-byte (is that expected?)
break;
case 0x10: // base address #0
new_value = (new_value & 0xf0) | (old_value & 0x0f);
case 0x11: case 0x12: case 0x13:
if (BX_VGA_THIS vbe_present) {
baseaddr_change |= (old_value != new_value);
} else {
break;
}
default:
BX_VGA_THIS pci_conf[write_addr] = new_value;
}
value >>= 8;
}
if (baseaddr_change) {
if (BX_VGA_THIS vbe_set_base_addr(&BX_VGA_THIS pci_base_address[0],
&BX_VGA_THIS pci_conf[0x10])) {
BX_INFO(("new base address: 0x%08x", BX_VGA_THIS pci_base_address[0]));
}
}
if (romaddr_change) {
if (DEV_pci_set_base_mem(this, mem_read_handler, mem_write_handler,
&BX_VGA_THIS pci_rom_address,
&BX_VGA_THIS pci_conf[0x30],
BX_VGA_THIS pci_rom_size)) {
BX_INFO(("new ROM address: 0x%08x", BX_VGA_THIS pci_rom_address));
}
}
}
#endif