/[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

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

Parent Directory Parent Directory | Revision Log Revision Log


Revision 42 - (hide annotations)
Mon Oct 8 16:22:32 2007 UTC (16 years, 7 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 dpavlin 32 /*
2 dpavlin 34 * Copyright (C) 2006-2007 Anders Gavare. All rights reserved.
3 dpavlin 32 *
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 dpavlin 42 * $Id: dev_pvr.c,v 1.24 2007/06/15 19:57:33 debug Exp $
29 dpavlin 32 *
30 dpavlin 42 * 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 dpavlin 32 * 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 dpavlin 34 #define PVR_MARGIN 16
78 dpavlin 32
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 dpavlin 34 /* BRDCOLR: */
107     int border_updated;
108 dpavlin 32 /* 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 dpavlin 34 /* Make sure to redraw border on geometry changes. */
167     d->border_updated = 1;
168    
169 dpavlin 32 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 dpavlin 42 struct pvr_data *d = extra;
424 dpavlin 32 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 dpavlin 42 struct pvr_data *d = extra;
449 dpavlin 32 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 dpavlin 34 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 dpavlin 32 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 dpavlin 34 d->border_updated = 1;
552 dpavlin 32 }
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 dpavlin 34 /* 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 dpavlin 32 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 dpavlin 34 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 dpavlin 32
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 dpavlin 34 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 dpavlin 32
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 dpavlin 34 /* 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 dpavlin 32 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 dpavlin 40 size_t i;
1124 dpavlin 32
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 dpavlin 42 struct pvr_data *d;
1179     struct pvr_data_alt *d_alt;
1180    
1181     CHECK_ALLOCATION(d = malloc(sizeof(struct pvr_data)));
1182 dpavlin 32 memset(d, 0, sizeof(struct pvr_data));
1183 dpavlin 42
1184     CHECK_ALLOCATION(d_alt = malloc(sizeof(struct pvr_data_alt)));
1185 dpavlin 32 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 dpavlin 34 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 dpavlin 32
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 dpavlin 42 PVR_FB_TICK_SHIFT);
1226 dpavlin 32
1227     return 1;
1228     }
1229    

  ViewVC Help
Powered by ViewVC 1.1.26