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)
}
|