summaryrefslogtreecommitdiffstatshomepage
path: root/src/devices/video/pc_vga_nvidia.cpp
blob: 31d5c23c6a4501dbc1213415675d861151474330 (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
// license:BSD-3-Clause
// copyright-holders: Angelo Salese
// thanks-to: Marcelina Koscielnicka
/**************************************************************************************************

nVidia NV3:G80 VGA core

TODO:
- Many blanks in the CRTC section;
\- Untested beyond 16bpp modes;
\- Regular VGA gfx modes don't draw text properly in some tests (common bug with other VGA archs);
\- Diamond Viper BIOS don't report back the video card string at POST;
\- STB BIOS sets 640x481 after POST;
\- ASUS BIOS sets 640x428 (?);
- The "red screen" from VGA SENSE may really be a ping from monitor signal;
- Sequencer register $6 for lock/unlock of extended VGA;
- I2C;
- Interrupts;
- RMA access;
- PLL portion(s), understand how it chooses extended clocks;
- Verify that NV10 really uses this chip (seems to access more regs?)
- Video overlay (PVIDEO, NV10+);
- VGA stack (NV40+);
- TV encoder (NV17:NV20, NV30:G80);

Notes:
- NV1 looks very different, not worth to subclass here;

References:
- https://envytools.readthedocs.io/en/latest/hw/display/nv3/index.html

**************************************************************************************************/

#include "emu.h"
#include "pc_vga_nvidia.h"

#define LOG_WARN      (1U << 1)
#define LOG_RMA       (1U << 2) // extensively accessed even on POST
#define LOG_CRTC      (1U << 3)

#define VERBOSE (LOG_GENERAL | LOG_WARN | LOG_CRTC)
//#define LOG_OUTPUT_FUNC osd_printf_info
#include "logmacro.h"

#define LOGWARN(...)            LOGMASKED(LOG_WARN, __VA_ARGS__)
#define LOGRMA(...)             LOGMASKED(LOG_RMA, __VA_ARGS__)
#define LOGCRTC(...)            LOGMASKED(LOG_CRTC, __VA_ARGS__)

DEFINE_DEVICE_TYPE(NVIDIA_NV3_VGA,  nvidia_nv3_vga_device,  "nvidia_nv3_vga",  "nVidia NV3 VGA i/f")

nvidia_nv3_vga_device::nvidia_nv3_vga_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock)
	: svga_device(mconfig, NVIDIA_NV3_VGA, tag, owner, clock)
{
	m_main_if_space_config = address_space_config("io_regs", ENDIANNESS_LITTLE, 8, 4, 0, address_map_constructor(FUNC(nvidia_nv3_vga_device::io_3bx_3dx_map), this));
	m_crtc_space_config = address_space_config("crtc_regs", ENDIANNESS_LITTLE, 8, 8, 0, address_map_constructor(FUNC(nvidia_nv3_vga_device::crtc_map), this));
}

void nvidia_nv3_vga_device::device_add_mconfig(machine_config &config)
{
	// TODO: I2C, likely for DDC (AIDA16 extensively test that in non-safe mode)
}

static INPUT_PORTS_START(nv3_vga_sense)
	PORT_START("VGA_SENSE")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_UNKNOWN )
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_UNKNOWN )
	PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_UNKNOWN ) // causes a red screen at POST if low
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_UNKNOWN )
INPUT_PORTS_END

ioport_constructor nvidia_nv3_vga_device::device_input_ports() const
{
	return INPUT_PORTS_NAME(nv3_vga_sense);
}

void nvidia_nv3_vga_device::device_start()
{
	svga_device::device_start();
	zero();

	vga.crtc.maximum_scan_line = 1;

	// TODO: shared with main GPU, fake it for now
	// Start address ends at 20 bits so 0x1fffff mask / 2,097,151 bytes / 2MB window is (theorically) given
	// we assume that is really 4 MB, VESA tests extensively tests with 0x7f banks
	vga.svga_intf.vram_size = 4*1024*1024;

	for (int i = 0; i < 0x100; i++)
		set_pen_color(i, pal1bit(i & 1), pal1bit((i & 2) >> 1), pal1bit((i & 4) >> 2));

//  vga.miscellaneous_output = 0;
	m_ext_offset = 0;
	save_item(NAME(m_ext_offset));
}

void nvidia_nv3_vga_device::io_3bx_3dx_map(address_map &map)
{
	svga_device::io_3bx_3dx_map(map);
	map(0x00, 0x03).lrw8(
		NAME([this] (offs_t offset) {
			LOGRMA("RMA_ACCESS R %02x\n", offset & 3);
			return space().unmap();
		}),
		NAME([this] (offs_t offset, u8 data) {
			LOGRMA("RMA_ACCESS W %02x -> %02x\n", offset & 3, data);
		})
	);
}

uint8_t nvidia_nv3_vga_device::mem_r(offs_t offset)
{
	if (svga.rgb8_en || svga.rgb15_en || svga.rgb16_en || svga.rgb24_en || svga.rgb32_en)
		return svga_device::mem_linear_r(offset + (svga.bank_r * 0x8000));
	return svga_device::mem_r(offset);
}

void nvidia_nv3_vga_device::mem_w(offs_t offset, uint8_t data)
{
	if (svga.rgb8_en || svga.rgb15_en || svga.rgb16_en || svga.rgb24_en || svga.rgb32_en)
	{
		svga_device::mem_linear_w(offset + (svga.bank_w * 0x8000), data);
		return;
	}
	svga_device::mem_w(offset, data);
}

uint16_t nvidia_nv3_vga_device::offset()
{
	// TODO: check true enable condition
	if (svga.rgb8_en || svga.rgb16_en || svga.rgb24_en || svga.rgb32_en)
		return (vga.crtc.offset << 3) + m_ext_offset;
	return svga_device::offset();
}

// TODO: lock mechanism
void nvidia_nv3_vga_device::crtc_map(address_map &map)
{
	svga_device::crtc_map(map);
	// REPAINT_0
/*
 * xxx- ---- Row offset bits 3-5
 * ---x xxxx Start address bits 16-20
 */
	map(0x19, 0x19).lrw8(
		NAME([this] (offs_t offset) {
			return m_repaint[0];
		}),
		NAME([this] (offs_t offset, u8 data) {
			// TODO: untested, likely used in extended text modes
			//vga.crtc.start_addr_latch &= ~0x1f0000;
			//vga.crtc.start_addr_latch |= ((data & 0x1f) << 16);
			m_ext_offset = ((data & 0xe0) >> 5) << 11;
			m_repaint[0] = data;
			//m_ext_offset = recalculate_ext_offset();
			LOGCRTC("EXT CRTC: REPAINT_0 %02x\n", data);
		})
	);
	// REPAINT_1
/*
 * ???? ?-?? <unknown, definitely used>
 * ---- -x-- Shifts row offset by 1 bit (verify)
 */
	map(0x1a, 0x1a).lrw8(
		NAME([this] (offs_t offset) {
			return m_repaint[1];
		}),
		NAME([this] (offs_t offset, u8 data) {
			// TODO: this definitely uses more regs in the equation
			//                      REPAINT_0 REPAINT_1 base_offset  <expected_extra_offset>
			// 10eh 320x200   16bpp 0x00      0x3c      0x50         560  / 0x230 (with scan doubler?)
			// 111h 640x480   16bpp 0x00      0x3c      0xa0         1120 / 0x460
			// 114h 800x600   16bpp 0x00      0x3c      0xc8         1400 / 0x578
			// 117h 1024x768  16bpp 0x20      0x3c      0x00         2048 / 0x800
			// 11ah 1280x1024 16bpp 0x20      0x38      0x40         2496 / 0x9c0
			// 132h 320x400   16bpp 0x00      0x3c      0x50         560  / 0x230
			// 13dh 640x400   16bpp 0x00      0x3c      0xa0         1120 / 0x460
			// 142h 1152x864  16bpp 0x20      0x3c      0x20         2272 / 0x8e0
			// 146h 1600x1200 16bpp 0x00      0x38      0x90         3056 / 0xbf0
			// 14bh 960x720   16bpp 0x10      0x3c      0xf0         1680 / 0x690
			m_repaint[1] = data;
			//m_ext_offset = recalculate_ext_offset();
			LOGCRTC("EXT CRTC: REPAINT_1 %02x\n", data);
		})
	);
	// WRITE_BANK
/*
 * --xx xxxx write bank, in 32k units
 */
	map(0x1d, 0x1d).lrw8(
		NAME([this] (offs_t offset) {
			return svga.bank_w;
		}),
		NAME([this] (offs_t offset, u8 data) {
			if (data & 0x80)
				LOGWARN("WRITE_BANK: attempt to write to undocumented bits %02x\n", data);
			svga.bank_w = data & 0x7f;
		})
	);
	// READ_BANK
/*
 * --xx xxxx read bank
 */
	map(0x1e, 0x1e).lrw8(
		NAME([this] (offs_t offset) {
			return svga.bank_r;
		}),
		NAME([this] (offs_t offset, u8 data) {
			if (data & 0x80)
				LOGWARN("READ_BANK: attempt to write to undocumented bits %02x\n", data);
			svga.bank_r = data & 0x7f;
		})
	);
	// EXTENDED_VERT
/*
 * ---x ---- Horizontal Total bit 8
 * ---- x--- Vertical Sync Start bit 10
 * ---- -x-- Vertical Blank Start bit 10
 * ---- --x- Vertical Display End bit 10
 * ---- ---x Vertical Total bit 10
 */
	map(0x25, 0x25).lrw8(
		NAME([this] (offs_t offset) {
			return vga.crtc.data[0x25];
		}),
		NAME([this] (offs_t offset, u8 data) {
			if (data & 0xe0)
				LOGWARN("EXTENDED_VERT: attempt to write to undocumented bits %02x\n", data);
			LOGCRTC("EXT CRTC: EXTENDED_VERT %02x\n", data & 0x1f);
			vga.crtc.horz_total =       (vga.crtc.horz_total & 0x00ff)       | ((BIT(data, 4) << 8));
			//vga.crtc.?? = (vga.crtc.?? & 0x03ff) | ((BIT(data, 3) << 8));
			vga.crtc.vert_blank_start = (vga.crtc.vert_blank_start & 0x03ff) | ((BIT(data, 2) << 10));
			vga.crtc.vert_disp_end =    (vga.crtc.vert_disp_end & 0x03ff)    | ((BIT(data, 1) << 10));
			vga.crtc.vert_total =       (vga.crtc.vert_total & 0x03ff)       | ((BIT(data, 0) << 10));
			vga.crtc.data[0x25] = data;
			recompute_params();
		})
	);
	// PIXEL_FMT
/*
 * ---- --xx Pixel Format
 * ---- --00 VGA
 * ---- --01 8bpp
 * ---- --10 16bpp
 * ---- --11 32bpp
 */
	map(0x28, 0x28).lrw8(
		NAME([this] (offs_t offset) {
			return svga.rgb32_en ? 3 : svga.rgb16_en ? 2 : svga.rgb8_en ? 1 : 0;
		}),
		NAME([this] (offs_t offset, u8 data) {
			if (data & 0xfc)
				LOGWARN("PIXEL_FMT: attempt to write to undocumented bits %02x\n", data);
			data &= 3;
			// NB: NV3 VESA doesn't have 15bpp support
			svga.rgb32_en = svga.rgb16_en = svga.rgb15_en = svga.rgb8_en = 0;
			switch(data)
			{
				case 0:
					LOGCRTC("PIXEL_FMT: VGA mode selected\n");
					break;
				case 1:
					svga.rgb8_en = 1;
					LOGCRTC("PIXEL_FMT: SVGA 8bpp mode selected\n");
					break;
				case 2:
					svga.rgb16_en = 1;
					LOGCRTC("PIXEL_FMT: SVGA 16bpp mode selected\n");
					break;
				case 3:
					svga.rgb32_en = 1;
					LOGCRTC("PIXEL_FMT: SVGA 32bpp mode selected\n");
					break;
			}
		})
	);
	// EXTENDED_HORZ
/*
 * ---- ---x Horizontal Display End bit 8
 */
	map(0x2d, 0x2d).lrw8(
		NAME([this] (offs_t offset) {
			return (vga.crtc.horz_disp_end >> 10) & 1;
		}),
		NAME([this] (offs_t offset, u8 data) {
			if (data & 0xfc)
				LOGWARN("EXTENDED_HORZ: attempt to write to undocumented bits %02x\n", data);
			LOGCRTC("EXT CRTC: EXTENDED_HORZ %02x\n", data & 1);
			vga.crtc.horz_disp_end = (vga.crtc.horz_disp_end & 0x00ff) | ((data & 0x01) << 8);
			recompute_params();
		})
	);
	// RMA_MODE
/*
 * ---- xxx- RMA index
 * ---- ---x enable RMA
 */
	map(0x38, 0x38).lrw8(
		NAME([this] (offs_t offset) {
			u8 res = vga.crtc.data[0x38];
			LOGRMA("RMA_MODE read %02x\n", res);
			return res;
		}),
		NAME([this] (offs_t offset, u8 data) {
			vga.crtc.data[0x38] = data;
			LOGRMA("RMA_MODE write %s, index %d\n", data & 1 ? "enable" : "disable", (data & 0xe) >> 1);
		})
	);
	// I2C_READ
/*
 * ---- x--- SDA
 * ---- -x-- SCL
 */
// map(0x3e, 0x3e)
	// I2C_WRITE
/*
 * ---x ---- SCL
 * ---- x--- SDA
 */
// map(0x3f, 0x3f)
}