summaryrefslogtreecommitdiffstatshomepage
path: root/src/devices/video/atirage.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/devices/video/atirage.cpp')
-rw-r--r--src/devices/video/atirage.cpp524
1 files changed, 524 insertions, 0 deletions
diff --git a/src/devices/video/atirage.cpp b/src/devices/video/atirage.cpp
new file mode 100644
index 00000000000..cc1022e376c
--- /dev/null
+++ b/src/devices/video/atirage.cpp
@@ -0,0 +1,524 @@
+// license:BSD-3-Clause
+// copyright-holders: R. Belmont
+/*
+ ATI Rage PCI/AGP SVGA
+
+ This implementation targets the mach64 VT and 3D Rage chips. Rage 128 has similar registers
+ but they're mapped differently.
+
+ mach64 VT = mach64 with video decoding. Uses a Rage-compatible register layout, as opposed to earlier mach64.
+ mach64 GT = Rage I (mach64 acceleration and VGA with 3D polygons and MPEG-1 decode)
+ mach64 GT-B = Rage II (Rage I with faster 2D & 3D and MPEG-2 decode)
+ Rage II+ = Rage II with full DVD acceleration
+ Rage IIc = Rage II+ with optional AGP support
+ Rage Pro = new triangle setup engine, improved perspective correction, fog + specular lighting,
+ and improved video decode
+ Rage Pro Turbo = Rage Pro with AGP 2X support and improved performance drivers for Win9X
+ Rage LT = lower-power Rage II with DVD support
+ Rage Mobility C, EC, L, and M2 = lower-power Rage Pro with DVD motion compensation
+ Rage Mobility P, M, and M1 = lower-power Rage Pro with DVD motion compensation and IDCT accleration
+ Rage XL = cost-reduced Rage Pro with improved 3D image quality, used in many servers until 2006
+
+ Most PCI IDs are a 2-letter ATI product code in ASCII. For instance, Rage I & II aka mach64 GT are 0x4754 'GT'.
+
+ Reference: rrg-g02700_mach64_register_reference_guide_jul96.pdf, aka "mach64 Register Reference Guide: ATI VT-264 and 3D RAGE"
+ http://hackipedia.org/browse.cgi/Computer/Platform/PC%2C%20IBM%20compatible/Video/VGA/SVGA/ATI%2C%20Array%20Technology%20Inc
+*/
+
+#include "emu.h"
+#include "screen.h"
+#include "atirage.h"
+
+#define LOG_GENERAL (1U << 0)
+#define LOG_REGISTERS (1U << 1)
+#define LOG_CRTC (1U << 2)
+#define LOG_DAC (1U << 3)
+
+#define VERBOSE (0)
+#include "logmacro.h"
+
+DEFINE_DEVICE_TYPE(ATI_RAGEII, atirageii_device, "rageii", "ATI Rage II PCI")
+DEFINE_DEVICE_TYPE(ATI_RAGEIIC, atirageiic_device, "rageiic", "ATI Rage IIC PCI")
+DEFINE_DEVICE_TYPE(ATI_RAGEPRO, atiragepro_device, "ragepro", "ATI Rage Pro PCI")
+
+// register offsets
+static constexpr u32 CRTC_H_TOTAL_DISP = 0x00;
+static constexpr u32 CRTC_V_TOTAL_DISP = 0x08;
+static constexpr u32 CRTC_OFF_PITCH = 0x14;
+static constexpr u32 CRTC_GEN_CNTL = 0x1c;
+static constexpr u32 GP_IO = 0x78;
+static constexpr u32 CLOCK_CNTL = 0x90;
+static constexpr u32 CRTC_DAC_BASE = 0xc0;
+static constexpr u32 CONFIG_CHIP_ID = 0xe0;
+
+// PLL register offsets
+static constexpr u32 PLL_MACRO_CNTL = 1;
+static constexpr u32 PLL_REF_DIV = 2;
+static constexpr u32 PLL_GEN_CNTL = 3;
+static constexpr u32 MCLK_FB_DIV = 4;
+static constexpr u32 PLL_VCLK_CNTL = 5;
+static constexpr u32 VCLK_POST_DIV = 6;
+static constexpr u32 VCLK0_FB_DIV = 7;
+static constexpr u32 VCLK1_FB_DIV = 8;
+static constexpr u32 VCLK2_FB_DIV = 9;
+static constexpr u32 VCLK3_FB_DIV = 10;
+static constexpr u32 PLL_XCLK_CNTL = 11;
+static constexpr u32 PLL_FCP_CNTL = 12;
+
+// mach64 & 3D Rage post-dividers for PLL
+static const int pll_post_dividers[8] =
+{
+ 1, 2, 4, 8, 3, 5, 6, 12
+};
+
+void atirage_device::device_add_mconfig(machine_config &config)
+{
+ screen_device &screen(SCREEN(config, "screen", SCREEN_TYPE_RASTER));
+ screen.set_raw(XTAL(25'174'800), 900, 0, 640, 526, 0, 480);
+ screen.set_screen_update(FUNC(atirage_device::screen_update));
+
+ ATIMACH64(config, m_mach64, 0);
+ m_mach64->set_screen("screen");
+ m_mach64->set_vram_size(0x600000);
+}
+
+atirage_device::atirage_device(const machine_config &mconfig, device_type type, const char *tag, device_t *owner, uint32_t clock)
+ : pci_device(mconfig, type, tag, owner, clock),
+ m_mach64(*this, "vga"),
+ m_screen(*this, "screen"),
+ read_gpio(*this),
+ write_gpio(*this)
+{
+ m_hres = m_vres = m_htotal = m_vtotal = m_format = 0;
+ m_dac_windex = m_dac_rindex = m_dac_state = 0;
+ m_dac_mask = 0xff;
+
+}
+
+atirageii_device::atirageii_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock)
+ : atirage_device(mconfig, ATI_RAGEII, tag, owner, clock)
+{
+}
+
+atirageiic_device::atirageiic_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock)
+ : atirage_device(mconfig, ATI_RAGEIIC, tag, owner, clock)
+{
+}
+
+atiragepro_device::atiragepro_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock)
+ : atirage_device(mconfig, ATI_RAGEPRO, tag, owner, clock)
+{
+}
+
+void atirage_device::io_map(address_map& map)
+{
+ map(0x00000000, 0x000003ff).rw(FUNC(atirage_device::regs_0_read), FUNC(atirage_device::regs_0_write));
+}
+
+void atirage_device::mem_map(address_map& map)
+{
+ map(0x00000000, 0x005fffff).rw(m_mach64, FUNC(mach64_device::framebuffer_r), FUNC(mach64_device::framebuffer_w));
+ map(0x007ff800, 0x007ffbff).rw(FUNC(atirage_device::regs_1_read), FUNC(atirage_device::regs_1_write));
+ map(0x007ffc00, 0x007fffff).rw(FUNC(atirage_device::regs_0_read), FUNC(atirage_device::regs_0_write));
+ map(0x00800000, 0x00dfffff).rw(m_mach64, FUNC(mach64_device::framebuffer_be_r), FUNC(mach64_device::framebuffer_be_w));
+}
+
+void atirage_device::reg_map(address_map& map)
+{
+}
+
+void atirage_device::config_map(address_map &map)
+{
+ pci_device::config_map(map);
+ map(0x0040, 0x0043).rw(FUNC(atirage_device::user_cfg_r), FUNC(atirage_device::user_cfg_w));
+}
+
+void atirage_device::device_start()
+{
+ read_gpio.resolve_safe(0);
+ write_gpio.resolve_safe();
+
+ pci_device::device_start();
+
+ add_map(0x1000000, M_MEM, FUNC(atirage_device::mem_map)); // 16 MB memory map
+ add_map(0x100, M_IO, FUNC(atirage_device::io_map)); // 256 byte I/O map
+ add_map(0x01000, M_MEM, FUNC(atirage_device::reg_map)); // 4K register map
+
+ command = 3;
+ intr_pin = 1;
+ intr_line = 0;
+
+ // clear the registers
+ std::fill(std::begin(m_regs0), std::end(m_regs0), 0);
+ std::fill(std::begin(m_regs1), std::end(m_regs1), 0);
+ std::fill(std::begin(m_pll_regs), std::end(m_pll_regs), 0);
+ std::fill(std::begin(m_dac_colors), std::end(m_dac_colors), 0);
+
+ // set PLL defaults from the manual
+ m_pll_regs[PLL_MACRO_CNTL] = 0xd4;
+ m_pll_regs[PLL_REF_DIV] = 0x36;
+ m_pll_regs[PLL_GEN_CNTL] = 0x4f;
+ m_pll_regs[MCLK_FB_DIV] = 0x97;
+ m_pll_regs[PLL_VCLK_CNTL] = 0x04;
+ m_pll_regs[VCLK_POST_DIV] = 0x6a;
+ m_pll_regs[VCLK0_FB_DIV] = 0xbe;
+ m_pll_regs[VCLK1_FB_DIV] = 0xd6;
+ m_pll_regs[VCLK2_FB_DIV] = 0xee;
+ m_pll_regs[VCLK3_FB_DIV] = 0x88;
+ m_pll_regs[PLL_XCLK_CNTL] = 0x00;
+ m_pll_regs[PLL_FCP_CNTL] = 0x41;
+
+ m_user_cfg = 8;
+ save_item(NAME(m_user_cfg));
+ save_item(NAME(m_regs0));
+ save_item(NAME(m_regs1));
+ save_item(NAME(m_pll_regs));
+ save_item(NAME(m_hres));
+ save_item(NAME(m_vres));
+ save_item(NAME(m_htotal));
+ save_item(NAME(m_vtotal));
+ save_item(NAME(m_format));
+ save_item(NAME(m_pixel_clock));
+ save_item(NAME(m_dac_windex));
+ save_item(NAME(m_dac_rindex));
+ save_item(NAME(m_dac_state));
+ save_item(NAME(m_dac_mask));
+ save_item(NAME(m_dac_colors));
+}
+
+void atirageii_device::device_start()
+{
+ // mach64 GT-B / 3D Rage II (ATI documentation uses both names)
+ set_ids(0x10024754, 0x00, 0x030000, 0x10026987);
+ atirage_device::device_start();
+ revision = 0x9a;
+ m_regs0[CONFIG_CHIP_ID] = 0x54;
+ m_regs0[CONFIG_CHIP_ID+1] = 0x47;
+ m_regs0[CONFIG_CHIP_ID+3] = 0x9a;
+}
+
+void atirageiic_device::device_start()
+{
+ // Rage IIc PCI
+ set_ids(0x10024756, 0x00, 0x030000, 0x10026987);
+ atirage_device::device_start();
+ revision = 0x3a;
+ m_regs0[CONFIG_CHIP_ID] = 0x56;
+ m_regs0[CONFIG_CHIP_ID+1] = 0x47;
+ m_regs0[CONFIG_CHIP_ID+3] = 0x3a;
+}
+
+void atiragepro_device::device_start()
+{
+ // Rage Pro PCI
+ set_ids(0x10024750, 0x00, 0x030000, 0x10026987);
+ atirage_device::device_start();
+ revision = 0x5c;
+ m_regs0[CONFIG_CHIP_ID] = 0x50;
+ m_regs0[CONFIG_CHIP_ID+1] = 0x47;
+ m_regs0[CONFIG_CHIP_ID+3] = 0x5c;
+}
+
+void atirage_device::map_extra(uint64_t memory_window_start, uint64_t memory_window_end, uint64_t memory_offset, address_space *memory_space,
+ uint64_t io_window_start, uint64_t io_window_end, uint64_t io_offset, address_space *io_space)
+{
+}
+
+u8 atirage_device::regs_0_read(offs_t offset)
+{
+ switch (offset)
+ {
+ case CRTC_DAC_BASE: // DAC write index
+ return m_dac_windex;
+
+ case CRTC_DAC_BASE + 1:
+ {
+ u8 result;
+ switch (m_dac_state)
+ {
+ case 0: // red
+ result = ((m_dac_colors[m_dac_rindex] >> 16) & 0xff);
+ break;
+
+ case 1: // blue
+ result = ((m_dac_colors[m_dac_rindex] >> 8) & 0xff);
+ break;
+
+ case 2: // green
+ result = (m_dac_colors[m_dac_rindex] & 0xff);
+ break;
+ }
+
+ m_dac_state++;
+ if (m_dac_state >= 3)
+ {
+ m_dac_state = 0;
+ m_dac_rindex++;
+ }
+ return result;
+ }
+ break;
+
+ case CRTC_DAC_BASE + 2:
+ return m_dac_mask;
+
+ case CRTC_DAC_BASE + 3:
+ return m_dac_rindex;
+
+ case CLOCK_CNTL + 2:
+ return m_pll_regs[(m_regs0[CLOCK_CNTL+1] >> 2) & 0xf] << 16;
+
+ case GP_IO: // monitor sense - either 3-line Apple or EDID
+ return read_gpio() & 0xff;
+
+ case GP_IO + 1:
+ return (read_gpio() & 0xff00) >> 8;
+
+ case GP_IO + 2:
+ return (read_gpio() & 0xff0000) >> 16;
+
+ case GP_IO + 3:
+ return read_gpio() >> 24;
+ }
+
+ return m_regs0[offset];
+}
+
+void atirage_device::regs_0_write(offs_t offset, u8 data)
+{
+ // the FCode drivers try to write to the chip ID, no idea why
+ if ((offset >= CONFIG_CHIP_ID) && (offset <= (CONFIG_CHIP_ID + 3)))
+ {
+ return;
+ }
+
+ LOGMASKED(LOG_REGISTERS, "regs_0_write: %02x to %x\n", data, offset);
+
+ m_regs0[offset] = data;
+ switch (offset)
+ {
+ case CRTC_DAC_BASE: // DAC write index
+ m_dac_state = 0;
+ m_dac_windex = data;
+ break;
+
+ case CRTC_DAC_BASE + 1:
+ switch (m_dac_state)
+ {
+ case 0: // red
+ m_dac_colors[m_dac_windex] &= 0x00ffff;
+ m_dac_colors[m_dac_windex] |= ((data & 0xff) << 16);
+ break;
+
+ case 1: // green
+ m_dac_colors[m_dac_windex] &= 0xff00ff;
+ m_dac_colors[m_dac_windex] |= ((data & 0xff) << 8);
+ break;
+
+ case 2: // blue
+ m_dac_colors[m_dac_windex] &= 0xffff00;
+ m_dac_colors[m_dac_windex] |= (data & 0xff);
+ break;
+ }
+
+ m_dac_state++;
+ if (m_dac_state == 3)
+ {
+ m_dac_state = 0;
+ m_mach64->set_color(m_dac_windex, m_dac_colors[m_dac_windex]);
+ m_dac_windex++;
+ }
+ break;
+
+ case CRTC_DAC_BASE + 2:
+ m_dac_mask = data;
+ break;
+
+ case CRTC_DAC_BASE + 3:
+ m_dac_state = 0;
+ m_dac_rindex = data >> 24;
+ break;
+
+ case CRTC_OFF_PITCH:
+ case CRTC_GEN_CNTL:
+ update_mode();
+ break;
+
+ case CLOCK_CNTL + 2:
+ if (BIT(m_regs0[CLOCK_CNTL+1], 1))
+ {
+ u8 regnum = (m_regs0[CLOCK_CNTL+1] >> 2) & 0xf;
+ m_pll_regs[regnum] = data & 0xff;
+ }
+ break;
+
+ case GP_IO:
+ case GP_IO + 1:
+ case GP_IO + 2:
+ case GP_IO + 3:
+ write_gpio(*(u32 *)&m_regs0[GP_IO]);
+ break;
+ }
+}
+
+u8 atirage_device::regs_1_read(offs_t offset)
+{
+ LOGMASKED(LOG_REGISTERS, "regs 1 read @ %x\n", offset);
+ return m_regs1[offset];
+}
+
+void atirage_device::regs_1_write(offs_t offset, u8 data)
+{
+ m_regs1[offset] = data;
+}
+
+u32 atirage_device::user_cfg_r()
+{
+ return m_user_cfg;
+}
+
+void atirage_device::user_cfg_w(u32 data)
+{
+ m_user_cfg = data;
+}
+
+void atirage_device::update_mode()
+{
+ // first prereq: must be in native mode and the CRTC must be enabled
+ if (!(m_regs0[CRTC_GEN_CNTL+3] & 3))
+ {
+ LOGMASKED(LOG_CRTC, "VGA mode must be OFF and CRTC must be ON\n");
+ return;
+ }
+
+ m_htotal = (m_regs0[CRTC_H_TOTAL_DISP] | (m_regs0[CRTC_H_TOTAL_DISP+1] & 1) << 8) + 1;
+ m_htotal <<= 3; // in units of 8 pixels
+ m_hres = m_regs0[CRTC_H_TOTAL_DISP+2] + 1;
+ m_hres <<= 3;
+ m_vres = (m_regs0[CRTC_V_TOTAL_DISP+2] | (m_regs0[CRTC_V_TOTAL_DISP+3] & 7) << 8) + 1;
+ m_vtotal = (m_regs0[CRTC_V_TOTAL_DISP] | (m_regs0[CRTC_V_TOTAL_DISP+1] & 7) << 8) + 1;
+ m_format = m_regs0[CRTC_GEN_CNTL+1] & 7;
+ LOGMASKED(LOG_CRTC, "Setting mode (%d x %d), total (%d x %d) format %d\n", m_hres, m_vres, m_htotal, m_vtotal, m_format);
+
+ int vclk_source = (m_pll_regs[PLL_VCLK_CNTL] & 3);
+ if ((vclk_source != 0) && (vclk_source != 3))
+ {
+ LOGMASKED(LOG_CRTC, "VCLK source (%d) is not VPLL, can't calculate dot clock\n", m_pll_regs[PLL_VCLK_CNTL] & 3);
+ return;
+ }
+
+ double vpll_frequency;
+ int clk_source = m_regs0[CLOCK_CNTL] & 3;
+
+ switch (vclk_source)
+ {
+ case 0: // CPUCLK (the PCI bus clock, not to exceed 33 MHz)
+ vpll_frequency = (33000000.0 * m_pll_regs[VCLK0_FB_DIV + clk_source]) / m_pll_regs[PLL_REF_DIV];
+ break;
+
+ case 3: // PLLVCLK
+ vpll_frequency = ((clock() * 2.0) * m_pll_regs[VCLK0_FB_DIV + clk_source]) / m_pll_regs[PLL_REF_DIV];
+ break;
+ }
+ LOGMASKED(LOG_CRTC, "VPLL freq %f\n", vpll_frequency);
+
+ int vpll_post_divider = (m_pll_regs[VCLK_POST_DIV] >> (clk_source << 1)) & 3;
+ // Rage Pro adds one more bit to the divider from bits 4/5/6/7 of XCLK_CNTL depending on the clock source.
+ // This should always be zero on mach64/Rage/Rage II.
+ vpll_post_divider |= ((m_pll_regs[PLL_XCLK_CNTL] >> (clk_source + 2)) & 4);
+
+ m_pixel_clock = u32(vpll_frequency / pll_post_dividers[vpll_post_divider]);
+ LOGMASKED(LOG_CRTC, "Pixel clock = %d, refresh = %f\n", m_pixel_clock, (double)m_pixel_clock / (double)m_htotal / (double)m_vtotal);
+
+ rectangle visarea(0, m_hres - 1, 0, m_vres - 1);
+ m_screen->configure(m_htotal, m_vtotal, visarea, attotime::from_ticks(m_htotal * m_vtotal, m_pixel_clock).as_attoseconds());
+}
+
+u32 atirage_device::screen_update(screen_device &screen, bitmap_rgb32 &bitmap, const rectangle &cliprect)
+{
+ // are we in VGA mode rather than native? if so, let the legacy VGA stuff draw.
+ if (!(m_regs0[CRTC_GEN_CNTL+3] & 1))
+ {
+ return m_mach64->screen_update(screen, bitmap, cliprect);
+ }
+
+ // is the CRTC not enabled or the display disable bit set?
+ if ((!(m_regs0[CRTC_GEN_CNTL+3] & 2)) || (m_regs0[CRTC_GEN_CNTL] & 0x40))
+ {
+ bitmap.fill(0, cliprect);
+ return 0;
+ }
+
+ const int offset = ((m_regs0[CRTC_OFF_PITCH+2] & 0xf) << 16) | (m_regs0[CRTC_OFF_PITCH+1] << 8) | (m_regs0[CRTC_OFF_PITCH]);
+ u8 *vram = m_mach64->get_framebuffer_addr() + (offset * 8);
+ int stride = (m_regs0[CRTC_OFF_PITCH+2] >> 6) | (m_regs0[CRTC_OFF_PITCH+3] << 2);
+ stride *= 4;
+
+ switch (m_format)
+ {
+ case 2: // 8 bpp (also can be a weird 2/2/3 direct color mode)
+ for (u32 y = 0; y < m_vres; y++)
+ {
+ const u8 *src = &vram[stride*y];
+ u32 *dst = &bitmap.pix(y, 0);
+ for (u32 x = 0; x < m_hres; x++)
+ {
+ *dst++ = m_dac_colors[src[x]];
+ }
+ vram += stride;
+ }
+ break;
+
+ default:
+ LOGMASKED(LOG_GENERAL, "Unknown pixel format %d\n", m_format);
+ break;
+ }
+
+ return 0;
+}
+
+/*
+02 to CLOCK_CTNL + 1
+PLL: cd to 0
+06 to CLOCK_CTNL + 1
+PLL: d5 to 1
+2a to CLOCK_CTNL + 1
+PLL: 17 to 10
+1a to CLOCK_CTNL + 1
+PLL: c0 to 6
+0a to CLOCK_CTNL + 1
+PLL: 21 to 2
+16 to CLOCK_CTNL + 1
+PLL: 03 to 5
+12 to CLOCK_CTNL + 1
+PLL: 91 to 4
+0e to CLOCK_CTNL + 1
+PLL: 14 to 3
+2e to CLOCK_CTNL + 1
+PLL: 01 to 11
+32 to CLOCK_CTNL + 1
+PLL: a6 to 12
+32 to CLOCK_CTNL + 1
+PLL: e6 to 12
+32 to CLOCK_CTNL + 1
+PLL: a6 to 12
+2a to CLOCK_CTNL + 1
+PLL: 98 to 10
+2e to CLOCK_CTNL + 1
+PLL: 01 to 11
+18 to CLOCK_CTNL + 1
+Read PLL 6
+1a to CLOCK_CTNL + 1
+PLL: 80 to 6
+0a to CLOCK_CTNL + 1
+PLL: 24 to 2
+12 to CLOCK_CTNL + 1
+PLL: 9e to 4
+32 to CLOCK_CTNL + 1
+PLL: a6 to 12
+32 to CLOCK_CTNL + 1
+PLL: e6 to 12
+32 to CLOCK_CTNL + 1
+PLL: a6 to 12
+
+*/