Search
lxdream.org :: lxdream/src/pvr2/pvr2.c
lxdream 0.9.1
released Jun 29
Download Now
filename src/pvr2/pvr2.c
changeset 261:93fdb2a70e18
prev218:cbad5a3f8387
next265:5daf59b7f31b
author nkeynes
date Wed Jan 03 09:01:51 2007 +0000 (13 years ago)
permissions -rw-r--r--
last change Implement the main CRTC registers, along with the sync status register.
Timings are now pretty close to the real thing.
view annotate diff log raw
     1 /**
     2  * $Id: pvr2.c,v 1.34 2007-01-03 09:01:51 nkeynes Exp $
     3  *
     4  * PVR2 (Video) Core module implementation and MMIO registers.
     5  *
     6  * Copyright (c) 2005 Nathan Keynes.
     7  *
     8  * This program is free software; you can redistribute it and/or modify
     9  * it under the terms of the GNU General Public License as published by
    10  * the Free Software Foundation; either version 2 of the License, or
    11  * (at your option) any later version.
    12  *
    13  * This program is distributed in the hope that it will be useful,
    14  * but WITHOUT ANY WARRANTY; without even the implied warranty of
    15  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    16  * GNU General Public License for more details.
    17  */
    18 #define MODULE pvr2_module
    20 #include "dream.h"
    21 #include "display.h"
    22 #include "mem.h"
    23 #include "asic.h"
    24 #include "clock.h"
    25 #include "pvr2/pvr2.h"
    26 #include "sh4/sh4core.h"
    27 #define MMIO_IMPL
    28 #include "pvr2/pvr2mmio.h"
    30 char *video_base;
    32 static void pvr2_init( void );
    33 static void pvr2_reset( void );
    34 static uint32_t pvr2_run_slice( uint32_t );
    35 static void pvr2_save_state( FILE *f );
    36 static int pvr2_load_state( FILE *f );
    38 void pvr2_display_frame( void );
    40 int colour_format_bytes[] = { 2, 2, 2, 1, 3, 4, 1, 1 };
    42 struct dreamcast_module pvr2_module = { "PVR2", pvr2_init, pvr2_reset, NULL, 
    43 					pvr2_run_slice, NULL,
    44 					pvr2_save_state, pvr2_load_state };
    47 display_driver_t display_driver = NULL;
    49 struct video_timing {
    50     int fields_per_second;
    51     int total_lines;
    52     int retrace_lines;
    53     int line_time_ns;
    54 };
    56 struct video_timing pal_timing = { 50, 625, 65, 31945 };
    57 struct video_timing ntsc_timing= { 60, 525, 65, 31746 };
    59 struct pvr2_state {
    60     uint32_t frame_count;
    61     uint32_t line_count;
    62     uint32_t line_remainder;
    63     uint32_t irq_vpos1;
    64     uint32_t irq_vpos2;
    65     uint32_t odd_even_field; /* 1 = odd, 0 = even */
    66     gboolean retrace;
    68     /* timing */
    69     uint32_t dot_clock;
    70     uint32_t total_lines;
    71     uint32_t line_size;
    72     uint32_t line_time_ns;
    73     uint32_t vsync_lines;
    74     uint32_t hsync_width_ns;
    75     uint32_t front_porch_ns;
    76     uint32_t back_porch_ns;
    77     gboolean interlaced;
    78     struct video_timing timing;
    79 } pvr2_state;
    81 struct video_buffer video_buffer[2];
    82 int video_buffer_idx = 0;
    84 static void pvr2_init( void )
    85 {
    86     register_io_region( &mmio_region_PVR2 );
    87     register_io_region( &mmio_region_PVR2PAL );
    88     register_io_region( &mmio_region_PVR2TA );
    89     video_base = mem_get_region_by_name( MEM_REGION_VIDEO );
    90     texcache_init();
    91     pvr2_state.dot_clock = 27069;
    92     pvr2_state.total_lines = pal_timing.total_lines;
    93     pvr2_state.line_time_ns = pal_timing.line_time_ns;
    94     pvr2_state.front_porch_ns = 12000;
    95     pvr2_state.back_porch_ns = 4000;
    96     pvr2_state.hsync_width_ns = 4000;
    97     pvr2_state.vsync_lines = 5;
    98     pvr2_reset();
    99     pvr2_ta_reset();
   100 }
   102 static void pvr2_reset( void )
   103 {
   104     pvr2_state.line_count = 0;
   105     pvr2_state.line_remainder = 0;
   106     pvr2_state.irq_vpos1 = 0;
   107     pvr2_state.irq_vpos2 = 0;
   108     pvr2_state.retrace = FALSE;
   109     pvr2_state.timing = ntsc_timing;
   110     video_buffer_idx = 0;
   112     pvr2_ta_init();
   113     pvr2_render_init();
   114     texcache_flush();
   115 }
   117 static void pvr2_save_state( FILE *f )
   118 {
   119     fwrite( &pvr2_state, sizeof(pvr2_state), 1, f );
   120     pvr2_ta_save_state( f );
   121 }
   123 static int pvr2_load_state( FILE *f )
   124 {
   125     if( fread( &pvr2_state, sizeof(pvr2_state), 1, f ) != 1 )
   126 	return 1;
   127     return pvr2_ta_load_state(f);
   128 }
   130 static uint32_t pvr2_run_slice( uint32_t nanosecs ) 
   131 {
   132     pvr2_state.line_remainder += nanosecs;
   133     while( pvr2_state.line_remainder >= pvr2_state.line_time_ns ) {
   134 	pvr2_state.line_remainder -= pvr2_state.line_time_ns;
   136 	pvr2_state.line_count++;
   137 	if( pvr2_state.line_count == pvr2_state.total_lines ) {
   138 	    asic_event( EVENT_RETRACE );
   139 	    pvr2_state.line_count = 0;
   140 	    pvr2_state.retrace = TRUE;
   141 	}
   143 	if( pvr2_state.line_count == pvr2_state.irq_vpos1 ) {
   144 	    asic_event( EVENT_SCANLINE1 );
   145 	} 
   146 	if( pvr2_state.line_count == pvr2_state.irq_vpos2 ) {
   147 	    asic_event( EVENT_SCANLINE2 );
   148 	}
   150 	if( pvr2_state.line_count == pvr2_state.timing.retrace_lines ) {
   151 	    if( pvr2_state.retrace ) {
   152 		pvr2_display_frame();
   153 		pvr2_state.retrace = FALSE;
   154 	    }
   155 	}
   156     }
   157     return nanosecs;
   158 }
   160 int pvr2_get_frame_count() 
   161 {
   162     return pvr2_state.frame_count;
   163 }
   165 /**
   166  * Display the next frame, copying the current contents of video ram to
   167  * the window. If the video configuration has changed, first recompute the
   168  * new frame size/depth.
   169  */
   170 void pvr2_display_frame( void )
   171 {
   172     uint32_t display_addr = MMIO_READ( PVR2, DISP_ADDR1 );
   174     int dispsize = MMIO_READ( PVR2, DISP_SIZE );
   175     int dispmode = MMIO_READ( PVR2, DISP_MODE );
   176     int vidcfg = MMIO_READ( PVR2, DISP_SYNCCFG );
   177     int vid_stride = ((dispsize & DISPSIZE_MODULO) >> 20) - 1;
   178     int vid_lpf = ((dispsize & DISPSIZE_LPF) >> 10) + 1;
   179     int vid_ppl = ((dispsize & DISPSIZE_PPL)) + 1;
   180     gboolean bEnabled = (dispmode & DISPMODE_DE) && (vidcfg & DISPCFG_VO ) ? TRUE : FALSE;
   181     gboolean interlaced = (vidcfg & DISPCFG_I ? TRUE : FALSE);
   182     video_buffer_t buffer = &video_buffer[video_buffer_idx];
   183     video_buffer_idx = !video_buffer_idx;
   184     video_buffer_t last = &video_buffer[video_buffer_idx];
   185     buffer->rowstride = (vid_ppl + vid_stride) << 2;
   186     buffer->data = video_base + MMIO_READ( PVR2, DISP_ADDR1 );
   187     buffer->vres = vid_lpf;
   188     if( interlaced ) buffer->vres <<= 1;
   189     switch( (dispmode & DISPMODE_COL) >> 2 ) {
   190     case 0: 
   191 	buffer->colour_format = COLFMT_ARGB1555;
   192 	buffer->hres = vid_ppl << 1; 
   193 	break;
   194     case 1: 
   195 	buffer->colour_format = COLFMT_RGB565;
   196 	buffer->hres = vid_ppl << 1; 
   197 	break;
   198     case 2:
   199 	buffer->colour_format = COLFMT_RGB888;
   200 	buffer->hres = (vid_ppl << 2) / 3; 
   201 	break;
   202     case 3: 
   203 	buffer->colour_format = COLFMT_ARGB8888;
   204 	buffer->hres = vid_ppl; 
   205 	break;
   206     }
   208     if( buffer->hres <=8 )
   209 	buffer->hres = 640;
   210     if( buffer->vres <=8 )
   211 	buffer->vres = 480;
   212     if( display_driver != NULL ) {
   213 	if( buffer->hres != last->hres ||
   214 	    buffer->vres != last->vres ||
   215 	    buffer->colour_format != last->colour_format) {
   216 	    display_driver->set_display_format( buffer->hres, buffer->vres,
   217 						buffer->colour_format );
   218 	}
   219 	if( !bEnabled ) {
   220 	    display_driver->display_blank_frame( 0 );
   221 	} else if( MMIO_READ( PVR2, DISP_CFG2 ) & 0x08 ) { /* Blanked */
   222 	    uint32_t colour = MMIO_READ( PVR2, DISP_BORDER );
   223 	    display_driver->display_blank_frame( colour );
   224 	} else if( !pvr2_render_display_frame( PVR2_RAM_BASE + display_addr ) ) {
   225 	    display_driver->display_frame( buffer );
   226 	}
   227     }
   228     pvr2_state.frame_count++;
   229 }
   231 /**
   232  * This has to handle every single register individually as they all get masked 
   233  * off differently (and its easier to do it at write time)
   234  */
   235 void mmio_region_PVR2_write( uint32_t reg, uint32_t val )
   236 {
   237     if( reg >= 0x200 && reg < 0x600 ) { /* Fog table */
   238         MMIO_WRITE( PVR2, reg, val );
   239         return;
   240     }
   242     switch(reg) {
   243     case PVRID:
   244     case PVRVER:
   245     case GUNPOS: /* Read only registers */
   246 	break;
   247     case PVRRESET:
   248 	val &= 0x00000007; /* Do stuff? */
   249 	MMIO_WRITE( PVR2, reg, val );
   250 	break;
   251     case RENDER_START:
   252 	if( val == 0xFFFFFFFF || val == 0x00000001 )
   253 	    pvr2_render_scene();
   254 	break;
   255     case RENDER_POLYBASE:
   256     	MMIO_WRITE( PVR2, reg, val&0x00F00000 );
   257     	break;
   258     case RENDER_TSPCFG:
   259     	MMIO_WRITE( PVR2, reg, val&0x00010101 );
   260     	break;
   261     case DISP_BORDER:
   262     	MMIO_WRITE( PVR2, reg, val&0x01FFFFFF );
   263     	break;
   264     case DISP_MODE:
   265     	MMIO_WRITE( PVR2, reg, val&0x00FFFF7F );
   266     	break;
   267     case RENDER_MODE:
   268     	MMIO_WRITE( PVR2, reg, val&0x00FFFF0F );
   269     	break;
   270     case RENDER_SIZE:
   271     	MMIO_WRITE( PVR2, reg, val&0x000001FF );
   272     	break;
   273     case DISP_ADDR1:
   274 	val &= 0x00FFFFFC;
   275 	MMIO_WRITE( PVR2, reg, val );
   276 	if( pvr2_state.retrace ) {
   277 	    pvr2_display_frame();
   278 	    pvr2_state.retrace = FALSE;
   279 	}
   280 	break;
   281     case DISP_ADDR2:
   282     	MMIO_WRITE( PVR2, reg, val&0x00FFFFFC );
   283     	break;
   284     case DISP_SIZE:
   285     	MMIO_WRITE( PVR2, reg, val&0x3FFFFFFF );
   286     	break;
   287     case RENDER_ADDR1:
   288     case RENDER_ADDR2:
   289     	MMIO_WRITE( PVR2, reg, val&0x01FFFFFC );
   290     	break;
   291     case RENDER_HCLIP:
   292 	MMIO_WRITE( PVR2, reg, val&0x07FF07FF );
   293 	break;
   294     case RENDER_VCLIP:
   295 	MMIO_WRITE( PVR2, reg, val&0x03FF03FF );
   296 	break;
   297     case DISP_HPOSIRQ:
   298 	MMIO_WRITE( PVR2, reg, val&0x03FF33FF );
   299 	break;
   300     case DISP_VPOSIRQ:
   301 	val = val & 0x03FF03FF;
   302 	pvr2_state.irq_vpos1 = (val >> 16);
   303 	pvr2_state.irq_vpos2 = val & 0x03FF;
   304 	MMIO_WRITE( PVR2, reg, val );
   305 	break;
   306     case RENDER_NEARCLIP:
   307 	MMIO_WRITE( PVR2, reg, val & 0x7FFFFFFF );
   308 	break;
   309     case RENDER_SHADOW:
   310 	MMIO_WRITE( PVR2, reg, val&0x000001FF );
   311 	break;
   312     case RENDER_OBJCFG:
   313     	MMIO_WRITE( PVR2, reg, val&0x003FFFFF );
   314     	break;
   315     case RENDER_TSPCLIP:
   316     	MMIO_WRITE( PVR2, reg, val&0x7FFFFFFF );
   317     	break;
   318     case RENDER_FARCLIP:
   319 	MMIO_WRITE( PVR2, reg, val&0xFFFFFFF0 );
   320 	break;
   321     case RENDER_BGPLANE:
   322     	MMIO_WRITE( PVR2, reg, val&0x1FFFFFFF );
   323     	break;
   324     case RENDER_ISPCFG:
   325     	MMIO_WRITE( PVR2, reg, val&0x00FFFFF9 );
   326     	break;
   327     case VRAM_CFG1:
   328 	MMIO_WRITE( PVR2, reg, val&0x000000FF );
   329 	break;
   330     case VRAM_CFG2:
   331 	MMIO_WRITE( PVR2, reg, val&0x003FFFFF );
   332 	break;
   333     case VRAM_CFG3:
   334 	MMIO_WRITE( PVR2, reg, val&0x1FFFFFFF );
   335 	break;
   336     case RENDER_FOGTBLCOL:
   337     case RENDER_FOGVRTCOL:
   338 	MMIO_WRITE( PVR2, reg, val&0x00FFFFFF );
   339 	break;
   340     case RENDER_FOGCOEFF:
   341 	MMIO_WRITE( PVR2, reg, val&0x0000FFFF );
   342 	break;
   343     case RENDER_CLAMPHI:
   344     case RENDER_CLAMPLO:
   345 	MMIO_WRITE( PVR2, reg, val );
   346 	break;
   347     case RENDER_TEXSIZE:
   348 	MMIO_WRITE( PVR2, reg, val&0x00031F1F );
   349 	break;
   350     case RENDER_PALETTE:
   351 	MMIO_WRITE( PVR2, reg, val&0x00000003 );
   352 	break;
   354 	/********** CRTC registers *************/
   355     case DISP_HBORDER:
   356     case DISP_VBORDER:
   357 	MMIO_WRITE( PVR2, reg, val&0x03FF03FF );
   358 	break;
   359     case DISP_TOTAL:
   360 	val = val & 0x03FF03FF;
   361 	MMIO_WRITE( PVR2, reg, val );
   362 	pvr2_state.total_lines = (val >> 16) + 1;
   363 	pvr2_state.line_size = (val & 0x03FF) + 1;
   364 	pvr2_state.line_time_ns = 1000000 * pvr2_state.line_size / pvr2_state.dot_clock;
   365 	break;
   366     case DISP_SYNCCFG:
   367 	MMIO_WRITE( PVR2, reg, val&0x000003FF );
   368 	pvr2_state.interlaced = (val & 0x0010) ? TRUE : FALSE;
   369 	break;
   370     case DISP_SYNCTIME:
   371 	pvr2_state.vsync_lines = (val >> 8) & 0x0F;
   372 	pvr2_state.hsync_width_ns = ((val & 0x7F) + 1) * 1000000 / pvr2_state.dot_clock;
   373 	MMIO_WRITE( PVR2, reg, val&0xFFFFFF7F );
   374 	break;
   375     case DISP_CFG2:
   376 	MMIO_WRITE( PVR2, reg, val&0x003F01FF );
   377 	break;
   378     case DISP_HPOS:
   379 	val = val & 0x03FF;
   380 	pvr2_state.front_porch_ns = (val + 1) * 1000000 / pvr2_state.dot_clock;
   381 	MMIO_WRITE( PVR2, reg, val );
   382 	break;
   383     case DISP_VPOS:
   384 	MMIO_WRITE( PVR2, reg, val&0x03FF03FF );
   385 	break;
   387 	/*********** Tile accelerator registers ***********/
   388     case TA_POLYPOS:
   389     case TA_LISTPOS:
   390 	/* Readonly registers */
   391 	break;
   392     case TA_TILEBASE:
   393     case TA_LISTEND:
   394     case TA_LISTBASE:
   395 	MMIO_WRITE( PVR2, reg, val&0x00FFFFE0 );
   396 	break;
   397     case RENDER_TILEBASE:
   398     case TA_POLYBASE:
   399     case TA_POLYEND:
   400 	MMIO_WRITE( PVR2, reg, val&0x00FFFFFC );
   401 	break;
   402     case TA_TILESIZE:
   403 	MMIO_WRITE( PVR2, reg, val&0x000F003F );
   404 	break;
   405     case TA_TILECFG:
   406 	MMIO_WRITE( PVR2, reg, val&0x00133333 );
   407 	break;
   408     case TA_INIT:
   409 	if( val & 0x80000000 )
   410 	    pvr2_ta_init();
   411 	break;
   412     case TA_REINIT:
   413 	break;
   414 	/**************** Scaler registers? ****************/
   415     case SCALERCFG:
   416 	MMIO_WRITE( PVR2, reg, val&0x0007FFFF );
   417 	break;
   419     case YUV_ADDR:
   420 	MMIO_WRITE( PVR2, reg, val&0x00FFFFF8 );
   421 	break;
   422     case YUV_CFG:
   423 	MMIO_WRITE( PVR2, reg, val&0x01013F3F );
   424 	break;
   427 	/**************** Unknowns ***************/
   428     case PVRUNK1:
   429     	MMIO_WRITE( PVR2, reg, val&0x000007FF );
   430     	break;
   431     case PVRUNK2:
   432 	MMIO_WRITE( PVR2, reg, val&0x00000007 );
   433 	break;
   434     case PVRUNK3:
   435 	MMIO_WRITE( PVR2, reg, val&0x000FFF3F );
   436 	break;
   437     case PVRUNK5:
   438 	MMIO_WRITE( PVR2, reg, val&0x0000FFFF );
   439 	break;
   440     case PVRUNK6:
   441 	MMIO_WRITE( PVR2, reg, val&0x000000FF );
   442 	break;
   443     case PVRUNK7:
   444 	MMIO_WRITE( PVR2, reg, val&0x00000001 );
   445 	break;
   446     }
   447 }
   449 /**
   450  * Calculate the current read value of the syncstat register, using
   451  * the current SH4 clock time as an offset from the last timeslice.
   452  * The register reads (LSB to MSB) as:
   453  *     0..9  Current scan line
   454  *     10    Odd/even field (1 = odd, 0 = even)
   455  *     11    Display active (including border and overscan)
   456  *     12    Horizontal sync off
   457  *     13    Vertical sync off
   458  * Note this method is probably incorrect for anything other than straight
   459  * interlaced PAL, and needs further testing.
   460  */
   461 uint32_t pvr2_get_sync_status()
   462 {
   463     uint32_t tmp = pvr2_state.line_remainder + sh4r.slice_cycle;
   464     uint32_t line = pvr2_state.line_count + (tmp / pvr2_state.line_time_ns);
   465     uint32_t remainder = tmp % pvr2_state.line_time_ns;
   466     uint32_t field = pvr2_state.odd_even_field;
   467     uint32_t result;
   469     if( line >= pvr2_state.total_lines ) {
   470 	line -= pvr2_state.total_lines;
   471 	if( pvr2_state.interlaced ) {
   472 	    field == 1 ? 0 : 1;
   473 	}
   474     }
   476     result = line;
   478     if( field ) {
   479 	result |= 0x0400;
   480     }
   481     if( (line & 0x01) == field ) {
   482 	if( remainder > pvr2_state.hsync_width_ns ) {
   483 	    result |= 0x1000; /* !HSYNC */
   484 	}
   485 	if( line >= pvr2_state.vsync_lines ) {
   486 	    if( remainder > pvr2_state.front_porch_ns ) {
   487 		result |= 0x2800; /* Display active */
   488 	    } else {
   489 		result |= 0x2000; /* Front porch */
   490 	    }
   491 	}
   492     } else {
   493 	if( remainder < (pvr2_state.line_time_ns - pvr2_state.back_porch_ns) &&
   494 	    line >= pvr2_state.vsync_lines ) {
   495 	    result |= 0x3800; /* Display active */
   496 	} else {
   497 	    result |= 0x1000; /* Back porch */
   498 	}
   499     }
   500     return result;
   501 }
   503 MMIO_REGION_READ_FN( PVR2, reg )
   504 {
   505     switch( reg ) {
   506         case DISP_SYNCSTAT:
   507             return pvr2_get_sync_status();
   508         default:
   509             return MMIO_READ( PVR2, reg );
   510     }
   511 }
   513 MMIO_REGION_DEFFNS( PVR2PAL )
   515 void pvr2_set_base_address( uint32_t base ) 
   516 {
   517     mmio_region_PVR2_write( DISP_ADDR1, base );
   518 }
   523 int32_t mmio_region_PVR2TA_read( uint32_t reg )
   524 {
   525     return 0xFFFFFFFF;
   526 }
   528 void mmio_region_PVR2TA_write( uint32_t reg, uint32_t val )
   529 {
   530     pvr2_ta_write( (char *)&val, sizeof(uint32_t) );
   531 }
   534 void pvr2_vram64_write( sh4addr_t destaddr, char *src, uint32_t length )
   535 {
   536     int bank_flag = (destaddr & 0x04) >> 2;
   537     uint32_t *banks[2];
   538     uint32_t *dwsrc;
   539     int i;
   541     destaddr = destaddr & 0x7FFFFF;
   542     if( destaddr + length > 0x800000 ) {
   543 	length = 0x800000 - destaddr;
   544     }
   546     for( i=destaddr & 0xFFFFF000; i < destaddr + length; i+= PAGE_SIZE ) {
   547 	texcache_invalidate_page( i );
   548     }
   550     banks[0] = ((uint32_t *)(video_base + ((destaddr & 0x007FFFF8) >>1)));
   551     banks[1] = banks[0] + 0x100000;
   552     if( bank_flag ) 
   553 	banks[0]++;
   555     /* Handle non-aligned start of source */
   556     if( destaddr & 0x03 ) {
   557 	char *dest = ((char *)banks[bank_flag]) + (destaddr & 0x03);
   558 	for( i= destaddr & 0x03; i < 4 && length > 0; i++, length-- ) {
   559 	    *dest++ = *src++;
   560 	}
   561 	bank_flag = !bank_flag;
   562     }
   564     dwsrc = (uint32_t *)src;
   565     while( length >= 4 ) {
   566 	*banks[bank_flag]++ = *dwsrc++;
   567 	bank_flag = !bank_flag;
   568 	length -= 4;
   569     }
   571     /* Handle non-aligned end of source */
   572     if( length ) {
   573 	src = (char *)dwsrc;
   574 	char *dest = (char *)banks[bank_flag];
   575 	while( length-- > 0 ) {
   576 	    *dest++ = *src++;
   577 	}
   578     }  
   579 }
   581 void pvr2_vram_write_invert( sh4addr_t destaddr, char *src, uint32_t length, uint32_t line_length )
   582 {
   583     char *dest = video_base + (destaddr & 0x007FFFFF);
   584     char *p = src + length - line_length;
   585     while( p >= src ) {
   586 	memcpy( dest, p, line_length );
   587 	p -= line_length;
   588 	dest += line_length;
   589     }
   590 }
   592 void pvr2_vram64_read( char *dest, sh4addr_t srcaddr, uint32_t length )
   593 {
   594     int bank_flag = (srcaddr & 0x04) >> 2;
   595     uint32_t *banks[2];
   596     uint32_t *dwdest;
   597     int i;
   599     srcaddr = srcaddr & 0x7FFFFF;
   600     if( srcaddr + length > 0x800000 )
   601 	length = 0x800000 - srcaddr;
   603     banks[0] = ((uint32_t *)(video_base + ((srcaddr&0x007FFFF8)>>1)));
   604     banks[1] = banks[0] + 0x100000;
   605     if( bank_flag )
   606 	banks[0]++;
   608     /* Handle non-aligned start of source */
   609     if( srcaddr & 0x03 ) {
   610 	char *src = ((char *)banks[bank_flag]) + (srcaddr & 0x03);
   611 	for( i= srcaddr & 0x03; i < 4 && length > 0; i++, length-- ) {
   612 	    *dest++ = *src++;
   613 	}
   614 	bank_flag = !bank_flag;
   615     }
   617     dwdest = (uint32_t *)dest;
   618     while( length >= 4 ) {
   619 	*dwdest++ = *banks[bank_flag]++;
   620 	bank_flag = !bank_flag;
   621 	length -= 4;
   622     }
   624     /* Handle non-aligned end of source */
   625     if( length ) {
   626 	dest = (char *)dwdest;
   627 	char *src = (char *)banks[bank_flag];
   628 	while( length-- > 0 ) {
   629 	    *dest++ = *src++;
   630 	}
   631     }
   632 }
   634 void pvr2_vram64_dump( sh4addr_t addr, uint32_t length, FILE *f ) 
   635 {
   636     char tmp[length];
   637     pvr2_vram64_read( tmp, addr, length );
   638     fwrite_dump( tmp, length, f );
   639 }
.