From f4d210273431338411588a8b57646645edaa076c Mon Sep 17 00:00:00 2001 From: OBattler Date: Sun, 28 Apr 2024 16:47:32 +0200 Subject: [PATCH] Bochs VBE graphics card, originally by Cacodemon345, then extensively cleaned-up and improved. --- src/include/86box/video.h | 13 +- src/video/CMakeLists.txt | 3 +- src/video/vid_bochs_vbe.c | 941 ++++++++++++++++++++++++++++++++++++++ src/video/vid_table.c | 13 +- src/video/video.c | 2 +- 5 files changed, 959 insertions(+), 13 deletions(-) create mode 100644 src/video/vid_bochs_vbe.c diff --git a/src/include/86box/video.h b/src/include/86box/video.h index bf36bf7de..1c8b61015 100644 --- a/src/include/86box/video.h +++ b/src/include/86box/video.h @@ -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); #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); #else 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; # 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 */ extern const device_t gd5401_isa_device; extern const device_t gd5402_isa_device; @@ -579,10 +586,6 @@ extern const device_t velocity_200_agp_device; /* Wyse 700 */ 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 /*EMU_VIDEO_H*/ diff --git a/src/video/CMakeLists.txt b/src/video/CMakeLists.txt index 12ba34a02..c8fb7021d 100644 --- a/src/video/CMakeLists.txt +++ b/src/video/CMakeLists.txt @@ -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_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_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) target_compile_definitions(vid PRIVATE USE_XL24) diff --git a/src/video/vid_bochs_vbe.c b/src/video/vid_bochs_vbe.c new file mode 100644 index 000000000..c94049a1c --- /dev/null +++ b/src/video/vid_bochs_vbe.c @@ -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 +#include +#include +#include +#include +#include +#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 +}; diff --git a/src/video/vid_table.c b/src/video/vid_table.c index e526216c3..b2686b2f3 100644 --- a/src/video/vid_table.c +++ b/src/video/vid_table.c @@ -163,11 +163,8 @@ video_cards[] = { { &mach32_pci_device, VIDEO_FLAG_TYPE_8514 }, { &mach64gx_pci_device }, { &mach64vt2_device }, - { &et4000w32p_videomagic_revb_pci_device }, - { &et4000w32p_revc_pci_device }, - { &et4000w32p_cardex_pci_device }, - { &et4000w32p_noncardex_pci_device }, - { &et4000w32p_pci_device }, + { &bochs_svga_device }, + { &chips_69000_device }, { &gd5430_pci_device, }, { &gd5434_pci_device }, { &gd5436_pci_device, VIDEO_FLAG_TYPE_SPECIAL }, @@ -175,6 +172,11 @@ video_cards[] = { { &gd5446_pci_device, VIDEO_FLAG_TYPE_SPECIAL }, { &gd5446_stb_pci_device, VIDEO_FLAG_TYPE_SPECIAL }, { &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_diamond_stealth64_964_pci_device }, { &s3_elsa_winner2000_pro_x_964_pci_device }, @@ -206,7 +208,6 @@ video_cards[] = { { &s3_virge_357_pci_device }, { &s3_diamond_stealth_4000_pci_device }, { &s3_trio3d2x_pci_device }, - { &chips_69000_device }, { &millennium_device }, { &millennium_ii_device }, { &mystique_device }, diff --git a/src/video/video.c b/src/video/video.c index 01c398118..0773e61ce 100644 --- a/src/video/video.c +++ b/src/video/video.c @@ -104,7 +104,7 @@ monitor_settings_t monitor_settings[MONITORS_NUM]; atomic_bool doresize_monitors[MONITORS_NUM]; #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 void *(*video_copy)(void *__restrict, const void *__restrict, size_t); #endif