/[gxemul]/trunk/src/devices/dev_pvr.c
This is repository of my old source code which isn't updated any more. Go to git.rot13.org for current projects!
ViewVC logotype

Contents of /trunk/src/devices/dev_pvr.c

Parent Directory Parent Directory | Revision Log Revision Log


Revision 42 - (show annotations)
Mon Oct 8 16:22:32 2007 UTC (16 years, 6 months ago) by dpavlin
File MIME type: text/plain
File size: 31578 byte(s)
++ trunk/HISTORY	(local)
$Id: HISTORY,v 1.1613 2007/06/15 20:11:26 debug Exp $
20070501	Continuing a little on m88k disassembly (control registers,
		more instructions).
		Adding a dummy mvme88k machine mode.
20070502	Re-adding MIPS load/store alignment exceptions.
20070503	Implementing more of the M88K disassembly code.
20070504	Adding disassembly of some more M88K load/store instructions.
		Implementing some relatively simple M88K instructions (br.n,
		xor[.u] imm, and[.u] imm).
20070505	Implementing M88K three-register and, or, xor, and jmp[.n],
		bsr[.n] including function call trace stuff.
		Applying a patch from Bruce M. Simpson which implements the
		SYSCON_BOARD_CPU_CLOCK_FREQ_ID object of the syscon call in
		the yamon PROM emulation.
20070506	Implementing M88K bb0[.n] and bb1[.n], and skeletons for
		ldcr and stcr (although no control regs are implemented yet).
20070509	Found and fixed the bug which caused Linux for QEMU_MIPS to
		stop working in 0.4.5.1: It was a faulty change to the MIPS
		'sc' and 'scd' instructions I made while going through gcc -W
		warnings on 20070428.
20070510	Updating the Linux/QEMU_MIPS section in guestoses.html to
		use mips-test-0.2.tar.gz instead of 0.1.
		A big thank you to Miod Vallat for sending me M88K manuals.
		Implementing more M88K instructions (addu, subu, div[u], mulu,
		ext[u], clr, set, cmp).
20070511	Fixing bugs in the M88K "and" and "and.u" instructions (found
		by comparing against the manual).
		Implementing more M88K instructions (mask[.u], mak, bcnd (auto-
		generated)) and some more control register details.
		Cleanup: Removing the experimental AVR emulation mode and
		corresponding devices; AVR emulation wasn't really meaningful.
		Implementing autogeneration of most M88K loads/stores. The
		rectangle drawing demo (with -O0) for M88K runs :-)
		Beginning on M88K exception handling.
		More M88K instructions: tb0, tb1, rte, sub, jsr[.n].
		Adding some skeleton MVME PROM ("BUG") emulation.
20070512	Fixing a bug in the M88K cmp instruction.
		Adding the M88K lda (scaled register) instruction.
		Fixing bugs in 64-bit (32-bit pairs) M88K loads/stores.
		Removing the unused tick_hz stuff from the machine struct.
		Implementing the M88K xmem instruction. OpenBSD/mvme88k gets
		far enough to display the Copyright banner :-)
		Implementing subu.co (guess), addu.co, addu.ci, ff0, and ff1.
		Adding a dev_mvme187, for MVME187-specific devices/registers.
		OpenBSD/mvme88k prints more boot messages. :)
20070515	Continuing on MVME187 emulation (adding more devices, beginning
		on the CMMUs, etc).
		Adding the M88K and.c, xor.c, and or.c instructions, and making
		sure that mul, div, etc cause exceptions if executed when SFD1
		is disabled.
20070517	Continuing on M88K and MVME187 emulation in general; moving
		the CMMU registers to the CPU struct, separating dev_pcc2 from
		dev_mvme187, and beginning on memory_m88k.c (BATC and PATC).
		Fixing a bug in 64-bit (32-bit pairs) M88K fast stores.
		Implementing the clock part of dev_mk48txx.
		Implementing the M88K fstcr and xcr instructions.
		Implementing m88k_cpu_tlbdump().
		Beginning on the implementation of a separate address space
		for M88K .usr loads/stores.
20070520	Removing the non-working (skeleton) Sandpoint, SonyNEWS, SHARK
		Dnard, and Zaurus machine modes.
		Experimenting with dyntrans to_be_translated read-ahead. It
		seems to give a very small performance increase for MIPS
		emulation, but a large performance degradation for SuperH. Hm.
20070522	Disabling correct SuperH ITLB emulation; it does not seem to be
		necessary in order to let SH4 guest OSes run, and it slows down
		userspace code.
		Implementing "samepage" branches for SuperH emulation, and some
		other minor speed hacks.
20070525	Continuing on M88K memory-related stuff: exceptions, memory
		transaction register contents, etc.
		Implementing the M88K subu.ci instruction.
		Removing the non-working (skeleton) Iyonix machine mode.
		OpenBSD/mvme88k reaches userland :-), starts executing
		/sbin/init's instructions, and issues a few syscalls, before
		crashing.
20070526	Fixing bugs in dev_mk48txx, so that OpenBSD/mvme88k detects
		the correct time-of-day.
		Implementing a generic IRQ controller for the test machines
		(dev_irqc), similar to a proposed patch from Petr Stepan.
		Experimenting some more with translation read-ahead.
		Adding an "expect" script for automated OpenBSD/landisk
		install regression/performance tests.
20070527	Adding a dummy mmEye (SH3) machine mode skeleton.
		FINALLY found the strange M88K bug I have been hunting: I had
		not emulated the SNIP value for exceptions occurring in
		branch delay slots correctly.
		Implementing correct exceptions for 64-bit M88K loads/stores.
		Address to symbol lookups are now disabled when M88K is
		running in usermode (because usermode addresses don't have
		anything to do with supervisor addresses).
20070531	Removing the mmEye machine mode skeleton.
20070604	Some minor code cleanup.
20070605	Moving src/useremul.c into a subdir (src/useremul/), and
		cleaning up some more legacy constructs.
		Adding -Wstrict-aliasing and -fstrict-aliasing detection to
		the configure script.
20070606	Adding a check for broken GCC on Solaris to the configure
		script. (GCC 3.4.3 on Solaris cannot handle static variables
		which are initialized to 0 or NULL. :-/)
		Removing the old (non-working) ARC emulation modes: NEC RD94,
		R94, R96, and R98, and the last traces of Olivetti M700 and
		Deskstation Tyne.
		Removing the non-working skeleton WDSC device (dev_wdsc).
20070607	Thinking about how to use the host's cc + ld at runtime to
		generate native code. (See experiments/native_cc_ld_test.i
		for an example.)
20070608	Adding a program counter sampling timer, which could be useful
		for native code generation experiments.
		The KN02_CSR_NRMMOD bit in the DECstation 5000/200 (KN02) CSR
		should always be set, to allow a 5000/200 PROM to boot.
20070609	Moving out breakpoint details from the machine struct into
		a helper struct, and removing the limit on max nr of
		breakpoints.
20070610	Moving out tick functions into a helper struct as well (which
		also gets rid of the max limit).
20070612	FINALLY figured out why Debian/DECstation stopped working when
		translation read-ahead was enabled: in src/memory_rw.c, the
		call to invalidate_code_translation was made also if the
		memory access was an instruction load (if the page was mapped
		as writable); it shouldn't be called in that case.
20070613	Implementing some more MIPS32/64 revision 2 instructions: di,
		ei, ext, dext, dextm, dextu, and ins.
20070614	Implementing an instruction combination for the NetBSD/arm
		idle loop (making the host not use any cpu if NetBSD/arm
		inside the emulator is not using any cpu).
		Increasing the nr of ARM VPH entries from 128 to 384.
20070615	Removing the ENABLE_arch stuff from the configure script, so
		that all included architectures are included in both release
		and development builds.
		Moving memory related helper functions from misc.c to memory.c.
		Adding preliminary instructions for netbooting NetBSD/pmppc to
		guestoses.html; it doesn't work yet, there are weird timeouts.
		Beginning a total rewrite of the userland emulation modes
		(removing all emulation modes, beginning from scratch with
		NetBSD/MIPS and FreeBSD/Alpha only).
20070616	After fixing a bug in the DEC21143 NIC (the TDSTAT_OWN bit was
		only cleared for the last segment when transmitting, not all
		segments), NetBSD/pmppc boots with root-on-nfs without the
		timeouts. Updating guestoses.html.
		Removing the skeleton PSP (Playstation Portable) mode.
		Moving X11-related stuff in the machine struct into a helper
		struct.
		Cleanup of out-of-memory checks, to use a new CHECK_ALLOCATION
		macro (which prints a meaningful error message).
		Adding a COMMENT to each machine and device (for automagic
		.index comment generation).
		Doing regression testing for the next release.

==============  RELEASE 0.4.6  ==============


1 /*
2 * Copyright (C) 2006-2007 Anders Gavare. All rights reserved.
3 *
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions are met:
6 *
7 * 1. Redistributions of source code must retain the above copyright
8 * notice, this list of conditions and the following disclaimer.
9 * 2. Redistributions in binary form must reproduce the above copyright
10 * notice, this list of conditions and the following disclaimer in the
11 * documentation and/or other materials provided with the distribution.
12 * 3. The name of the author may not be used to endorse or promote products
13 * derived from this software without specific prior written permission.
14 *
15 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
16 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
17 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
18 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
19 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
20 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
21 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
22 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
23 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
24 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
25 * SUCH DAMAGE.
26 *
27 *
28 * $Id: dev_pvr.c,v 1.24 2007/06/15 19:57:33 debug Exp $
29 *
30 * COMMENT: PowerVR CLX2 (graphics controller used in the Dreamcast)
31 *
32 * Implemented by reading http://www.ludd.luth.se/~jlo/dc/powervr-reg.txt and
33 * http://mc.pp.se/dc/pvr.html, source code of various demos and KalistOS,
34 * and doing a lot of guessing.
35 *
36 * TODO: Almost everything
37 *
38 * x) Change resolution during runtime (PAL/NTSC/???)
39 *
40 * x) Lots of work on the 3D "Tile Accelerator" engine.
41 * Recognize commands and turn into OpenGL or similar
42 * commands on the host?
43 * Color clipping.
44 * Wire-frame when running on a host without XGL?
45 *
46 * Multiple lists of various kinds (6?).
47 * Lists growing downwards!
48 * Pixel clip for rendering.
49 * Real Rendering, using OpenGL if possible.
50 * Tile bins... with 6 pointers for each tile (?)
51 * PVR DMA.
52 */
53
54 #include <stdio.h>
55 #include <stdlib.h>
56 #include <string.h>
57
58 #include "cpu.h"
59 #include "device.h"
60 #include "devices.h"
61 #include "float_emul.h"
62 #include "machine.h"
63 #include "memory.h"
64 #include "misc.h"
65 #include "timer.h"
66
67 #include "dreamcast_pvr.h"
68 #include "dreamcast_sysasicvar.h"
69
70
71 #define debug fatal
72
73 #define INTERNAL_FB_ADDR 0x300000000ULL
74 #define PVR_FB_TICK_SHIFT 19
75
76 #define PVR_VBLANK_HZ 60.0
77 #define PVR_MARGIN 16
78
79 struct pvr_data {
80 struct vfb_data *fb;
81 int fb_update_x1;
82 int fb_update_y1;
83 int fb_update_x2;
84 int fb_update_y2;
85
86 struct timer *vblank_timer;
87 int vblank_interrupts_pending;
88
89 /* PVR registers: */
90 uint32_t reg[PVRREG_REGSIZE / sizeof(uint32_t)];
91
92 /* Calculated by pvr_geometry_updated(): */
93 int xsize, ysize;
94 int bytes_per_pixel;
95
96 /* Cached values (from registers): */
97 /* DIWMODE: */
98 int clock_double;
99 int strip_buffer_enabled;
100 int strip_length;
101 int argb8888_threshold;
102 int extend;
103 int pixelmode;
104 int line_double;
105 int display_enabled;
106 /* BRDCOLR: */
107 int border_updated;
108 /* SYNCCONF: */
109 int video_enabled;
110 int broadcast_standard;
111 int interlaced;
112 int h_sync_positive;
113 int v_sync_positive;
114 /* TILEBUF_SIZE: */
115 int tilebuf_xsize;
116 int tilebuf_ysize;
117
118 /* Tile Accelerator Command: */
119 uint32_t ta[64 / sizeof(uint32_t)];
120
121 uint8_t *vram;
122 uint8_t *vram_alt;
123 };
124
125 struct pvr_data_alt {
126 struct pvr_data *d;
127 };
128
129
130 #define REG(x) (d->reg[(x)/sizeof(uint32_t)])
131 #define DEFAULT_WRITE REG(relative_addr) = idata;
132
133
134 /*
135 * pvr_fb_invalidate():
136 */
137 static void pvr_fb_invalidate(struct pvr_data *d, int start, int stop)
138 {
139 d->fb_update_x1 = d->fb_update_y1 = 0;
140 d->fb_update_x2 = d->xsize - 1;
141 d->fb_update_y2 = d->ysize - 1;
142 }
143
144
145 /*
146 * pvr_vblank_timer_tick():
147 *
148 * This function is called PVR_VBLANK_HZ times per real-world second. Its job
149 * is to fake vertical retrace interrupts.
150 */
151 static void pvr_vblank_timer_tick(struct timer *t, void *extra)
152 {
153 struct pvr_data *d = (struct pvr_data *) extra;
154 d->vblank_interrupts_pending ++;
155 }
156
157
158 /*
159 * pvr_geometry_updated():
160 *
161 * This function should be called every time a register is written to which
162 * affects the framebuffer geometry (size, bit-depth, starting position, etc).
163 */
164 static void pvr_geometry_updated(struct pvr_data *d)
165 {
166 /* Make sure to redraw border on geometry changes. */
167 d->border_updated = 1;
168
169 d->xsize = (REG(PVRREG_DIWSIZE) >> DIWSIZE_DPL_SHIFT) & DIWSIZE_MASK;
170 d->ysize = (REG(PVRREG_DIWSIZE) >> DIWSIZE_LPF_SHIFT) & DIWSIZE_MASK;
171
172 /* E.g. 319x479 => 320x480 */
173 d->xsize = (d->xsize + 1) * sizeof(uint32_t);
174 d->ysize ++;
175
176 switch (d->pixelmode) {
177 case 0:
178 case 1: d->bytes_per_pixel = 2; break;
179 case 2: d->bytes_per_pixel = 3; break;
180 case 3: d->bytes_per_pixel = 4; break;
181 }
182
183 d->xsize /= d->bytes_per_pixel;
184
185 if (REG(PVRREG_DIWCONF) & DIWCONF_LR)
186 d->xsize /= 2;
187
188 if (d->line_double)
189 d->ysize /= 2;
190
191 /* Only show geometry debug message if output is enabled: */
192 if (!d->video_enabled || !d->display_enabled)
193 return;
194
195 debug("[ pvr_geometry_updated: %i x %i, ", d->xsize, d->ysize);
196
197 switch (d->pixelmode) {
198 case 0: debug("RGB0555 (16-bit)"); break;
199 case 1: debug("RGB565 (16-bit)"); break;
200 case 2: debug("RGB888 (24-bit)"); break;
201 case 3: debug("RGB0888 (32-bit)"); break;
202 }
203
204 debug(" ]\n");
205 }
206
207
208 /* Ugly quick-hack: */
209 static void line(struct pvr_data *d, int x1, int y1, int x2, int y2)
210 {
211 int fb_base = REG(PVRREG_FB_RENDER_ADDR1);
212 int i;
213 for (i=0; i<256; i++) {
214 int px = (i * x2 + (256-i) * x1) >> 8;
215 int py = (i * y2 + (256-i) * y1) >> 8;
216 if (px > 0 && py > 0 && px < d->xsize && py < d->ysize) {
217 d->vram[fb_base + (px + py * d->xsize)*
218 d->bytes_per_pixel] = 255;
219 d->vram[fb_base + (px + py * d->xsize)*
220 d->bytes_per_pixel + 1] = 255;
221 }
222 }
223 }
224
225
226 /*
227 * pvr_render():
228 *
229 * Render from the Object Buffer to the framebuffer.
230 *
231 * TODO: This function is totally bogus so far, the format of the Object
232 * Buffer is just a quick made-up hack to see if it works at all.
233 */
234 static void pvr_render(struct cpu *cpu, struct pvr_data *d)
235 {
236 int ob_ofs = REG(PVRREG_OB_ADDR);
237 int fb_base = REG(PVRREG_FB_RENDER_ADDR1);
238 int wf_point_nr, texture = 0;
239 int wf_x[4], wf_y[4];
240
241 debug("[ pvr_render: rendering to FB offset 0x%x ]\n", fb_base);
242
243 /* Clear all pixels first: */
244 /* TODO */
245 memset(d->vram + fb_base, 0, d->xsize * d->ysize * d->bytes_per_pixel);
246
247 wf_point_nr = 0;
248
249 for (;;) {
250 uint8_t cmd = d->vram[ob_ofs];
251
252 if (cmd == 0)
253 break;
254 else if (cmd == 1) {
255 int16_t px = d->vram[ob_ofs+2] + d->vram[ob_ofs+3]*256;
256 int16_t py = d->vram[ob_ofs+4] + d->vram[ob_ofs+5]*256;
257
258 wf_x[wf_point_nr] = px;
259 wf_y[wf_point_nr] = py;
260
261 wf_point_nr ++;
262 if (wf_point_nr == 4) {
263 #if 1
264 line(d, wf_x[0], wf_y[0], wf_x[1], wf_y[1]);
265 line(d, wf_x[0], wf_y[0], wf_x[2], wf_y[2]);
266 line(d, wf_x[1], wf_y[1], wf_x[3], wf_y[3]);
267 line(d, wf_x[2], wf_y[2], wf_x[3], wf_y[3]);
268 wf_point_nr = 0;
269 wf_x[0] = wf_x[2]; wf_y[0] = wf_y[2];
270 wf_x[1] = wf_x[3]; wf_y[1] = wf_y[3];
271 #else
272 draw_texture(d, wf_x[0], wf_y[0],
273 wf_x[1], wf_y[1],
274 wf_x[2], wf_y[2],
275 wf_x[3], wf_y[3], texture);
276 #endif
277 }
278
279 } else if (cmd == 2) {
280 wf_point_nr = 0;
281 texture = d->vram[ob_ofs+4] + (d->vram[ob_ofs+5]
282 << 8) + (d->vram[ob_ofs+6] << 16) +
283 (d->vram[ob_ofs+7] << 24);
284 texture <<= 3;
285 texture &= 0x7fffff;
286 printf("TEXTURE = %x\n", texture);
287 } else {
288 fatal("pvr_render: internal error, unknown cmd\n");
289 }
290
291 ob_ofs += sizeof(uint64_t);
292 }
293
294 SYSASIC_TRIGGER_EVENT(SYSASIC_EVENT_RENDERDONE);
295 }
296
297
298 /*
299 * pvr_reset_ta():
300 *
301 * Reset the Tile Accelerator.
302 */
303 static void pvr_reset_ta(struct pvr_data *d)
304 {
305 REG(PVRREG_DIWCONF) = DIWCONF_MAGIC;
306 }
307
308
309 /*
310 * pvr_reset():
311 *
312 * Reset the PVR.
313 */
314 static void pvr_reset(struct pvr_data *d)
315 {
316 /* TODO */
317 }
318
319
320 /*
321 * pvr_ta_init():
322 *
323 * Initialize the Tile Accelerator. This makes the TA ready to receive
324 * commands (via address 0x10000000).
325 */
326 static void pvr_ta_init(struct cpu *cpu, struct pvr_data *d)
327 {
328 REG(PVRREG_TA_OPB_POS) = REG(PVRREG_TA_OPB_START);
329 REG(PVRREG_TA_OB_POS) = REG(PVRREG_TA_OB_START);
330 }
331
332
333 /*
334 * pvr_ta_command():
335 *
336 * Read a command (e.g. parts of a polygon primitive) from d->ta[], and output
337 * "compiled commands" into the Object list and Object Pointer list.
338 */
339 static void pvr_ta_command(struct cpu *cpu, struct pvr_data *d, int list_ofs)
340 {
341 int ob_ofs;
342 int16_t x, y;
343 uint32_t *ta = &d->ta[list_ofs];
344
345 #if 0
346 /* Dump the Tile Accelerator command for debugging: */
347 {
348 int i;
349 fatal("TA cmd:");
350 for (i=0; i<8; i++)
351 fatal(" %08x", (int) ta[i]);
352 fatal("\n");
353 }
354 #endif
355
356 /*
357 * TODO: REWRITE!!!
358 *
359 * THis is just a quick hack to see if I can get out at least
360 * the pixel coordinates.
361 */
362
363 {
364 struct ieee_float_value fx, fy;
365 ieee_interpret_float_value(ta[1], &fx, IEEE_FMT_S);
366 ieee_interpret_float_value(ta[2], &fy, IEEE_FMT_S);
367 x = fx.f; y = fy.f;
368 }
369
370 ob_ofs = REG(PVRREG_TA_OB_POS);
371
372 switch (ta[0] >> 28) {
373 case 0x8:
374 d->vram[ob_ofs + 0] = 2;
375 d->vram[ob_ofs + 4] = ta[3];
376 d->vram[ob_ofs + 5] = ta[3] >> 8;
377 d->vram[ob_ofs + 6] = ta[3] >> 16;
378 d->vram[ob_ofs + 7] = ta[3] >> 24;
379 REG(PVRREG_TA_OB_POS) = ob_ofs + sizeof(uint64_t);
380 break;
381 case 0xe:
382 case 0xf:
383 /* Point. */
384 d->vram[ob_ofs + 0] = 1;
385 d->vram[ob_ofs + 2] = x & 255;
386 d->vram[ob_ofs + 3] = x >> 8;
387 d->vram[ob_ofs + 4] = y & 255;
388 d->vram[ob_ofs + 5] = y >> 8;
389 REG(PVRREG_TA_OB_POS) = ob_ofs + sizeof(uint64_t);
390 break;
391 case 0x0:
392 if (ta[1] == 0) {
393 /* End of list. */
394 uint32_t opb_cfg = REG(PVRREG_TA_OPB_CFG);
395 d->vram[ob_ofs + 0] = 0;
396 REG(PVRREG_TA_OB_POS) = ob_ofs + sizeof(uint64_t);
397 if (opb_cfg & TA_OPB_CFG_OPAQUEPOLY_MASK)
398 SYSASIC_TRIGGER_EVENT(SYSASIC_EVENT_OPAQUEDONE);
399 if (opb_cfg & TA_OPB_CFG_OPAQUEMOD_MASK)
400 SYSASIC_TRIGGER_EVENT(
401 SYSASIC_EVENT_OPAQUEMODDONE);
402 if (opb_cfg & TA_OPB_CFG_TRANSPOLY_MASK)
403 SYSASIC_TRIGGER_EVENT(SYSASIC_EVENT_TRANSDONE);
404 if (opb_cfg & TA_OPB_CFG_TRANSMOD_MASK)
405 SYSASIC_TRIGGER_EVENT(
406 SYSASIC_EVENT_TRANSMODDONE);
407 if (opb_cfg & TA_OPB_CFG_PUNCHTHROUGH_MASK)
408 SYSASIC_TRIGGER_EVENT(SYSASIC_EVENT_PVR_PTDONE);
409 }
410 break;
411 case 2: /* Ignore for now. */
412 case 3: /* Ignore for now. */
413 /* TODO */
414 break;
415 default:fatal("Unimplemented top TA nibble %i\n", ta[0] >> 28);
416 exit(1);
417 }
418 }
419
420
421 DEVICE_ACCESS(pvr_ta)
422 {
423 struct pvr_data *d = extra;
424 uint64_t idata = 0, odata = 0;
425
426 if (writeflag == MEM_WRITE) {
427 idata = memory_readmax64(cpu, data, len);
428
429 /* Write to the tile accelerator command buffer: */
430 d->ta[relative_addr / sizeof(uint32_t)] = idata;
431
432 /* Execute the command, after a complete write: */
433 if (relative_addr == 0x1c)
434 pvr_ta_command(cpu, d, 0);
435 if (relative_addr == 0x3c)
436 pvr_ta_command(cpu, d, 8);
437 } else {
438 odata = d->ta[relative_addr / sizeof(uint32_t)];
439 memory_writemax64(cpu, data, len, odata);
440 }
441
442 return 1;
443 }
444
445
446 DEVICE_ACCESS(pvr)
447 {
448 struct pvr_data *d = extra;
449 uint64_t idata = 0, odata = 0;
450
451 if (writeflag == MEM_WRITE)
452 idata = memory_readmax64(cpu, data, len);
453
454 /* Default read action: Read from reg[]: */
455 if (writeflag == MEM_READ)
456 odata = d->reg[relative_addr / sizeof(uint32_t)];
457
458 /* Fog table access: */
459 if (relative_addr >= PVRREG_FOG_TABLE &&
460 relative_addr < PVRREG_FOG_TABLE + PVR_FOG_TABLE_SIZE) {
461 if (writeflag == MEM_WRITE)
462 DEFAULT_WRITE;
463 goto return_ok;
464 }
465
466 switch (relative_addr) {
467
468 case PVRREG_ID:
469 /* ID for Set 5.xx versions of the Dreamcast, according
470 to http://www.ludd.luth.se/~jlo/dc/powervr-reg.txt: */
471 odata = 0x17fd11db;
472 break;
473
474 case PVRREG_REVISION:
475 /* Revision 1.1, for Dreamcast Set 5.2x. */
476 odata = 0x00000011;
477 break;
478
479 case PVRREG_RESET:
480 if (writeflag == MEM_WRITE) {
481 if (idata != 0) {
482 debug("[ pvr: RESET ");
483 if (idata & PVR_RESET_PVR)
484 pvr_reset(d);
485 if (idata & PVR_RESET_TA)
486 pvr_reset_ta(d);
487 debug("]\n");
488 }
489 idata = 0;
490 DEFAULT_WRITE;
491 }
492 break;
493
494 case PVRREG_STARTRENDER:
495 if (writeflag == MEM_WRITE) {
496 debug("[ pvr: STARTRENDER ]\n");
497 pvr_render(cpu, d);
498 } else {
499 fatal("[ pvr: huh? read from STARTRENDER ]\n");
500 }
501 break;
502
503 case PVRREG_OB_ADDR:
504 if (writeflag == MEM_WRITE) {
505 debug("[ pvr: OB_ADDR set to 0x%08"PRIx32" ]\n",
506 (uint32_t)(idata & PVR_OB_ADDR_MASK));
507 /* if (idata & ~PVR_OB_ADDR_MASK) {
508 fatal("[ pvr: OB_ADDR: Fatal error: Unknown"
509 " bits set: 0x%08"PRIx32" ]\n",
510 (uint32_t)(idata & ~PVR_OB_ADDR_MASK));
511 exit(1);
512 }
513 idata &= PVR_OB_ADDR_MASK;
514 */
515 DEFAULT_WRITE;
516 }
517 break;
518
519 case PVRREG_TILEBUF_ADDR:
520 if (writeflag == MEM_WRITE) {
521 debug("[ pvr: TILEBUF_ADDR set to 0x%08"PRIx32" ]\n",
522 (uint32_t)(idata & PVR_TILEBUF_ADDR_MASK));
523 if (idata & ~PVR_TILEBUF_ADDR_MASK)
524 fatal("[ pvr: TILEBUF_ADDR: WARNING: Unknown"
525 " bits set: 0x%08"PRIx32" ]\n",
526 (uint32_t)(idata & ~PVR_TILEBUF_ADDR_MASK));
527 idata &= PVR_TILEBUF_ADDR_MASK;
528 DEFAULT_WRITE;
529 }
530 break;
531
532 case PVRREG_SPANSORT:
533 if (writeflag == MEM_WRITE) {
534 debug("[ pvr: SPANSORT: ");
535 if (idata & PVR_SPANSORT_SPAN0)
536 debug("SPAN0 ");
537 if (idata & PVR_SPANSORT_SPAN1)
538 debug("SPAN1 ");
539 if (idata & PVR_SPANSORT_TSP_CACHE_ENABLE)
540 debug("TSP_CACHE_ENABLE ");
541 debug("]\n");
542 DEFAULT_WRITE;
543 }
544 break;
545
546 case PVRREG_BRDCOLR:
547 if (writeflag == MEM_WRITE) {
548 debug("[ pvr: BRDCOLR set to 0x%06"PRIx32" ]\n",
549 (int)idata);
550 DEFAULT_WRITE;
551 d->border_updated = 1;
552 }
553 break;
554
555 case PVRREG_DIWMODE:
556 if (writeflag == MEM_WRITE) {
557 d->clock_double = idata & DIWMODE_C_MASK? 1:0;
558 d->strip_buffer_enabled = idata & DIWMODE_SE_MASK? 1:0;
559 d->strip_length = (idata & DIWMODE_SL_MASK)
560 >> DIWMODE_SL_SHIFT;
561 d->argb8888_threshold = (idata & DIWMODE_TH_MASK)
562 >> DIWMODE_TH_SHIFT;
563 d->extend = (idata & DIWMODE_EX_MASK)
564 >> DIWMODE_EX_SHIFT;
565 d->pixelmode = (idata & DIWMODE_COL_MASK)
566 >> DIWMODE_COL_SHIFT;
567 d->line_double = idata & DIWMODE_SD_MASK? 1:0;
568 d->display_enabled = idata & DIWMODE_DE_MASK? 1:0;
569
570 debug("[ pvr: DIWMODE set to: ");
571 debug("clock_double=%i, ", d->clock_double);
572 debug("strip_buffer_enabled=%i, ",
573 d->strip_buffer_enabled);
574 debug("strip_length=%i, ", d->strip_length);
575 debug("argb8888_threshold=%i, ", d->argb8888_threshold);
576 debug("extend=0x%x, ", d->extend);
577 debug("pixelmode=");
578 switch (d->pixelmode) {
579 case 0: debug("RGB0555 (16-bit)"); break;
580 case 1: debug("RGB565 (16-bit)"); break;
581 case 2: debug("RGB888 (24-bit)"); break;
582 case 3: debug("RGB0888 (32-bit)"); break;
583 }
584 debug(", line_double=%i, ", d->line_double);
585 debug("display_enabled=%i", d->display_enabled);
586 debug(" ]\n");
587
588 DEFAULT_WRITE;
589 pvr_geometry_updated(d);
590 pvr_fb_invalidate(d, -1, -1);
591 }
592 break;
593
594 case PVRREG_DIWSIZE:
595 if (writeflag == MEM_WRITE) {
596 debug("[ pvr: DIWSIZE set to modulo=%i, "
597 "width=%i, height=%i ]\n", (int)
598 ((idata >> DIWSIZE_MODULO_SHIFT) & DIWSIZE_MASK),
599 (int)((idata >> DIWSIZE_DPL_SHIFT) & DIWSIZE_MASK),
600 (int)((idata >> DIWSIZE_LPF_SHIFT) & DIWSIZE_MASK));
601 DEFAULT_WRITE;
602 pvr_geometry_updated(d);
603 pvr_fb_invalidate(d, -1, -1);
604 }
605 break;
606
607 case PVRREG_FB_RENDER_ADDR1:
608 if (writeflag == MEM_WRITE) {
609 debug("[ pvr: FB_RENDER_ADDR1 set to 0x%08"PRIx32
610 " ]\n", (int) idata);
611 DEFAULT_WRITE;
612 }
613 break;
614
615 case PVRREG_FB_RENDER_ADDR2:
616 if (writeflag == MEM_WRITE) {
617 debug("[ pvr: FB_RENDER_ADDR2 set to 0x%08"PRIx32
618 " ]\n", (int) idata);
619 DEFAULT_WRITE;
620 }
621 break;
622
623 case PVRREG_VRAM_CFG1:
624 if (writeflag == MEM_WRITE) {
625 debug("[ pvr: VRAM_CFG1 set to 0x%08"PRIx32,
626 (int) idata);
627 if (idata != VRAM_CFG1_GOOD_REFRESH_VALUE)
628 fatal("{ VRAM_CFG1 = 0x%08"PRIx32" is not "
629 "yet implemented! }", (int) idata);
630 debug(" ]\n");
631 DEFAULT_WRITE;
632 }
633 break;
634
635 case PVRREG_VRAM_CFG2:
636 if (writeflag == MEM_WRITE) {
637 debug("[ pvr: VRAM_CFG2 set to 0x%08"PRIx32,
638 (int) idata);
639 if (idata != VRAM_CFG2_UNKNOWN_MAGIC)
640 fatal("{ VRAM_CFG2 = 0x%08"PRIx32" is not "
641 "yet implemented! }", (int) idata);
642 debug(" ]\n");
643 DEFAULT_WRITE;
644 }
645 break;
646
647 case PVRREG_VRAM_CFG3:
648 if (writeflag == MEM_WRITE) {
649 debug("[ pvr: VRAM_CFG3 set to 0x%08"PRIx32,
650 (int) idata);
651 if (idata != VRAM_CFG3_UNKNOWN_MAGIC)
652 fatal("{ VRAM_CFG3 = 0x%08"PRIx32" is not "
653 "yet implemented! }", (int) idata);
654 debug(" ]\n");
655 DEFAULT_WRITE;
656 }
657 break;
658
659 case PVRREG_FOG_TABLE_COL:
660 if (writeflag == MEM_WRITE) {
661 debug("[ pvr: FOG_TABLE_COL set to 0x%08"PRIx32" ]\n",
662 (int) idata);
663 DEFAULT_WRITE;
664 }
665 break;
666
667 case PVRREG_FOG_VERTEX_COL:
668 if (writeflag == MEM_WRITE) {
669 debug("[ pvr: FOG_VERTEX_COL set to 0x%08"PRIx32" ]\n",
670 (int) idata);
671 DEFAULT_WRITE;
672 }
673 break;
674
675 case PVRREG_FB_RENDER_MODULO:
676 if (writeflag == MEM_WRITE) {
677 debug("[ pvr: PVRREG_FB_RENDER_MODULO set to %i ]\n",
678 (int) idata);
679 /* TODO */
680 DEFAULT_WRITE;
681 }
682 break;
683
684 case PVRREG_DIWADDRL:
685 if (writeflag == MEM_WRITE) {
686 debug("[ pvr: DIWADDRL set to 0x%08"PRIx32" ]\n",
687 (int) idata);
688 pvr_fb_invalidate(d, -1, -1);
689 DEFAULT_WRITE;
690 }
691 break;
692
693 case PVRREG_DIWADDRS:
694 if (writeflag == MEM_WRITE) {
695 debug("[ pvr: DIWADDRS set to 0x%08"PRIx32" ]\n",
696 (int) idata);
697 pvr_fb_invalidate(d, -1, -1);
698 DEFAULT_WRITE;
699 }
700 break;
701
702 case PVRREG_RASEVTPOS:
703 if (writeflag == MEM_WRITE) {
704 debug("[ pvr: RASEVTPOS pos1=%i pos2=%i ]\n",
705 (int)((idata & RASEVTPOS_POS1_MASK)
706 >> RASEVTPOS_POS1_SHIFT),
707 (int)(idata & RASEVTPOS_POS2_MASK));
708 DEFAULT_WRITE;
709 }
710 break;
711
712 case PVRREG_SYNCCONF:
713 if (writeflag == MEM_WRITE) {
714 d->video_enabled = idata & SYNCCONF_VO_MASK? 1:0;
715 d->broadcast_standard = (idata & SYNCCONF_BC_MASK)
716 >> SYNCCONF_BC_SHIFT;
717 d->interlaced = idata & SYNCCONF_I_MASK? 1:0;
718 d->h_sync_positive = idata & SYNCCONF_HP_MASK? 1:0;
719 d->v_sync_positive = idata & SYNCCONF_VP_MASK? 1:0;
720
721 debug("[ pvr: SYNCCONF set to: ");
722 debug("video_enabled=%i, ", d->video_enabled);
723 switch (d->broadcast_standard) {
724 case SYNCCONF_BC_VGA: debug("VGA"); break;
725 case SYNCCONF_BC_NTSC: debug("NTSC"); break;
726 case SYNCCONF_BC_PAL: debug("PAL"); break;
727 default: debug("*UNKNOWN*"); break;
728 }
729 debug(", interlaced=%i, ", d->interlaced);
730 debug("hsync=%i, ", d->h_sync_positive);
731 debug("vsync=%i ]\n", d->v_sync_positive);
732
733 DEFAULT_WRITE;
734 pvr_geometry_updated(d);
735 pvr_fb_invalidate(d, -1, -1);
736 }
737 break;
738
739 case PVRREG_BRDHORZ:
740 if (writeflag == MEM_WRITE) {
741 debug("[ pvr: BRDHORZ start=%i stop=%i ]\n",
742 (int)((idata & BRDHORZ_START_MASK)
743 >> BRDHORZ_START_SHIFT),
744 (int)(idata & BRDHORZ_STOP_MASK));
745 DEFAULT_WRITE;
746 }
747 break;
748
749 case PVRREG_SYNCSIZE:
750 if (writeflag == MEM_WRITE) {
751 debug("[ pvr: SYNCSIZE v=%i h=%i ]\n",
752 (int)((idata & SYNCSIZE_V_MASK)
753 >> SYNCSIZE_V_SHIFT),
754 (int)(idata & SYNCSIZE_H_MASK));
755 DEFAULT_WRITE;
756 }
757 break;
758
759 case PVRREG_BRDVERT:
760 if (writeflag == MEM_WRITE) {
761 debug("[ pvr: BRDVERT start=%i stop=%i ]\n",
762 (int)((idata & BRDVERT_START_MASK)
763 >> BRDVERT_START_SHIFT),
764 (int)(idata & BRDVERT_STOP_MASK));
765 DEFAULT_WRITE;
766 }
767 break;
768
769 case PVRREG_DIWCONF:
770 if (writeflag == MEM_WRITE) {
771 if ((idata & DIWCONF_MAGIC_MASK) !=
772 DIWCONF_MAGIC && (idata & DIWCONF_MAGIC_MASK)
773 != 0) {
774 fatal("PVRREG_DIWCONF magic not set to "
775 "Magic value. 0x%08x\n", (int)idata);
776 exit(1);
777 }
778 if (idata & DIWCONF_BLANK)
779 debug("[ pvr: PVRREG_DIWCONF: BLANK: TODO ]\n");
780
781 DEFAULT_WRITE;
782 pvr_geometry_updated(d);
783 }
784 break;
785
786 case PVRREG_DIWHSTRT:
787 if (writeflag == MEM_WRITE) {
788 debug("[ pvr: DIWHSTRT hpos=%i ]\n",
789 (int)(idata & DIWVSTRT_HPOS_MASK));
790 DEFAULT_WRITE;
791 }
792 break;
793
794 case PVRREG_DIWVSTRT:
795 if (writeflag == MEM_WRITE) {
796 debug("[ pvr: DIWVSTRT v2=%i v1=%i ]\n",
797 (int)((idata & DIWVSTRT_V2_MASK)
798 >> DIWVSTRT_V2_SHIFT),
799 (int)(idata & DIWVSTRT_V1_MASK));
800 DEFAULT_WRITE;
801 }
802 break;
803
804 case PVRREG_SYNC_STAT:
805 /* TODO. Ugly hack, but it works: */
806 odata = random();
807 break;
808
809 case PVRREG_TA_OPB_START:
810 if (writeflag == MEM_WRITE) {
811 if (idata & ~TA_OPB_START_MASK) {
812 fatal("[ pvr: UNEXPECTED bits in "
813 "TA_OPB_START: 0x%08x ]\n", (int)idata);
814 exit(1);
815 }
816 idata &= TA_OPB_START_MASK;
817 debug("[ pvr: TA_OPB_START set to 0x%x ]\n",
818 (int) idata);
819 DEFAULT_WRITE;
820 }
821 break;
822
823 case PVRREG_TA_OB_START:
824 if (writeflag == MEM_WRITE) {
825 if (idata & ~TA_OB_START_MASK) {
826 fatal("[ pvr: UNEXPECTED bits in "
827 "TA_OB_START: 0x%08x ]\n", (int)idata);
828 exit(1);
829 }
830 idata &= TA_OB_START_MASK;
831 debug("[ pvr: TA_OB_START set to 0x%x ]\n",
832 (int) idata);
833 DEFAULT_WRITE;
834 }
835 break;
836
837 case PVRREG_TA_OPB_END:
838 if (writeflag == MEM_WRITE) {
839 idata &= TA_OPB_END_MASK;
840 debug("[ pvr: TA_OPB_END set to 0x%x ]\n",
841 (int) idata);
842 DEFAULT_WRITE;
843 }
844 break;
845
846 case PVRREG_TA_OB_END:
847 if (writeflag == MEM_WRITE) {
848 idata &= TA_OB_END_MASK;
849 debug("[ pvr: TA_OB_END set to 0x%x ]\n",
850 (int) idata);
851 DEFAULT_WRITE;
852 }
853 break;
854
855 case PVRREG_TA_OPB_POS:
856 if (writeflag == MEM_WRITE) {
857 idata &= TA_OPB_POS_MASK;
858 debug("[ pvr: TA_OPB_POS set to 0x%x ]\n",
859 (int) idata);
860 DEFAULT_WRITE;
861 }
862 break;
863
864 case PVRREG_TA_OB_POS:
865 if (writeflag == MEM_WRITE) {
866 idata &= TA_OB_POS_MASK;
867 debug("[ pvr: TA_OB_POS set to 0x%x ]\n",
868 (int) idata);
869 DEFAULT_WRITE;
870 }
871 break;
872
873 case PVRREG_TILEBUF_SIZE:
874 if (writeflag == MEM_WRITE) {
875 d->tilebuf_ysize = (idata & TILEBUF_SIZE_HEIGHT_MASK)
876 >> TILEBUF_SIZE_HEIGHT_SHIFT;
877 d->tilebuf_xsize = idata & TILEBUF_SIZE_WIDTH_MASK;
878 d->tilebuf_xsize ++; d->tilebuf_ysize ++;
879 debug("[ pvr: TILEBUF_SIZE set to %i x %i ]\n",
880 d->tilebuf_xsize, d->tilebuf_ysize);
881 DEFAULT_WRITE;
882 }
883 break;
884
885 case PVRREG_TA_INIT:
886 if (writeflag == MEM_WRITE) {
887 debug("[ pvr: TA_INIT ]\n");
888
889 if (idata & PVR_TA_INIT)
890 pvr_ta_init(cpu, d);
891
892 if (idata != PVR_TA_INIT && idata != 0)
893 fatal("{ TA_INIT = 0x%08"PRIx32" is not "
894 "yet implemented! }", (int) idata);
895
896 /* Always reset to 0. */
897 idata = 0;
898 DEFAULT_WRITE;
899 }
900 break;
901
902 default:if (writeflag == MEM_READ) {
903 fatal("[ pvr: read from UNIMPLEMENTED addr 0x%x ]\n",
904 (int)relative_addr);
905 } else {
906 fatal("[ pvr: write to UNIMPLEMENTED addr 0x%x: 0x%x"
907 " ]\n", (int)relative_addr, (int)idata);
908 DEFAULT_WRITE;
909 }
910 }
911
912 return_ok:
913 if (writeflag == MEM_READ)
914 memory_writemax64(cpu, data, len, odata);
915
916 return 1;
917 }
918
919
920 static void extend_update_region(struct pvr_data *d, uint64_t low,
921 uint64_t high)
922 {
923 int vram_ofs = REG(PVRREG_DIWADDRL);
924 int bytes_per_line = d->xsize * d->bytes_per_pixel;
925
926 low -= vram_ofs;
927 high -= vram_ofs;
928
929 /* Access inside visible part of VRAM? */
930 if ((int64_t)high >= 0 && (int64_t)low <
931 bytes_per_line * d->ysize) {
932 int new_y1, new_y2;
933
934 d->fb_update_x1 = 0;
935 d->fb_update_x2 = d->xsize - 1;
936
937 /* Calculate which line the low and high addresses
938 correspond to: */
939 new_y1 = low / bytes_per_line;
940 new_y2 = high / bytes_per_line + 1;
941
942 if (d->fb_update_y1 < 0 || new_y1 < d->fb_update_y1)
943 d->fb_update_y1 = new_y1;
944 if (d->fb_update_y2 < 0 || new_y2 > d->fb_update_y2)
945 d->fb_update_y2 = new_y2;
946
947 if (d->fb_update_y2 >= d->ysize)
948 d->fb_update_y2 = d->ysize - 1;
949 }
950 }
951
952
953 DEVICE_TICK(pvr_fb)
954 {
955 struct pvr_data *d = extra;
956 uint64_t high, low = (uint64_t)(int64_t) -1;
957 int vram_ofs = REG(PVRREG_DIWADDRL), pixels_to_copy;
958 int y, bytes_per_line = d->xsize * d->bytes_per_pixel;
959 int fb_ofs, p;
960 uint8_t *fb = (uint8_t *) d->fb->framebuffer;
961 uint8_t *vram = (uint8_t *) d->vram;
962
963
964 /*
965 * Vertical retrace interrupts:
966 *
967 * TODO: Maybe it would be even more realistic to have the timer run
968 * at, say, 60*4 = 240 Hz, and have the following events:
969 *
970 * (tick & 3) == 0 SYSASIC_EVENT_VBLINT
971 * (tick & 3) == 1 SYSASIC_EVENT_PVR_SCANINT1
972 * (tick & 3) == 2 nothing
973 * (tick & 3) == 3 SYSASIC_EVENT_PVR_SCANINT2
974 */
975
976 if (d->vblank_interrupts_pending > 0) {
977 SYSASIC_TRIGGER_EVENT(SYSASIC_EVENT_VBLINT);
978 SYSASIC_TRIGGER_EVENT(SYSASIC_EVENT_PVR_SCANINT1);
979 SYSASIC_TRIGGER_EVENT(SYSASIC_EVENT_PVR_SCANINT2);
980
981 /* TODO: For now, I don't care about missed interrupts: */
982 d->vblank_interrupts_pending = 0;
983 }
984
985
986 /*
987 * Framebuffer update:
988 */
989
990 /* Border changed? */
991 if (d->border_updated) {
992 /* Fill border with border color: */
993 int rgb = REG(PVRREG_BRDCOLR), addr = 0;
994 int x, y, b = rgb & 0xff, g = (rgb >> 8) & 0xff, r = rgb >> 16;
995 int skiplen = (d->fb->xsize-2*PVR_MARGIN) * d->fb->bit_depth/8;
996
997 for (y=0; y<d->fb->ysize; y++) {
998 int xskip = y < PVR_MARGIN || y >=
999 d->fb->ysize - PVR_MARGIN? -1 : PVR_MARGIN;
1000 for (x=0; x<d->fb->xsize; x++) {
1001 if (x == xskip) {
1002 x = d->fb->xsize - PVR_MARGIN;
1003 addr += skiplen;
1004 }
1005 fb[addr] = r;
1006 fb[addr+1] = g;
1007 fb[addr+2] = b;
1008 addr += 3;
1009 }
1010 }
1011
1012 /* Full redraw of the framebuffer: */
1013 d->fb->update_x1 = 0; d->fb->update_x2 = d->fb->xsize - 1;
1014 d->fb->update_y1 = 0; d->fb->update_y2 = d->fb->ysize - 1;
1015 }
1016
1017 memory_device_dyntrans_access(cpu, cpu->mem, extra, &low, &high);
1018 if ((int64_t)low != -1)
1019 extend_update_region(d, low, high);
1020
1021 if (d->fb_update_x1 == -1)
1022 return;
1023
1024 /* Copy (part of) the VRAM to the framebuffer: */
1025 if (d->fb_update_x2 >= d->xsize)
1026 d->fb_update_x2 = d->xsize - 1;
1027 if (d->fb_update_y2 >= d->ysize)
1028 d->fb_update_y2 = d->ysize - 1;
1029
1030 vram_ofs += d->fb_update_y1 * bytes_per_line;
1031 vram_ofs += d->fb_update_x1 * d->bytes_per_pixel;
1032 pixels_to_copy = (d->fb_update_x2 - d->fb_update_x1 + 1);
1033 fb_ofs = (d->fb_update_y1 + PVR_MARGIN) * d->fb->bytes_per_line;
1034 fb_ofs += (d->fb_update_x1 + PVR_MARGIN) * d->fb->bit_depth / 8;
1035
1036 /* Copy the actual pixels: (Four manually inlined, for speed.) */
1037
1038 switch (d->pixelmode) {
1039 case 0: /* RGB0555 (16-bit) */
1040 for (y=d->fb_update_y1; y<=d->fb_update_y2; y++) {
1041 int fo = fb_ofs, vo = vram_ofs;
1042 for (p=0; p<pixels_to_copy; p++) {
1043 /* 0rrrrrgg(high) gggbbbbb(low) */
1044 fb[fo] = (vram[vo+1] << 1) & 0xf8;
1045 fb[fo+1] = ((vram[vo] >> 2) & 0x38) +
1046 (vram[vo+1] << 6);
1047 fb[fo+2] = (vram[vo] & 0x1f) << 3;
1048 fo += 3; vo += 2;
1049 }
1050 vram_ofs += bytes_per_line;
1051 fb_ofs += d->fb->bytes_per_line;
1052 }
1053 break;
1054
1055 case 1: /* RGB565 (16-bit) */
1056 for (y=d->fb_update_y1; y<=d->fb_update_y2; y++) {
1057 int fo = fb_ofs, vo = vram_ofs;
1058 for (p=0; p<pixels_to_copy; p++) {
1059 /* rrrrrggg(high) gggbbbbb(low) */
1060 fb[fo] = vram[vo+1] & 0xf8;
1061 fb[fo+1] = ((vram[vo] >> 3) & 0x1c) +
1062 (vram[vo+1] << 5);
1063 fb[fo+2] = (vram[vo] & 0x1f) << 3;
1064 fo += 3; vo += 2;
1065 }
1066 vram_ofs += bytes_per_line;
1067 fb_ofs += d->fb->bytes_per_line;
1068 }
1069 break;
1070
1071 case 2: /* RGB888 (24-bit) */
1072 for (y=d->fb_update_y1; y<=d->fb_update_y2; y++) {
1073 /* TODO: Reverse colors, like in the 32-bit case? */
1074 memcpy(fb+fb_ofs, vram+vram_ofs, 3*pixels_to_copy);
1075 vram_ofs += bytes_per_line;
1076 fb_ofs += d->fb->bytes_per_line;
1077 }
1078 break;
1079
1080 case 3: /* RGB0888 (32-bit) */
1081 for (y=d->fb_update_y1; y<=d->fb_update_y2; y++) {
1082 int fo = fb_ofs, vo = vram_ofs;
1083 for (p=0; p<pixels_to_copy; p++) {
1084 fb[fo] = vram[vo+2];
1085 fb[fo+1] = vram[vo+1];
1086 fb[fo+2] = vram[vo+0];
1087 fo += 3; vo += 4;
1088 }
1089 vram_ofs += bytes_per_line;
1090 fb_ofs += d->fb->bytes_per_line;
1091 }
1092 break;
1093 }
1094
1095 /*
1096 * Extend the real framebuffer to encompass the area
1097 * just written to:
1098 */
1099
1100 /* Offset to take the margin into account first... */
1101 d->fb_update_x1 += PVR_MARGIN; d->fb_update_y1 += PVR_MARGIN;
1102 d->fb_update_x2 += PVR_MARGIN; d->fb_update_y2 += PVR_MARGIN;
1103
1104 if (d->fb_update_x1 < d->fb->update_x1 || d->fb->update_x1 < 0)
1105 d->fb->update_x1 = d->fb_update_x1;
1106 if (d->fb_update_x2 > d->fb->update_x2 || d->fb->update_x2 < 0)
1107 d->fb->update_x2 = d->fb_update_x2;
1108 if (d->fb_update_y1 < d->fb->update_y1 || d->fb->update_y1 < 0)
1109 d->fb->update_y1 = d->fb_update_y1;
1110 if (d->fb_update_y2 > d->fb->update_y2 || d->fb->update_y2 < 0)
1111 d->fb->update_y2 = d->fb_update_y2;
1112
1113 /* Clear the PVR's update region: */
1114 d->fb_update_x1 = d->fb_update_x2 =
1115 d->fb_update_y1 = d->fb_update_y2 = -1;
1116 }
1117
1118
1119 DEVICE_ACCESS(pvr_vram_alt)
1120 {
1121 struct pvr_data_alt *d_alt = extra;
1122 struct pvr_data *d = d_alt->d;
1123 size_t i;
1124
1125 if (writeflag == MEM_READ) {
1126 /* Copy from real vram: */
1127 for (i=0; i<len; i++) {
1128 int addr = relative_addr + i;
1129 addr = ((addr & 4) << 20) | (addr & 3)
1130 | ((addr & 0x7ffff8) >> 1);
1131 data[i] = d->vram[addr];
1132 }
1133 return 1;
1134 }
1135
1136 /*
1137 * Convert writes to alternative VRAM, into normal writes:
1138 */
1139
1140 for (i=0; i<len; i++) {
1141 int addr = relative_addr + i;
1142 addr = ((addr & 4) << 20) | (addr & 3)
1143 | ((addr & 0x7ffff8) >> 1);
1144 d->vram[addr] = data[i];
1145 }
1146
1147 return 1;
1148 }
1149
1150
1151 DEVICE_ACCESS(pvr_vram)
1152 {
1153 struct pvr_data *d = extra;
1154
1155 if (writeflag == MEM_READ) {
1156 memcpy(data, d->vram + relative_addr, len);
1157 return 1;
1158 }
1159
1160 /*
1161 * Write to VRAM:
1162 *
1163 * Calculate which part of the framebuffer this write corresponds to,
1164 * if any, and increase the update region to encompass the written
1165 * memory range.
1166 */
1167
1168 memcpy(d->vram + relative_addr, data, len);
1169 extend_update_region(d, relative_addr, relative_addr + len - 1);
1170
1171 return 1;
1172 }
1173
1174
1175 DEVINIT(pvr)
1176 {
1177 struct machine *machine = devinit->machine;
1178 struct pvr_data *d;
1179 struct pvr_data_alt *d_alt;
1180
1181 CHECK_ALLOCATION(d = malloc(sizeof(struct pvr_data)));
1182 memset(d, 0, sizeof(struct pvr_data));
1183
1184 CHECK_ALLOCATION(d_alt = malloc(sizeof(struct pvr_data_alt)));
1185 memset(d_alt, 0, sizeof(struct pvr_data_alt));
1186
1187 d_alt->d = d;
1188
1189 memory_device_register(machine->memory, devinit->name,
1190 PVRREG_REGSTART, PVRREG_REGSIZE, dev_pvr_access, d,
1191 DM_DEFAULT, NULL);
1192
1193 /* 8 MB video RAM: */
1194 d->vram = zeroed_alloc(8 * 1048576);
1195 memory_device_register(machine->memory, "pvr_vram", 0x05000000,
1196 8 * 1048576, dev_pvr_vram_access, (void *)d,
1197 DM_DYNTRANS_OK | DM_DYNTRANS_WRITE_OK
1198 | DM_READS_HAVE_NO_SIDE_EFFECTS, d->vram);
1199
1200 /* 8 MB video RAM, when accessed at 0xa4000000: */
1201 d->vram_alt = zeroed_alloc(8 * 1048576);
1202 memory_device_register(machine->memory, "pvr_alt_vram", 0x04000000,
1203 8 * 1048576, dev_pvr_vram_alt_access, (void *)d_alt,
1204 DM_DEFAULT, NULL);
1205
1206 memory_device_register(machine->memory, "pvr_ta",
1207 0x10000000, sizeof(d->ta), dev_pvr_ta_access, d, DM_DEFAULT, NULL);
1208
1209 d->xsize = 640;
1210 d->ysize = 480;
1211 d->pixelmode = 1; /* RGB565 */
1212 d->bytes_per_pixel = 2;
1213
1214 d->fb = dev_fb_init(machine, machine->memory, INTERNAL_FB_ADDR,
1215 VFB_GENERIC, d->xsize + PVR_MARGIN*2, d->ysize + PVR_MARGIN*2,
1216 d->xsize + PVR_MARGIN*2, d->ysize + PVR_MARGIN*2,
1217 24, "Dreamcast PVR");
1218
1219 d->vblank_timer = timer_add(PVR_VBLANK_HZ, pvr_vblank_timer_tick, d);
1220
1221 pvr_reset(d);
1222 pvr_reset_ta(d);
1223
1224 machine_add_tickfunction(machine, dev_pvr_fb_tick, d,
1225 PVR_FB_TICK_SHIFT);
1226
1227 return 1;
1228 }
1229

  ViewVC Help
Powered by ViewVC 1.1.26