// license:BSD-3-Clause // copyright-holders:Aaron Giles /*************************************************************************** voodoo.h 3dfx Voodoo Graphics SST-1/2 emulator. ***************************************************************************/ #ifndef MAME_VIDEO_VOODOO_H #define MAME_VIDEO_VOODOO_H #pragma once #include "screen.h" // forward declarations class voodoo_1_device; namespace voodoo { class save_proxy; } //************************************************************************** // CONSTANTS //************************************************************************** namespace voodoo { // enumeration specifying which model of Voodoo we are emulating enum class voodoo_model : u8 { VOODOO_1, VOODOO_2, VOODOO_BANSHEE, VOODOO_3 }; // debug static constexpr bool DEBUG_DEPTH = false; // ENTER key to view depthbuf static constexpr bool DEBUG_BACKBUF = false; // L key to view backbuf static constexpr bool DEBUG_STATS = false; // \ key to view stats // logging static constexpr bool LOG_VBLANK_SWAP = false; static constexpr bool LOG_FIFO = false; static constexpr bool LOG_FIFO_VERBOSE = false; static constexpr bool LOG_REGISTERS = false; static constexpr bool LOG_WAITS = false; static constexpr bool LOG_LFB = false; static constexpr bool LOG_TEXTURE_RAM = false; static constexpr bool LOG_CMDFIFO = false; static constexpr bool LOG_CMDFIFO_VERBOSE = false; } //************************************************************************** // INTERNAL CLASSES //************************************************************************** // include register and render classes #include "voodoo_regs.h" #include "voodoo_render.h" namespace voodoo { // ======================> save_proxy // save_proxy is a helper class to make hierarchical state saving more manageable; class save_proxy { public: // constructor save_proxy(device_t &device) : m_device(device) { } // save an item; append the current prefix and pass through template void save_item(T &&item, char const *name) { std::string fullname = m_prefix; fullname += name; m_device.save_item(std::forward(item), fullname.c_str()); } // save a pointer item; append the current prefix and pass through template void save_pointer(T &&item, char const *name, u32 count) { std::string fullname = m_prefix; fullname += name; m_device.save_pointer(std::forward(item), fullname.c_str(), count); } // save a class; update the prefix then call the register_save method on the class template void save_class(T &item, char const *name) { std::string orig = m_prefix; m_prefix += name; m_prefix += "/"; item.register_save(*this); m_prefix = orig; } private: // internal state device_t &m_device; std::string m_prefix; }; // ======================> shared_tables // shared_tables are global tables that are shared between different components struct shared_tables { // construction shared_tables(); // texel lookups rgb_t *texel[16]; // 16 texture formats // 8-bit lookups rgb_t rgb332[256]; // RGB 3-3-2 lookup table rgb_t alpha8[256]; // alpha 8-bit lookup table rgb_t int8[256]; // intensity 8-bit lookup table rgb_t ai44[256]; // alpha, intensity 4-4 lookup table // 16-bit lookups rgb_t rgb565[65536]; // RGB 5-6-5 lookup table rgb_t argb1555[65536]; // ARGB 1-5-5-5 lookup table rgb_t argb4444[65536]; // ARGB 4-4-4-4 lookup table }; // ======================> tmu_state // tmu_state holds TMU-specific register and palette state class tmu_state { public: // construction tmu_state(); // initialization void init(int index, shared_tables const &share, u8 *ram, u32 size); // configuration void set_baseaddr_mask_shift(u32 mask, u8 shift) { m_basemask = mask; m_baseshift = shift; } // state saving void register_save(save_proxy &save); void post_load(); // simple getters voodoo_regs ®s() { return m_reg; } bool dirty() const { return m_regdirty; } // simple setters void mark_dirty() { m_regdirty = true; } // write to the NCC/palette registers void ncc_w(offs_t offset, u32 data); // prepare a texture for the renderer rasterizer_texture &prepare_texture(voodoo_renderer &renderer); private: // internal state int m_index; // index of ourself u8 *m_ram; // pointer to our RAM u32 m_mask; // mask to apply to pointers u32 m_basemask; // mask to apply to the texBaseAddr u8 m_baseshift; // shift to apply to the texBaseAddr // register state voodoo_regs m_reg; // TMU registers bool m_regdirty; // true if the LOD/mode/base registers have changed // lookups rgb_t const * const *m_texel_lookup; // texel lookups for each format // palettes bool m_palette_dirty[4]; // true if palette (0-1) or NCC (2-3) is dirty rgb_t m_palette[2][256]; // 2 versions of the palette }; // ======================> memory_fifo // memory_fifo is a simple memory access FIFO that is used on the frontend // PCI bus (64 entries) or within the framebuffer (up to 64k) class memory_fifo { public: // FIFO flags, added to offset static constexpr offs_t FLAGS_MASK = 0xf0000000; static constexpr offs_t TYPE_MASK = 0xc0000000; static constexpr offs_t TYPE_REGISTER = 0x00000000; static constexpr offs_t TYPE_LFB = 0x40000000; static constexpr offs_t TYPE_TEXTURE = 0x80000000; static constexpr offs_t NO_16_31 = 0x20000000; static constexpr offs_t NO_0_15 = 0x10000000; // construction memory_fifo(); // configuration void configure(u32 *base, u32 size); // state saving void register_save(save_proxy &save); // basic queries bool configured() const { return (m_size != 0); } u32 peek() const { return m_base[m_out]; } bool empty() const { return (m_in == m_out); } bool full() const { return ((m_in + 1) == m_out) || (m_in == (m_size - 1) && m_out == 0); } s32 items() const { s32 result = m_in - m_out; if (result < 0) result += m_size; return result; } s32 space() const { return m_size - 1 - items(); } // reset void reset() { m_in = m_out = 0; } // add/remove items void add(u32 data); u32 remove(); private: // internal state u32 *m_base; // base of the FIFO s32 m_size; // size of the FIFO s32 m_in; // input pointer s32 m_out; // output pointer }; // ======================> debug_stats // debug_stats are enabled via DEBUG_STATS and displayed via the backslash key; // these are independent of the real on-chip stats kept in thread_stats_block class debug_stats { public: // construction debug_stats(); // add in states from the emulation void add_emulation_stats(thread_stats_block const &block); // reset the stats void reset(); // simple getters bool displayed() const { return m_display; } char const *string() const { return m_string.c_str(); } // compute the string to display void update_string(rectangle const &visarea, u32 swap_history); // based on the current key state, update and return whether stats should be shown bool update_display_state(bool key_pressed); // public access to the statistics that are hand-updated s32 m_swaps; // total swaps s32 m_stalls; // total stalls s32 m_triangles; // total triangles s32 m_lfb_writes; // LFB writes s32 m_lfb_reads; // LFB reads s32 m_reg_writes; // register writes s32 m_reg_reads; // register reads s32 m_tex_writes; // texture writes s32 m_texture_mode[16]; // 16 different texture modes private: // stats that are updated from emulation stats s32 m_pixels_in; // total pixels in s32 m_pixels_out; // total pixels out s32 m_chroma_fail; // total chroma fail s32 m_zfunc_fail; // total z func fail s32 m_afunc_fail; // total a func fail s32 m_clipped; // total clipped s32 m_stippled; // total stippled // internal state bool m_lastkey; // last key state bool m_display; // display stats? std::string m_string; // string }; // ======================> static_register_table_entry // static_register_table_entry represents a read/write handler pair for Voodoo // registers, along with a valid mask, access flags, and a string name template struct static_register_table_entry { static constexpr u32 make_mask(int maskbits) { if (maskbits == 0) return 0; if (maskbits == 32) return 0xffffffff; return (1 << maskbits) - 1; } using read_handler = u32 (BaseType::*)(u32 chipmask, u32 regnum); using write_handler = u32 (BaseType::*)(u32 chipmask, u32 regnum, u32 data); u32 m_mask; // mask to apply to written data u32 m_chipmask_flags; // valid chips, plus flags char const *m_name; // string name write_handler m_write; // write handler read_handler m_read; // read handler }; // ======================> register_table_entry // register_table_entry is a live version of static_register_table_entry with // bound delegates in place of the member function pointers class register_table_entry { public: // internal delegates using read_handler = delegate; using write_handler = delegate; // flags for the chipmask static constexpr u32 CHIPMASK_NA = 0x0000; static constexpr u32 CHIPMASK_FBI = 0x0001; static constexpr u32 CHIPMASK_TREX = 0x0006; static constexpr u32 CHIPMASK_FBI_TREX = 0x0007; // flags for the sync state static constexpr u32 SYNC_NA = 0x0000; static constexpr u32 SYNC_NOSYNC = 0x0000; static constexpr u32 SYNC_SYNC = 0x0100; // flags for the FIFO state static constexpr u32 FIFO_NA = 0x0000; static constexpr u32 FIFO_NOFIFO = 0x0000; static constexpr u32 FIFO_FIFO = 0x0200; // simple getters char const *name() const { return m_name; } bool is_sync() const { return ((m_chipmask_flags & SYNC_SYNC) != 0); } bool is_fifo() const { return ((m_chipmask_flags & FIFO_FIFO) != 0); } // read/write helpers u32 read(voodoo_1_device &device, u32 chipmask, u32 regnum) const; u32 write(voodoo_1_device &device, u32 chipmask, u32 regnum, u32 data) const; // unpack from a static entry template void unpack(static_register_table_entry const &source, BaseType &device) { m_mask = source.m_mask; m_chipmask_flags = source.m_chipmask_flags; m_name = source.m_name; m_write = write_handler(source.m_write, &device); m_read = read_handler(source.m_read, &device); } private: // internal state u32 m_mask; // mask to apply to written data u32 m_chipmask_flags; // valid chips, plus flags char const *m_name; // string name write_handler m_write; // write handler read_handler m_read; // read handler }; } //************************************************************************** // GENERIC VOODOO DEVICE //************************************************************************** // ======================> generic_voodoo_device // generic_voodoo_device is a base class that can be used to represent any of the // specific voodoo devices below class generic_voodoo_device : public device_t, public device_video_interface { public: // configuration void set_fbmem(int value) { m_fbmem_in_mb = value; } void set_tmumem(int value1, int value2) { m_tmumem0_in_mb = value1; m_tmumem1_in_mb = value2; } void set_status_cycles(u32 value) { m_status_cycles = value; } template void set_cpu(T &&tag) { m_cpu.set_tag(std::forward(tag)); } auto vblank_callback() { return m_vblank_cb.bind(); } auto stall_callback() { return m_stall_cb.bind(); } auto pciint_callback() { return m_pciint_cb.bind(); } // getters voodoo::voodoo_model model() const { return m_model; } // address map and read/write helpers virtual void core_map(address_map &map) = 0; virtual u32 read(offs_t offset, u32 mem_mask = ~0) = 0; virtual void write(offs_t offset, u32 data, u32 mem_mask = ~0) = 0; // external control virtual void set_init_enable(u32 newval) = 0; // video update virtual int update(bitmap_rgb32 &bitmap, const rectangle &cliprect) = 0; protected: // internal construction generic_voodoo_device(const machine_config &mconfig, device_type type, const char *tag, device_t *owner, u32 clock, voodoo::voodoo_model model); // device-level overrides virtual void device_start() override; // configuration const voodoo::voodoo_model m_model; // which voodoo model u8 m_fbmem_in_mb; // framebuffer memory, in MB u8 m_tmumem0_in_mb; // TMU0 memory, in MB u8 m_tmumem1_in_mb; // TMU1 memory, in MB u32 m_status_cycles; // number of cycles to eat on status reads (optimization) required_device m_cpu; // the CPU we interact with devcb_write_line m_vblank_cb; // VBLANK callback devcb_write_line m_stall_cb; // stalling callback devcb_write_line m_pciint_cb; // PCI interrupt callback }; //************************************************************************** // VOODOO 1 DEVICE //************************************************************************** DECLARE_DEVICE_TYPE(VOODOO_1, voodoo_1_device) // ======================> voodoo_1_device // voodoo_1_device represents the original generation of 3dfx Voodoo Graphics devices; // these devices have independent framebuffer and texture memory, and can be flexibly // configured with 1 or 2 TMUs class voodoo_1_device : public generic_voodoo_device { friend class voodoo::register_table_entry; // enumeration describing reasons we might be stalled enum stall_state : u8 { NOT_STALLED = 0, STALLED_UNTIL_FIFO_LWM, STALLED_UNTIL_FIFO_EMPTY }; // flags for LFB writes static constexpr u8 LFB_RGB_PRESENT_0 = 0x01; static constexpr u8 LFB_ALPHA_PRESENT_0 = 0x02; static constexpr u8 LFB_DEPTH_PRESENT_0 = 0x04; static constexpr u8 LFB_DEPTH_PRESENT_MSW_0 = 0x08; static constexpr u8 LFB_PIXEL0_MASK = 0x0f; static constexpr u8 LFB_RGB_PRESENT_1 = 0x10; static constexpr u8 LFB_ALPHA_PRESENT_1 = 0x20; static constexpr u8 LFB_DEPTH_PRESENT_1 = 0x40; static constexpr u8 LFB_PIXEL1_MASK = 0x70; protected: // number of clocks to set up a triangle (just a guess) static constexpr u32 TRIANGLE_SETUP_CLOCKS = 100; // internal construction voodoo_1_device(const machine_config &mconfig, device_type type, const char *tag, device_t *owner, u32 clock, voodoo::voodoo_model model); public: // nominal clock values static constexpr u32 NOMINAL_CLOCK = 50'000'000; // construction voodoo_1_device(const machine_config &mconfig, const char *tag, device_t *owner, u32 clock) : voodoo_1_device(mconfig, VOODOO_1, tag, owner, clock, voodoo::voodoo_model::VOODOO_1) { } // destruction virtual ~voodoo_1_device(); // address map and read/write helpers virtual void core_map(address_map &map) override; virtual u32 read(offs_t offset, u32 mem_mask = ~0) override; virtual void write(offs_t offset, u32 data, u32 mem_mask = ~0) override; // external control virtual void set_init_enable(u32 newval) override; // video update virtual int update(bitmap_rgb32 &bitmap, const rectangle &cliprect) override; protected: // device-level overrides virtual void device_start() override; virtual void device_stop() override; virtual void device_reset() override; virtual void device_post_load() override; // system management virtual void soft_reset(); virtual void register_save(voodoo::save_proxy &save, u32 total_allocation); // buffer accessors virtual u16 *draw_buffer_indirect(int index); virtual u16 *lfb_buffer_indirect(int index); u16 *draw_buffer(int index) const { return (u16 *)(m_fbram + m_rgboffs[index]); } u16 *front_buffer() const { return draw_buffer(m_frontbuf); } u16 *back_buffer() const { return draw_buffer(m_backbuf); } u16 *aux_buffer() const { return (m_auxoffs != ~0) ? (u16 *)(m_fbram + m_auxoffs) : nullptr; } u16 *ram_end() const { return (u16 *)(m_fbram + m_fbmask + 1); } // read/write and FIFO helpers void prepare_for_read(); bool prepare_for_write(); void recompute_fbmem_fifo(); void add_to_fifo(u32 offset, u32 data, u32 mem_mask); void flush_fifos(attotime current_time); virtual u32 execute_fifos(); // mapped reads u32 map_register_r(offs_t offset); u32 map_lfb_r(offs_t offset); // mapped writes void map_register_w(offs_t offset, u32 data, u32 mem_mask = ~0); void map_lfb_w(offs_t offset, u32 data, u32 mem_mask = ~0); void map_texture_w(offs_t offset, u32 data, u32 mem_mask = ~0); // internal reads and writes u32 internal_lfb_r(offs_t offset); void internal_lfb_w(offs_t offset, u32 data, u32 mem_mask); u32 expand_lfb_data(voodoo::reg_lfb_mode const lfbmode, u32 data, rgb_t src_color[2], u16 src_depth[2]); virtual void internal_texture_w(offs_t offset, u32 data); // register read accessors u32 reg_invalid_r(u32 chipmask, u32 regnum); u32 reg_passive_r(u32 chipmask, u32 regnum); u32 reg_status_r(u32 chipmask, u32 regnum); u32 reg_fbiinit2_r(u32 chipmask, u32 regnum); u32 reg_vretrace_r(u32 chipmask, u32 regnum); u32 reg_stats_r(u32 chipmask, u32 regnum); // register write accessors u32 reg_invalid_w(u32 chipmask, u32 regnum, u32 data); u32 reg_unimplemented_w(u32 chipmask, u32 regnum, u32 data); u32 reg_passive_w(u32 chipmask, u32 regnum, u32 data); u32 reg_fpassive_4_w(u32 chipmask, u32 regnum, u32 data); u32 reg_fpassive_12_w(u32 chipmask, u32 regnum, u32 data); u32 reg_starts_w(u32 chipmask, u32 regnum, u32 data); u32 reg_startt_w(u32 chipmask, u32 regnum, u32 data); u32 reg_dsdx_w(u32 chipmask, u32 regnum, u32 data); u32 reg_dtdx_w(u32 chipmask, u32 regnum, u32 data); u32 reg_dsdy_w(u32 chipmask, u32 regnum, u32 data); u32 reg_dtdy_w(u32 chipmask, u32 regnum, u32 data); u32 reg_fstarts_w(u32 chipmask, u32 regnum, u32 data); u32 reg_fstartt_w(u32 chipmask, u32 regnum, u32 data); u32 reg_fdsdx_w(u32 chipmask, u32 regnum, u32 data); u32 reg_fdtdx_w(u32 chipmask, u32 regnum, u32 data); u32 reg_fdsdy_w(u32 chipmask, u32 regnum, u32 data); u32 reg_fdtdy_w(u32 chipmask, u32 regnum, u32 data); u32 reg_startw_w(u32 chipmask, u32 regnum, u32 data); u32 reg_dwdx_w(u32 chipmask, u32 regnum, u32 data); u32 reg_dwdy_w(u32 chipmask, u32 regnum, u32 data); u32 reg_fstartw_w(u32 chipmask, u32 regnum, u32 data); u32 reg_fdwdx_w(u32 chipmask, u32 regnum, u32 data); u32 reg_fdwdy_w(u32 chipmask, u32 regnum, u32 data); u32 reg_triangle_w(u32 chipmask, u32 regnum, u32 data); u32 reg_nop_w(u32 chipmask, u32 regnum, u32 data); u32 reg_fastfill_w(u32 chipmask, u32 regnum, u32 data); u32 reg_swapbuffer_w(u32 chipmask, u32 regnum, u32 data); u32 reg_fogtable_w(u32 chipmask, u32 regnum, u32 data); u32 reg_fbiinit_w(u32 chipmask, u32 regnum, u32 data); u32 reg_video_w(u32 chipmask, u32 regnum, u32 data); u32 reg_clut_w(u32 chipmask, u32 regnum, u32 data); u32 reg_dac_w(u32 chipmask, u32 regnum, u32 data); u32 reg_texture_w(u32 chipmask, u32 regnum, u32 data); u32 reg_palette_w(u32 chipmask, u32 regnum, u32 data); // VBLANK timing void adjust_vblank_start_timer(); virtual void vblank_start(void *ptr, s32 param); virtual void vblank_stop(void *ptr, s32 param); void swap_buffers(); virtual void rotate_buffers(); // video timing and updates int update_common(bitmap_rgb32 &bitmap, const rectangle &cliprect, rgb_t const *pens); void recompute_video_timing(u32 hsyncon, u32 hsyncoff, u32 hvis, u32 hbp, u32 vsyncon, u32 vsyncoff, u32 vvis, u32 vbp); virtual void recompute_video_memory(); void recompute_video_memory_common(u32 config, u32 rowpixels); // rendering voodoo::voodoo_renderer &renderer() { return *m_renderer; } s32 triangle(); // statistics void accumulate_statistics(const voodoo::thread_stats_block &block); void update_statistics(bool accumulate); void reset_counters(); // stall management bool operation_pending() const { return !m_operation_end.is_zero(); } void clear_pending_operation() { m_operation_end = attotime::zero; } void check_stalled_cpu(attotime current_time); void stall_cpu(stall_state state); void stall_resume_callback(void *ptr, s32 param); // misc helpers u32 chipmask_from_offset(u32 offset) { u32 chipmask = BIT(offset, 8, 4); if (chipmask == 0) chipmask = 0xf; return chipmask & m_chipmask; } // configuration u8 m_chipmask; // mask for which chips are available // PCI state/FIFOs voodoo::reg_init_en m_init_enable; // initEnable value (set externally) stall_state m_stall_state; // state of the system if we're stalled int m_stall_trigger; // trigger used for stalling attotime m_operation_end; // time when the pending operation ends voodoo::memory_fifo m_pci_fifo; // PCI FIFO voodoo::memory_fifo m_fbmem_fifo; // framebuffer memory fifo bool m_flush_flag; // true if we are currently flushing FIFOs // allocated memory u8 *m_fbram; // pointer to aligned framebuffer u32 m_fbmask; // mask to apply to pointers std::unique_ptr m_memory; // allocated framebuffer/texture memory std::unique_ptr m_shared; // shared tables std::unique_ptr m_renderer; // rendering helper // video buffer configuration u32 m_rgboffs[3]; // word offset to 3 RGB buffers u32 m_auxoffs; // word offset to 1 aux buffer u8 m_frontbuf; // front buffer index u8 m_backbuf; // back buffer index bool m_video_changed; // did the frontbuffer video change? // linear frame buffer access configuration u8 m_lfb_stride; // stride of LFB accesses in bits // video configuration u32 m_width; // width of current frame buffer u32 m_height; // height of current frame buffer u32 m_xoffs; // horizontal offset (back porch) u32 m_yoffs; // vertical offset (back porch) u32 m_vsyncstart; // vertical sync start scanline u32 m_vsyncstop; // vertical sync stop // VBLANK/swapping state u8 m_swaps_pending; // number of pending swaps u8 m_vblank; // VBLANK state u8 m_vblank_count; // number of VBLANKs since last swap u8 m_vblank_swap_pending; // a swap is pending, waiting for a vblank u8 m_vblank_swap; // swap when we hit this count u8 m_vblank_dont_swap; // don't actually swap when we hit this point // register state voodoo::voodoo_regs m_reg; // FBI registers voodoo::register_table_entry m_regtable[256]; // generated register table voodoo::tmu_state m_tmu[2]; // TMU states u8 m_dac_reg[32]; // up to 32 DAC registers u8 m_dac_read_result; // pending DAC read result // timers emu_timer *m_vsync_start_timer; // VBLANK timer emu_timer *m_vsync_stop_timer; // VBLANK end timer emu_timer *m_stall_resume_timer; // timer to resume processing after stall // statistics voodoo::debug_stats m_stats; // internal statistics voodoo::thread_stats_block m_lfb_stats; // LFB access statistics // tracking for logging offs_t m_last_status_pc; // PC of last status description (for logging) u32 m_last_status_value; // value of last status read (for logging) // memory for PCI FIFO u32 m_pci_fifo_mem[64*2]; // memory backing the PCI FIFO // pens and CLUT bool m_clut_dirty; // do we need to recompute? std::vector m_clut; // clut gamma data std::vector m_pen; // mapping from pixels to pens // register table static voodoo::static_register_table_entry const s_register_table[256]; }; //************************************************************************** // INLINE FUNCITIONS //************************************************************************** //------------------------------------------------- // write - handle a write through a register table // entry, performing any necessary logging //------------------------------------------------- inline u32 voodoo::register_table_entry::write(voodoo_1_device &device, u32 chipmask, u32 regnum, u32 data) const { // statistics if enabled if (DEBUG_STATS) device.m_stats.m_reg_writes++; // log if enabled if (LOG_REGISTERS) { if (regnum < voodoo_regs::reg_fvertexAx || regnum > voodoo_regs::reg_fdWdY) device.logerror("VOODOO.REG:%s(%d) write = %08X\n", m_name, chipmask, data); else device.logerror("VOODOO.REG:%s(%d) write = %f\n", m_name, chipmask, double(u2f(data))); } return m_write(chipmask & m_chipmask_flags, regnum, data & m_mask); } //------------------------------------------------- // read - handle a read through a register table // entry, performing any necessary logging //------------------------------------------------- inline u32 voodoo::register_table_entry::read(voodoo_1_device &device, u32 chipmask, u32 regnum) const { // statistics if enabled if (DEBUG_STATS) device.m_stats.m_reg_reads++; // get the result u32 result = m_read(chipmask & m_chipmask_flags, regnum) & m_mask; // log if enabled if (LOG_REGISTERS) { // don't log multiple identical status reads from the same address bool logit = true; if (regnum == voodoo_regs::reg_vdstatus) { offs_t pc = device.m_cpu->pc(); if (pc == device.m_last_status_pc && result == device.m_last_status_value) logit = false; device.m_last_status_pc = pc; device.m_last_status_value = result; } if (regnum == voodoo_regs::reg_cmdFifoRdPtr) logit = false; if (logit) device.logerror("VOODOO.REG:%s read = %08X\n", m_name, result); } return result; } #endif // MAME_VIDEO_VOODOO_H