summaryrefslogtreecommitdiffstatshomepage
path: root/src/devices/video/voodoo.h
blob: 94a20c0f7af196047ffb5f533e0c3f113113384c (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
// 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<typename T>
	void save_item(T &&item, char const *name)
	{
		std::string fullname = m_prefix;
		fullname += name;
		m_device.save_item(std::forward<T>(item), fullname.c_str());
	}

	// save a pointer item; append the current prefix and pass through
	template<typename T>
	void save_pointer(T &&item, char const *name, u32 count)
	{
		std::string fullname = m_prefix;
		fullname += name;
		m_device.save_pointer(std::forward<T>(item), fullname.c_str(), count);
	}

	// save a class; update the prefix then call the register_save method on the class
	template<typename T>
	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 &regs() { 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<typename BaseType>
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<u32 (u32, u32)>;
	using write_handler = delegate<u32 (u32, u32, u32)>;

	// 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<typename BaseType>
	void unpack(static_register_table_entry<BaseType> 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 <typename T> void set_cpu(T &&tag) { m_cpu.set_tag(std::forward<T>(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<cpu_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(s32 param);
	virtual void vblank_stop(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(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<u8[]> m_memory;          // allocated framebuffer/texture memory
	std::unique_ptr<voodoo::shared_tables> m_shared; // shared tables
	std::unique_ptr<voodoo::voodoo_renderer> 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<rgb_t> m_clut;               // clut gamma data
	std::vector<rgb_t> m_pen;                // mapping from pixels to pens

	// register table
	static voodoo::static_register_table_entry<voodoo_1_device> 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