Bochs VBE graphics card, originally by Cacodemon345, then extensively cleaned-up and improved.

This commit is contained in:
OBattler
2024-04-28 16:47:32 +02:00
parent 048bb29665
commit f4d2102734
5 changed files with 959 additions and 13 deletions

View File

@@ -223,7 +223,7 @@ extern void video_screenshot_monitor(uint32_t *buf, int start_x, int start_y, in
extern void video_screenshot(uint32_t *buf, int start_x, int start_y, int row_len); extern void video_screenshot(uint32_t *buf, int start_x, int start_y, int row_len);
#ifdef _WIN32 #ifdef _WIN32
extern void *__cdecl (*video_copy)(void *_Dst, const void *_Src, size_t _Size); extern void * (__cdecl *video_copy)(void *_Dst, const void *_Src, size_t _Size);
extern void *__cdecl video_transform_copy(void *_Dst, const void *_Src, size_t _Size); extern void *__cdecl video_transform_copy(void *_Dst, const void *_Src, size_t _Size);
#else #else
extern void *(*video_copy)(void *__restrict _Dst, const void *__restrict _Src, size_t _Size); extern void *(*video_copy)(void *__restrict _Dst, const void *__restrict _Src, size_t _Size);
@@ -334,6 +334,13 @@ extern const device_t compaq_ati28800_device;
extern const device_t ati28800_wonderxl24_device; extern const device_t ati28800_wonderxl24_device;
# endif # endif
/* Bochs */
extern const device_t bochs_svga_device;
/* Chips & Technologies */
extern const device_t chips_69000_device;
extern const device_t chips_69000_onboard_device;
/* Cirrus Logic GD54xx */ /* Cirrus Logic GD54xx */
extern const device_t gd5401_isa_device; extern const device_t gd5401_isa_device;
extern const device_t gd5402_isa_device; extern const device_t gd5402_isa_device;
@@ -579,10 +586,6 @@ extern const device_t velocity_200_agp_device;
/* Wyse 700 */ /* Wyse 700 */
extern const device_t wy700_device; extern const device_t wy700_device;
/* Chips & Technologies */
extern const device_t chips_69000_device;
extern const device_t chips_69000_onboard_device;
#endif #endif
#endif /*EMU_VIDEO_H*/ #endif /*EMU_VIDEO_H*/

View File

@@ -26,7 +26,8 @@ add_library(vid OBJECT agpgart.c video.c vid_table.c vid_cga.c vid_cga_comp.c
vid_rtg310x.c vid_f82c425.c vid_ti_cf62011.c vid_tvga.c vid_tgui9440.c vid_rtg310x.c vid_f82c425.c vid_ti_cf62011.c vid_tvga.c vid_tgui9440.c
vid_tkd8001_ramdac.c vid_att20c49x_ramdac.c vid_s3.c vid_s3_virge.c vid_tkd8001_ramdac.c vid_att20c49x_ramdac.c vid_s3.c vid_s3_virge.c
vid_ibm_rgb528_ramdac.c vid_sdac_ramdac.c vid_ogc.c vid_mga.c vid_nga.c vid_ibm_rgb528_ramdac.c vid_sdac_ramdac.c vid_ogc.c vid_mga.c vid_nga.c
vid_tvp3026_ramdac.c vid_att2xc498_ramdac.c vid_xga.c) vid_tvp3026_ramdac.c vid_att2xc498_ramdac.c vid_xga.c
vid_bochs_vbe.c)
if(XL24) if(XL24)
target_compile_definitions(vid PRIVATE USE_XL24) target_compile_definitions(vid PRIVATE USE_XL24)

941
src/video/vid_bochs_vbe.c Normal file
View File

@@ -0,0 +1,941 @@
/*
* 86Box A hypervisor and IBM PC system emulator that specializes in
* running old operating systems and software designed for IBM
* PC systems and compatibles from 1981 through fairly recent
* system designs based on the PCI bus.
*
* This file is part of the 86Box distribution.
*
* Bochs VBE SVGA emulation.
*
* Uses code from libxcvt to calculate CRTC timings.
*
* Authors: Cacodemon345
* The Bochs Project
* Fabrice Bellard
* The libxcvt authors
*
* Copyright 2024 Cacodemon345
* Copyright 2003 Fabrice Bellard
* Copyright 2002-2024 The Bochs Project
* Copyright 2002-2003 Mike Nordell
* Copyright 2000-2021 The libxcvt authors
*
* See https://gitlab.freedesktop.org/xorg/lib/libxcvt/-/blob/master/COPYING for libxcvt license details
*/
#include <stdbool.h>
#include <stdio.h>
#include <stdint.h>
#include <string.h>
#include <stdlib.h>
#include <wchar.h>
#include <86box/86box.h>
#include <86box/io.h>
#include <86box/mem.h>
#include <86box/rom.h>
#include <86box/device.h>
#include <86box/timer.h>
#include <86box/video.h>
#include <86box/vid_svga.h>
#include <86box/vid_svga_render.h>
#include <86box/pci.h>
#include <86box/i2c.h>
#include <86box/vid_ddc.h>
#define VBE_DISPI_BANK_SIZE_KB 64
#define VBE_DISPI_BANK_GRANULARITY_KB 32
#define VBE_DISPI_MAX_XRES 1920
#define VBE_DISPI_MAX_YRES 1600
#define VBE_DISPI_IOPORT_INDEX 0x01CE
#define VBE_DISPI_IOPORT_DATA 0x01CF
#define VBE_DISPI_INDEX_ID 0x0
#define VBE_DISPI_INDEX_XRES 0x1
#define VBE_DISPI_INDEX_YRES 0x2
#define VBE_DISPI_INDEX_BPP 0x3
#define VBE_DISPI_INDEX_ENABLE 0x4
#define VBE_DISPI_INDEX_BANK 0x5
#define VBE_DISPI_INDEX_VIRT_WIDTH 0x6
#define VBE_DISPI_INDEX_VIRT_HEIGHT 0x7
#define VBE_DISPI_INDEX_X_OFFSET 0x8
#define VBE_DISPI_INDEX_Y_OFFSET 0x9
#define VBE_DISPI_INDEX_VIDEO_MEMORY_64K 0xa
#define VBE_DISPI_INDEX_DDC 0xb
#define VBE_DISPI_ID0 0xB0C0
#define VBE_DISPI_ID1 0xB0C1
#define VBE_DISPI_ID2 0xB0C2
#define VBE_DISPI_ID3 0xB0C3
#define VBE_DISPI_ID4 0xB0C4
#define VBE_DISPI_ID5 0xB0C5
#define VBE_DISPI_DISABLED 0x00
#define VBE_DISPI_ENABLED 0x01
#define VBE_DISPI_GETCAPS 0x02
#define VBE_DISPI_BANK_GRANULARITY_32K 0x10
#define VBE_DISPI_8BIT_DAC 0x20
#define VBE_DISPI_LFB_ENABLED 0x40
#define VBE_DISPI_NOCLEARMEM 0x80
#define VBE_DISPI_BANK_WR 0x4000
#define VBE_DISPI_BANK_RD 0x8000
#define VBE_DISPI_BANK_RW 0xc000
typedef struct vbe_mode_info_t {
uint32_t hdisplay;
uint32_t vdisplay;
float vrefresh;
float hsync;
uint64_t dot_clock;
uint16_t hsync_start;
uint16_t hsync_end;
uint16_t htotal;
uint16_t vsync_start;
uint16_t vsync_end;
uint16_t vtotal;
} vbe_mode_info_t;
static video_timings_t timing_bochs = { .type = VIDEO_PCI, .write_b = 2, .write_w = 2, .write_l = 1, .read_b = 20, .read_w = 20, .read_l = 21 };
typedef struct bochs_vbe_t {
svga_t svga;
uint8_t pci_conf_status;
uint8_t pci_rom_enable;
uint8_t pci_line_interrupt;
uint8_t slot;
uint8_t pci_regs[256];
uint16_t vbe_index;
uint16_t bank_gran;
uint16_t rom_addr;
uint16_t id5_val;
uint16_t vbe_regs[16];
uint32_t vram_size;
rom_t bios_rom;
mem_mapping_t linear_mapping;
mem_mapping_t linear_mapping_2;
void * i2c;
void * ddc;
} bochs_vbe_t;
static bochs_vbe_t *reset_state = NULL;
static void
gen_mode_info(int hdisplay, int vdisplay, float vrefresh, vbe_mode_info_t* mode_info)
{
int vsync;
int vsync_and_back_porch;
mode_info->hdisplay = hdisplay;
mode_info->vdisplay = vdisplay;
mode_info->vrefresh = vrefresh;
/* 1) top/bottom margin size (% of height) - default: 1.8 */
#define CVT_MARGIN_PERCENTAGE 1.8
/* 2) character cell horizontal granularity (pixels) - default 8 */
#define CVT_H_GRANULARITY 8
/* 4) Minimum vertical front porch (lines) - default 3 */
#define CVT_MIN_V_PORCH_RND 3
/* 4) Minimum number of vertical back porch lines - default 6 */
#define CVT_MIN_V_BPORCH 6
/* Pixel Clock step (kHz) */
#define CVT_CLOCK_STEP 250
/* CVT default is 60.0Hz */
if (!mode_info->vrefresh)
mode_info->vrefresh = 60.0;
/* 1. Required field rate */
const float vfield_rate = mode_info->vrefresh;
/* 2. Horizontal pixels */
const int hdisplay_rnd = mode_info->hdisplay - (mode_info->hdisplay % CVT_H_GRANULARITY);
/* 3. Determine left and right borders */
const int hmargin = 0;
/* 4. Find total active pixels */
mode_info->hdisplay = hdisplay_rnd + (2 * hmargin);
/* 5. Find number of lines per field */
const int vdisplay_rnd = mode_info->vdisplay;
/* 6. Find top and bottom margins */
const int vmargin = 0;
mode_info->vdisplay = mode_info->vdisplay + 2 * vmargin;
/* 7. interlace */
/* Please rename this */
const float interlace = 0.0;
/* Determine vsync Width from aspect ratio */
if (!(mode_info->vdisplay % 3) && ((mode_info->vdisplay * 4 / 3) == mode_info->hdisplay))
vsync = 4;
else if (!(mode_info->vdisplay % 9) &&
((mode_info->vdisplay * 16 / 9) == mode_info->hdisplay))
vsync = 5;
else if (!(mode_info->vdisplay % 10) &&
((mode_info->vdisplay * 16 / 10) == mode_info->hdisplay))
vsync = 6;
else if (!(mode_info->vdisplay % 4) &&
((mode_info->vdisplay * 5 / 4) == mode_info->hdisplay))
vsync = 7;
else if (!(mode_info->vdisplay % 9) &&
((mode_info->vdisplay * 15 / 9) == mode_info->hdisplay))
vsync = 7;
else /* Custom */
vsync = 10;
/* Simplified GTF calculation. */
/* 4) Minimum time of vertical sync + back porch interval (µs)
* default 550.0 */
#define CVT_MIN_VSYNC_BP 550.0
/* 3) Nominal HSync width (% of line period) - default 8 */
#define CVT_HSYNC_PERCENTAGE 8
/* 8. Estimated Horizontal period */
const float hperiod = ((float) (1000000.0 / vfield_rate - CVT_MIN_VSYNC_BP)) /
(vdisplay_rnd + 2 * vmargin + CVT_MIN_V_PORCH_RND + interlace);
/* 9. Find number of lines in sync + backporch */
if (((int) (CVT_MIN_VSYNC_BP / hperiod) + 1) < (vsync + CVT_MIN_V_BPORCH))
vsync_and_back_porch = vsync + CVT_MIN_V_BPORCH;
else
vsync_and_back_porch = (int) (CVT_MIN_VSYNC_BP / hperiod) + 1;
/* 10. Find number of lines in back porch */
/* 11. Find total number of lines in vertical field */
mode_info->vtotal = vdisplay_rnd + (2 * vmargin) + vsync_and_back_porch + interlace +
CVT_MIN_V_PORCH_RND;
/* 5) Definition of Horizontal blanking time limitation */
/* Gradient (%/kHz) - default 600 */
#define CVT_M_FACTOR 600
/* Offset (%) - default 40 */
#define CVT_C_FACTOR 40
/* Blanking time scaling factor - default 128 */
#define CVT_K_FACTOR 128
/* Scaling factor weighting - default 20 */
#define CVT_J_FACTOR 20
#define CVT_M_PRIME CVT_M_FACTOR * CVT_K_FACTOR / 256
#define CVT_C_PRIME (CVT_C_FACTOR - CVT_J_FACTOR) * CVT_K_FACTOR / 256 + \
CVT_J_FACTOR
/* 12. Find ideal blanking duty cycle from formula */
float hblank_percentage = CVT_C_PRIME - CVT_M_PRIME * hperiod / 1000.0;
/* 13. Blanking time */
if (hblank_percentage < 20)
hblank_percentage = 20;
int hblank = mode_info->hdisplay * hblank_percentage / (100.0 - hblank_percentage);
hblank -= hblank % (2 * CVT_H_GRANULARITY);
/* 14. Find total number of pixels in a line. */
mode_info->htotal = mode_info->hdisplay + hblank;
/* Fill in HSync values */
mode_info->hsync_end = mode_info->hdisplay + hblank / 2;
int hsync_w = (mode_info->htotal * CVT_HSYNC_PERCENTAGE) / 100;
hsync_w -= hsync_w % CVT_H_GRANULARITY;
mode_info->hsync_start = mode_info->hsync_end - hsync_w;
/* Fill in vsync values */
mode_info->vsync_start = mode_info->vdisplay + CVT_MIN_V_PORCH_RND;
mode_info->vsync_end = mode_info->vsync_start + vsync;
/* 15/13. Find pixel clock frequency (kHz for xf86) */
mode_info->dot_clock = mode_info->htotal * 1000.0 / hperiod;
mode_info->dot_clock -= mode_info->dot_clock % CVT_CLOCK_STEP;
/* 16/14. Find actual Horizontal Frequency (kHz) */
mode_info->hsync = ((float) mode_info->dot_clock) / ((float) mode_info->htotal);
/* 17/15. Find actual Field rate */
mode_info->vrefresh = (1000.0 * ((float) mode_info->dot_clock)) /
((float) (mode_info->htotal * mode_info->vtotal));
/* 18/16. Find actual vertical frame frequency */
/* ignore - we don't do interlace here */
}
void
bochs_vbe_recalctimings(svga_t* svga)
{
bochs_vbe_t *dev = (bochs_vbe_t *) svga->priv;
if (dev->vbe_regs[VBE_DISPI_INDEX_ENABLE] & VBE_DISPI_ENABLED) {
vbe_mode_info_t mode = { 0 };
svga->bpp = dev->vbe_regs[VBE_DISPI_INDEX_BPP];
dev->vbe_regs[VBE_DISPI_INDEX_XRES] &= ~7;
if (dev->vbe_regs[VBE_DISPI_INDEX_XRES] == 0) {
dev->vbe_regs[VBE_DISPI_INDEX_XRES] = 8;
}
if (dev->vbe_regs[VBE_DISPI_INDEX_VIRT_WIDTH] > VBE_DISPI_MAX_XRES)
dev->vbe_regs[VBE_DISPI_INDEX_VIRT_WIDTH] = VBE_DISPI_MAX_XRES;
if (dev->vbe_regs[VBE_DISPI_INDEX_VIRT_WIDTH] < dev->vbe_regs[VBE_DISPI_INDEX_XRES])
dev->vbe_regs[VBE_DISPI_INDEX_VIRT_WIDTH] = dev->vbe_regs[VBE_DISPI_INDEX_XRES];
if (dev->vbe_regs[VBE_DISPI_INDEX_X_OFFSET] > VBE_DISPI_MAX_XRES)
dev->vbe_regs[VBE_DISPI_INDEX_X_OFFSET] = VBE_DISPI_MAX_XRES;
if (dev->vbe_regs[VBE_DISPI_INDEX_Y_OFFSET] > VBE_DISPI_MAX_YRES)
dev->vbe_regs[VBE_DISPI_INDEX_Y_OFFSET] = VBE_DISPI_MAX_YRES;
if (dev->vbe_regs[VBE_DISPI_INDEX_YRES] == 0)
dev->vbe_regs[VBE_DISPI_INDEX_YRES] = 1;
if (dev->vbe_regs[VBE_DISPI_INDEX_YRES] > VBE_DISPI_MAX_YRES)
dev->vbe_regs[VBE_DISPI_INDEX_YRES] = VBE_DISPI_MAX_YRES;
gen_mode_info(dev->vbe_regs[VBE_DISPI_INDEX_XRES],
dev->vbe_regs[VBE_DISPI_INDEX_YRES], 72.f, &mode);
svga->char_width = 1;
svga->dots_per_clock = 1;
svga->clock = (cpuclock * (double) (1ULL << 32)) / (mode.dot_clock * 1000.);
svga->dispend = mode.vdisplay;
svga->hdisp = mode.hdisplay;
svga->vsyncstart = mode.vsync_start;
svga->vtotal = mode.vtotal;
svga->htotal = mode.htotal;
svga->hblankstart = mode.hdisplay;
svga->hblankend = mode.hdisplay + (mode.htotal - mode.hdisplay - 1);
svga->vblankstart = svga->dispend; /* no vertical overscan. */
svga->rowcount = 0;
if (dev->vbe_regs[VBE_DISPI_INDEX_BPP] != 4) {
svga->fb_only = 1;
svga->adv_flags |= FLAG_NO_SHIFT3;
} else {
svga->fb_only = 0;
svga->adv_flags &= ~FLAG_NO_SHIFT3;
}
svga->bpp = dev->vbe_regs[VBE_DISPI_INDEX_BPP];
if (svga->bpp == 4) {
svga->rowoffset = dev->vbe_regs[VBE_DISPI_INDEX_VIRT_WIDTH] >> 3;
svga->ma_latch = (dev->vbe_regs[VBE_DISPI_INDEX_Y_OFFSET] * svga->rowoffset) +
(dev->vbe_regs[VBE_DISPI_INDEX_X_OFFSET] >> 3);
} else {
svga->rowoffset = dev->vbe_regs[VBE_DISPI_INDEX_VIRT_WIDTH] * (svga->bpp / 8);
svga->ma_latch = (dev->vbe_regs[VBE_DISPI_INDEX_Y_OFFSET] * svga->rowoffset) +
(dev->vbe_regs[VBE_DISPI_INDEX_X_OFFSET] * (svga->bpp / 8));
}
if ((svga->ma_latch + dev->vbe_regs[VBE_DISPI_INDEX_YRES] * svga->rowoffset) >
svga->vram_max) {
dev->vbe_regs[VBE_DISPI_INDEX_Y_OFFSET] = 0;
svga->ma_latch = (svga->bpp == 4) ? (dev->vbe_regs[VBE_DISPI_INDEX_X_OFFSET] >> 3) :
(dev->vbe_regs[VBE_DISPI_INDEX_X_OFFSET] * (svga->bpp / 8));
if ((svga->ma_latch + dev->vbe_regs[VBE_DISPI_INDEX_YRES] * svga->rowoffset) >
svga->vram_max) {
svga->ma_latch = 0;
dev->vbe_regs[VBE_DISPI_INDEX_X_OFFSET] = 0;
}
}
svga->split = 0xffffff;
switch (svga->bpp) {
case 4:
svga->render = svga_render_4bpp_highres;
break;
default:
case 8:
svga->render = svga_render_8bpp_clone_highres;
break;
case 15:
svga->render = svga_render_15bpp_highres;
break;
case 16:
svga->render = svga_render_16bpp_highres;
break;
case 24:
svga->render = svga_render_24bpp_highres;
break;
case 32:
svga->render = svga_render_32bpp_highres;
break;
}
} else {
svga->fb_only = 0;
svga->packed_4bpp = 0;
svga->adv_flags &= ~FLAG_NO_SHIFT3;
}
}
uint16_t
bochs_vbe_inw(const uint16_t addr, void *priv)
{
const bochs_vbe_t *dev = (bochs_vbe_t *) priv;
const bool vbe_get_caps = !!(dev->vbe_regs[VBE_DISPI_INDEX_ENABLE] &
VBE_DISPI_GETCAPS);
uint16_t ret;
if (addr == 0x1ce)
ret = dev->vbe_index;
else switch (dev->vbe_index) {
default:
ret = dev->vbe_regs[dev->vbe_index];
break;
case VBE_DISPI_INDEX_XRES:
ret = vbe_get_caps ? VBE_DISPI_MAX_XRES : dev->vbe_regs[dev->vbe_index];
break;
case VBE_DISPI_INDEX_YRES:
ret = vbe_get_caps ? VBE_DISPI_MAX_YRES : dev->vbe_regs[dev->vbe_index];
break;
case VBE_DISPI_INDEX_BPP:
ret = vbe_get_caps ? 32 : dev->vbe_regs[dev->vbe_index];
break;
case VBE_DISPI_INDEX_VIDEO_MEMORY_64K:
ret = dev->vram_size >> 16;
break;
case VBE_DISPI_INDEX_BANK:
ret = vbe_get_caps ? (VBE_DISPI_BANK_GRANULARITY_32K << 8) :
dev->vbe_regs[dev->vbe_index];
break;
case VBE_DISPI_INDEX_DDC:
if (dev->vbe_regs[dev->vbe_index] & (1 << 7)) {
ret = dev->vbe_regs[dev->vbe_index] & ((1 << 7) | 0x3);
ret |= i2c_gpio_get_scl(dev->i2c) << 2;
ret |= i2c_gpio_get_sda(dev->i2c) << 3;
} else
ret = 0x000f;
break;
}
return ret;
}
uint32_t
bochs_vbe_inl(const uint16_t addr, void *priv)
{
const bochs_vbe_t *dev = (bochs_vbe_t *) priv;
uint32_t ret;
if (addr == 0x1ce)
ret = dev->vbe_index;
else
ret = dev->vram_size;
pclog("[%04X:%08X] [R] %04X = %08X\n", CS, cpu_state.pc, addr, ret);
return ret;
}
void
bochs_vbe_outw(const uint16_t addr, const uint16_t val, void *priv)
{
bochs_vbe_t *dev = (bochs_vbe_t *) priv;
if (addr == 0x1ce)
dev->vbe_index = val;
else if ((addr == 0x1cf) || (addr == 0x1d0)) switch (dev->vbe_index) {
default:
break;
case VBE_DISPI_INDEX_ID:
if ((val == VBE_DISPI_ID0) || (val == VBE_DISPI_ID1) ||
(val == VBE_DISPI_ID2) || (val == VBE_DISPI_ID3) ||
(val == VBE_DISPI_ID4))
dev->vbe_regs[dev->vbe_index] = val;
else if (val == VBE_DISPI_ID5)
dev->vbe_regs[dev->vbe_index] = dev->id5_val;
break;
case VBE_DISPI_INDEX_XRES:
case VBE_DISPI_INDEX_YRES:
case VBE_DISPI_INDEX_BPP:
case VBE_DISPI_INDEX_VIRT_WIDTH:
case VBE_DISPI_INDEX_X_OFFSET:
case VBE_DISPI_INDEX_Y_OFFSET:
dev->vbe_regs[dev->vbe_index] = val;
svga_recalctimings(&dev->svga);
break;
case VBE_DISPI_INDEX_BANK:
if (val & VBE_DISPI_BANK_RD)
dev->svga.read_bank = (val & 0x1ff) * (dev->bank_gran << 10);
if (val & VBE_DISPI_BANK_WR)
dev->svga.write_bank = (val & 0x1ff) * (dev->bank_gran << 10);
break;
case VBE_DISPI_INDEX_DDC:
if (val & (1 << 7)) {
i2c_gpio_set(dev->i2c, !!(val & 1), !!(val & 2));
dev->vbe_regs[dev->vbe_index] = val;
} else
dev->vbe_regs[dev->vbe_index] &= ~(1 << 7);
break;
case VBE_DISPI_INDEX_ENABLE: {
uint32_t new_bank_gran;
dev->vbe_regs[dev->vbe_index] = val;
if ((val & VBE_DISPI_ENABLED) &&
!(dev->vbe_regs[VBE_DISPI_ENABLED] & VBE_DISPI_ENABLED)) {
dev->vbe_regs[dev->vbe_index] = val;
dev->vbe_regs[VBE_DISPI_INDEX_X_OFFSET] = 0;
dev->vbe_regs[VBE_DISPI_INDEX_Y_OFFSET] = 0;
dev->vbe_regs[VBE_DISPI_INDEX_VIRT_WIDTH] = 0;
svga_recalctimings(&dev->svga);
if (!(val & VBE_DISPI_NOCLEARMEM)) {
memset(dev->svga.vram, 0,
dev->vbe_regs[VBE_DISPI_INDEX_YRES] * dev->svga.rowoffset);
}
} else
dev->svga.read_bank = dev->svga.write_bank = 0;
if ((val & VBE_DISPI_BANK_GRANULARITY_32K) != 0)
new_bank_gran = 32;
else
new_bank_gran = 64;
if (dev->bank_gran != new_bank_gran) {
dev->bank_gran = new_bank_gran;
dev->svga.read_bank = dev->svga.write_bank = 0;
}
if (val & VBE_DISPI_8BIT_DAC)
dev->svga.adv_flags &= ~FLAG_RAMDAC_SHIFT;
else
dev->svga.adv_flags |= FLAG_RAMDAC_SHIFT;
dev->vbe_regs[dev->vbe_index] &= ~VBE_DISPI_NOCLEARMEM;
}
}
}
void
bochs_vbe_outl(const uint16_t addr, const uint32_t val, void *priv)
{
bochs_vbe_outw(addr, val & 0xffff, priv);
bochs_vbe_outw(addr + 2, val >> 16, priv);
}
void
bochs_vbe_out(uint16_t addr, uint8_t val, void *priv)
{
bochs_vbe_t *dev = (bochs_vbe_t *) priv;
svga_t * svga = &dev->svga;
uint8_t old;
if (((addr & 0xfff0) == 0x3d0 || (addr & 0xfff0) == 0x3b0) && !(svga->miscout & 1))
addr ^= 0x60;
switch (addr) {
case VBE_DISPI_IOPORT_INDEX:
dev->vbe_index = val;
return;
case VBE_DISPI_IOPORT_DATA:
bochs_vbe_outw(0x1cf, val | (bochs_vbe_inw(0x1cf, dev) & 0xFF00), dev);
return;
case VBE_DISPI_IOPORT_DATA + 1:
bochs_vbe_outw(0x1cf, (val << 8) | (bochs_vbe_inw(0x1cf, dev) & 0xFF), dev);
return;
case 0x3D4:
svga->crtcreg = val & 0x3f;
return;
case 0x3D5:
if (svga->crtcreg & 0x20)
return;
if ((svga->crtcreg < 7) && (svga->crtc[0x11] & 0x80))
return;
if ((svga->crtcreg == 7) && (svga->crtc[0x11] & 0x80))
val = (svga->crtc[7] & ~0x10) | (val & 0x10);
old = svga->crtc[svga->crtcreg];
svga->crtc[svga->crtcreg] = val;
if (old != val) {
if (svga->crtcreg < 0xe || svga->crtcreg > 0x10) {
if ((svga->crtcreg == 0xc) || (svga->crtcreg == 0xd)) {
svga->fullchange = 3;
svga->ma_latch = ((svga->crtc[0xc] << 8) | svga->crtc[0xd]) + ((svga->crtc[8] & 0x60) >> 5);
} else {
svga->fullchange = changeframecount;
svga_recalctimings(svga);
}
}
}
break;
default:
break;
}
svga_out(addr, val, svga);
}
uint8_t
bochs_vbe_in(uint16_t addr, void *priv)
{
bochs_vbe_t *dev = (bochs_vbe_t *) priv;
svga_t * svga = &dev->svga;
uint8_t ret;
if (((addr & 0xfff0) == 0x3d0 || (addr & 0xfff0) == 0x3b0) && !(svga->miscout & 1))
addr ^= 0x60;
switch (addr) {
default:
ret = svga_in(addr, svga);
break;
case VBE_DISPI_IOPORT_INDEX:
ret = dev->vbe_index;
break;
case VBE_DISPI_IOPORT_DATA:
ret = bochs_vbe_inw(0x1cf, dev);
break;
case VBE_DISPI_IOPORT_DATA + 1:
ret = bochs_vbe_inw(0x1cf, dev) >> 8;
break;
case 0x3D4:
ret = svga->crtcreg;
break;
case 0x3D5:
if (svga->crtcreg & 0x20)
ret = 0xff;
else
ret = svga->crtc[svga->crtcreg];
break;
}
return ret;
}
static uint8_t
bochs_vbe_pci_read(const int func, const int addr, void *priv)
{
const bochs_vbe_t *dev = (bochs_vbe_t *) priv;
uint8_t ret = 0x00;
if (func == 0x00) switch (addr) {
default:
break;
case 0x00:
ret = 0x34;
break;
case 0x01:
ret = 0x12;
break;
case 0x02:
ret = 0x11;
break;
case 0x03:
ret = 0x11;
break;
case 0x04:
ret = (dev->pci_conf_status & 0b11100011) | 0x80;
break;
case 0x06:
ret = 0x80;
break;
case 0x07:
ret = 0x02;
break;
case 0x0b:
ret = 0x03;
break;
case 0x13:
ret = dev->pci_regs[addr];
break;
case 0x17:
ret = (dev->pci_regs[0x13] != 0x00) ? 0xe0 : 0x00;
break;
case 0x30:
ret = dev->pci_rom_enable & 0x01;
break;
case 0x32:
ret = dev->rom_addr & 0xff;
break;
case 0x33:
ret = (dev->rom_addr & 0xff00) >> 8;
break;
case 0x3c:
ret = dev->pci_line_interrupt;
break;
case 0x3d:
ret = 0x01;
break;
} else
ret = 0xff;
if (func == 0x00)
pclog("[R] %02X = %02X\n", addr, ret);
return ret;
}
static void
bochs_vbe_disable_handlers(bochs_vbe_t *dev)
{
io_removehandler(0x03c0, 0x0020, bochs_vbe_in, NULL, NULL,
bochs_vbe_out, NULL, NULL, dev);
io_removehandler(0x01ce, 0x0003, bochs_vbe_in, bochs_vbe_inw,
bochs_vbe_inl, bochs_vbe_out, bochs_vbe_outw, bochs_vbe_outl, dev);
mem_mapping_disable(&dev->linear_mapping_2);
mem_mapping_disable(&dev->linear_mapping);
mem_mapping_disable(&dev->svga.mapping);
mem_mapping_disable(&dev->bios_rom.mapping);
reset_state->linear_mapping_2 = dev->linear_mapping_2;
reset_state->linear_mapping = dev->linear_mapping;
reset_state->svga.mapping = dev->svga.mapping;
reset_state->bios_rom.mapping = dev->bios_rom.mapping;
}
static void
bochs_vbe_pci_write(const int func, const int addr, const uint8_t val, void *priv)
{
bochs_vbe_t *dev = (bochs_vbe_t *) priv;
if (func == 0x00)
pclog("[W] %02X = %02X\n", addr, val);
if (func == 0x00) switch (addr) {
default:
break;
case 0x04:
dev->pci_conf_status = val;
io_removehandler(0x03c0, 0x0020, bochs_vbe_in, NULL, NULL,
bochs_vbe_out, NULL, NULL, dev);
io_removehandler(0x01ce, 0x0003, bochs_vbe_in, bochs_vbe_inw,
bochs_vbe_inl, bochs_vbe_out, bochs_vbe_outw, bochs_vbe_outl, dev);
mem_mapping_disable(&dev->linear_mapping_2);
mem_mapping_disable(&dev->linear_mapping);
mem_mapping_disable(&dev->svga.mapping);
if (dev->pci_conf_status & PCI_COMMAND_IO) {
io_sethandler(0x03c0, 0x0020, bochs_vbe_in, NULL, NULL,
bochs_vbe_out, NULL, NULL, dev);
io_sethandler(0x01ce, 0x0003, bochs_vbe_in, bochs_vbe_inw, bochs_vbe_inl,
bochs_vbe_out, bochs_vbe_outw, bochs_vbe_outl, dev);
}
if (dev->pci_conf_status & PCI_COMMAND_MEM) {
mem_mapping_enable(&dev->svga.mapping);
if (dev->pci_regs[0x13] != 0x00) {
mem_mapping_enable(&dev->linear_mapping);
if (dev->pci_regs[0x13] != 0xe0)
mem_mapping_enable(&dev->linear_mapping_2);
}
}
break;
case 0x13:
dev->pci_regs[addr] = val;
mem_mapping_disable(&dev->linear_mapping_2);
mem_mapping_disable(&dev->linear_mapping);
if ((dev->pci_conf_status & PCI_COMMAND_MEM) && (val != 0x00)) {
mem_mapping_set_addr(&dev->linear_mapping, val << 24, 0x01000000);
if (val != 0xe0)
mem_mapping_set_addr(&dev->linear_mapping_2, 0xe0000000, 0x01000000);
}
break;
case 0x3c:
dev->pci_line_interrupt = val;
break;
case 0x30:
dev->pci_rom_enable = val & 0x01;
mem_mapping_disable(&dev->bios_rom.mapping);
if (dev->pci_rom_enable)
mem_mapping_set_addr(&dev->bios_rom.mapping, dev->rom_addr << 16, 0x10000);
break;
case 0x32:
dev->rom_addr = (dev->rom_addr & 0xff00) | (val & 0xfc);
if (dev->pci_rom_enable)
mem_mapping_set_addr(&dev->bios_rom.mapping, dev->rom_addr << 16, 0x10000);
break;
case 0x33:
dev->rom_addr = (dev->rom_addr & 0x00ff) | (val << 8);
if (dev->pci_rom_enable)
mem_mapping_set_addr(&dev->bios_rom.mapping, dev->rom_addr << 16, 0x10000);
break;
}
}
static void
bochs_vbe_reset(void *priv)
{
bochs_vbe_t *dev = (bochs_vbe_t *) priv;
if (reset_state != NULL) {
bochs_vbe_disable_handlers(dev);
reset_state->slot = dev->slot;
*dev = *reset_state;
}
}
static void *
bochs_vbe_init(const device_t *info)
{
bochs_vbe_t *dev = calloc(1, sizeof(bochs_vbe_t));
reset_state = calloc(1, sizeof(bochs_vbe_t));
dev->id5_val = device_get_config_int("revision");
dev->vram_size = device_get_config_int("memory") * (1 << 20);
rom_init(&dev->bios_rom, "roms/video/bochs/VGABIOS-lgpl-latest.bin",
0xc0000, 0x10000, 0xffff, 0x0000,
MEM_MAPPING_EXTERNAL);
video_inform(VIDEO_FLAG_TYPE_SPECIAL, &timing_bochs);
svga_init(info, &dev->svga, dev, dev->vram_size,
bochs_vbe_recalctimings,
bochs_vbe_in, bochs_vbe_out,
NULL,
NULL);
mem_mapping_add(&dev->linear_mapping, 0, 0,
svga_readb_linear, svga_readw_linear, svga_readl_linear,
svga_writeb_linear, svga_writew_linear, svga_writel_linear,
NULL, MEM_MAPPING_EXTERNAL, &dev->svga);
/* Hack: If the mapping gets mapped anywhere other than at 0xe0000000,
enable this second copy of it at 0xe0000000 so michaln's driver works. */
mem_mapping_add(&dev->linear_mapping_2, 0, 0,
svga_readb_linear, svga_readw_linear, svga_readl_linear,
svga_writeb_linear, svga_writew_linear, svga_writel_linear,
NULL, MEM_MAPPING_EXTERNAL, &dev->svga);
mem_mapping_disable(&dev->bios_rom.mapping);
mem_mapping_disable(&dev->svga.mapping);
mem_mapping_disable(&dev->linear_mapping);
mem_mapping_disable(&dev->linear_mapping_2);
dev->svga.bpp = 8;
dev->svga.miscout = 1;
dev->bank_gran = 64;
svga_set_ramdac_type(&dev->svga, RAMDAC_8BIT);
dev->svga.adv_flags |= FLAG_RAMDAC_SHIFT;
dev->svga.decode_mask = 0xffffff;
dev->i2c = i2c_gpio_init("ddc_bochs");
dev->ddc = ddc_init(i2c_gpio_get_bus(dev->i2c));
pci_add_card(PCI_ADD_NORMAL, bochs_vbe_pci_read, bochs_vbe_pci_write, dev, &dev->slot);
*reset_state = *dev;
return dev;
}
static int
bochs_vbe_available(void)
{
return rom_present("roms/video/bochs/VGABIOS-lgpl-latest.bin");
}
void
bochs_vbe_close(void *priv)
{
bochs_vbe_t *dev = (bochs_vbe_t *) priv;
ddc_close(dev->ddc);
i2c_gpio_close(dev->i2c);
svga_close(&dev->svga);
free(reset_state);
reset_state = NULL;
free(dev);
}
void
bochs_vbe_speed_changed(void *priv)
{
bochs_vbe_t *bochs_vbe = (bochs_vbe_t *) priv;
svga_recalctimings(&bochs_vbe->svga);
}
void
bochs_vbe_force_redraw(void *priv)
{
bochs_vbe_t *bochs_vbe = (bochs_vbe_t *) priv;
bochs_vbe->svga.fullchange = changeframecount;
}
static const device_config_t bochs_vbe_config[] = {
// clang-format off
{
.name = "revision",
.description = "Revision",
.type = CONFIG_SELECTION,
.selection = {
{
.description = "VirtualBox",
.value = VBE_DISPI_ID4
},
{
.description = "Bochs latest",
.value = VBE_DISPI_ID5
},
{
.description = ""
}
},
.default_int = VBE_DISPI_ID5
},
{
.name = "memory",
.description = "Memory size",
.type = CONFIG_SELECTION,
.selection = {
{
.description = "4 MB",
.value = 4
},
{
.description = "8 MB",
.value = 8
},
{
.description = "16 MB",
.value = 16
},
{
.description = ""
}
},
.default_int = 16
},
{
.type = CONFIG_END
}
// clang-format on
};
const device_t bochs_svga_device = {
.name = "Bochs SVGA",
.internal_name = "bochs_svga",
.flags = DEVICE_PCI,
.local = 0,
.init = bochs_vbe_init,
.close = bochs_vbe_close,
.reset = bochs_vbe_reset,
{ .available = bochs_vbe_available },
.speed_changed = bochs_vbe_speed_changed,
.force_redraw = bochs_vbe_force_redraw,
.config = bochs_vbe_config
};

View File

@@ -163,11 +163,8 @@ video_cards[] = {
{ &mach32_pci_device, VIDEO_FLAG_TYPE_8514 }, { &mach32_pci_device, VIDEO_FLAG_TYPE_8514 },
{ &mach64gx_pci_device }, { &mach64gx_pci_device },
{ &mach64vt2_device }, { &mach64vt2_device },
{ &et4000w32p_videomagic_revb_pci_device }, { &bochs_svga_device },
{ &et4000w32p_revc_pci_device }, { &chips_69000_device },
{ &et4000w32p_cardex_pci_device },
{ &et4000w32p_noncardex_pci_device },
{ &et4000w32p_pci_device },
{ &gd5430_pci_device, }, { &gd5430_pci_device, },
{ &gd5434_pci_device }, { &gd5434_pci_device },
{ &gd5436_pci_device, VIDEO_FLAG_TYPE_SPECIAL }, { &gd5436_pci_device, VIDEO_FLAG_TYPE_SPECIAL },
@@ -175,6 +172,11 @@ video_cards[] = {
{ &gd5446_pci_device, VIDEO_FLAG_TYPE_SPECIAL }, { &gd5446_pci_device, VIDEO_FLAG_TYPE_SPECIAL },
{ &gd5446_stb_pci_device, VIDEO_FLAG_TYPE_SPECIAL }, { &gd5446_stb_pci_device, VIDEO_FLAG_TYPE_SPECIAL },
{ &gd5480_pci_device }, { &gd5480_pci_device },
{ &et4000w32p_videomagic_revb_pci_device },
{ &et4000w32p_revc_pci_device },
{ &et4000w32p_cardex_pci_device },
{ &et4000w32p_noncardex_pci_device },
{ &et4000w32p_pci_device },
{ &s3_spea_mercury_lite_86c928_pci_device }, { &s3_spea_mercury_lite_86c928_pci_device },
{ &s3_diamond_stealth64_964_pci_device }, { &s3_diamond_stealth64_964_pci_device },
{ &s3_elsa_winner2000_pro_x_964_pci_device }, { &s3_elsa_winner2000_pro_x_964_pci_device },
@@ -206,7 +208,6 @@ video_cards[] = {
{ &s3_virge_357_pci_device }, { &s3_virge_357_pci_device },
{ &s3_diamond_stealth_4000_pci_device }, { &s3_diamond_stealth_4000_pci_device },
{ &s3_trio3d2x_pci_device }, { &s3_trio3d2x_pci_device },
{ &chips_69000_device },
{ &millennium_device }, { &millennium_device },
{ &millennium_ii_device }, { &millennium_ii_device },
{ &mystique_device }, { &mystique_device },

View File

@@ -104,7 +104,7 @@ monitor_settings_t monitor_settings[MONITORS_NUM];
atomic_bool doresize_monitors[MONITORS_NUM]; atomic_bool doresize_monitors[MONITORS_NUM];
#ifdef _WIN32 #ifdef _WIN32
void *__cdecl (*video_copy)(void *_Dst, const void *_Src, size_t _Size) = memcpy; void * (*__cdecl video_copy)(void *_Dst, const void *_Src, size_t _Size) = memcpy;
#else #else
void *(*video_copy)(void *__restrict, const void *__restrict, size_t); void *(*video_copy)(void *__restrict, const void *__restrict, size_t);
#endif #endif