nkeynes@31 | 1 | /**
|
nkeynes@98 | 2 | * $Id: pvr2.c,v 1.15 2006-02-15 12:40:20 nkeynes Exp $
|
nkeynes@31 | 3 | *
|
nkeynes@31 | 4 | * PVR2 (Video) MMIO and supporting functions.
|
nkeynes@31 | 5 | *
|
nkeynes@31 | 6 | * Copyright (c) 2005 Nathan Keynes.
|
nkeynes@31 | 7 | *
|
nkeynes@31 | 8 | * This program is free software; you can redistribute it and/or modify
|
nkeynes@31 | 9 | * it under the terms of the GNU General Public License as published by
|
nkeynes@31 | 10 | * the Free Software Foundation; either version 2 of the License, or
|
nkeynes@31 | 11 | * (at your option) any later version.
|
nkeynes@31 | 12 | *
|
nkeynes@31 | 13 | * This program is distributed in the hope that it will be useful,
|
nkeynes@31 | 14 | * but WITHOUT ANY WARRANTY; without even the implied warranty of
|
nkeynes@31 | 15 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
nkeynes@31 | 16 | * GNU General Public License for more details.
|
nkeynes@31 | 17 | */
|
nkeynes@35 | 18 | #define MODULE pvr2_module
|
nkeynes@31 | 19 |
|
nkeynes@1 | 20 | #include "dream.h"
|
nkeynes@1 | 21 | #include "video.h"
|
nkeynes@1 | 22 | #include "mem.h"
|
nkeynes@1 | 23 | #include "asic.h"
|
nkeynes@1 | 24 | #include "pvr2.h"
|
nkeynes@56 | 25 | #include "sh4/sh4core.h"
|
nkeynes@1 | 26 | #define MMIO_IMPL
|
nkeynes@1 | 27 | #include "pvr2.h"
|
nkeynes@1 | 28 |
|
nkeynes@1 | 29 | char *video_base;
|
nkeynes@1 | 30 |
|
nkeynes@15 | 31 | void pvr2_init( void );
|
nkeynes@30 | 32 | uint32_t pvr2_run_slice( uint32_t );
|
nkeynes@94 | 33 | void pvr2_display_frame( void );
|
nkeynes@94 | 34 |
|
nkeynes@94 | 35 | video_driver_t video_driver = NULL;
|
nkeynes@94 | 36 | struct video_buffer video_buffer[2];
|
nkeynes@94 | 37 | int video_buffer_idx = 0;
|
nkeynes@15 | 38 |
|
nkeynes@23 | 39 | struct dreamcast_module pvr2_module = { "PVR2", pvr2_init, NULL, NULL,
|
nkeynes@23 | 40 | pvr2_run_slice, NULL,
|
nkeynes@15 | 41 | NULL, NULL };
|
nkeynes@15 | 42 |
|
nkeynes@1 | 43 | void pvr2_init( void )
|
nkeynes@1 | 44 | {
|
nkeynes@1 | 45 | register_io_region( &mmio_region_PVR2 );
|
nkeynes@85 | 46 | register_io_region( &mmio_region_PVR2PAL );
|
nkeynes@56 | 47 | register_io_region( &mmio_region_PVR2TA );
|
nkeynes@1 | 48 | video_base = mem_get_region_by_name( MEM_REGION_VIDEO );
|
nkeynes@94 | 49 | video_driver = &video_gtk_driver;
|
nkeynes@98 | 50 | video_driver->set_output_format( 640, 480, COLFMT_RGB32 );
|
nkeynes@1 | 51 | }
|
nkeynes@1 | 52 |
|
nkeynes@23 | 53 | uint32_t pvr2_time_counter = 0;
|
nkeynes@94 | 54 | uint32_t pvr2_frame_counter = 0;
|
nkeynes@30 | 55 | uint32_t pvr2_time_per_frame = 20000000;
|
nkeynes@23 | 56 |
|
nkeynes@30 | 57 | uint32_t pvr2_run_slice( uint32_t nanosecs )
|
nkeynes@23 | 58 | {
|
nkeynes@30 | 59 | pvr2_time_counter += nanosecs;
|
nkeynes@30 | 60 | while( pvr2_time_counter >= pvr2_time_per_frame ) {
|
nkeynes@94 | 61 | pvr2_display_frame();
|
nkeynes@23 | 62 | pvr2_time_counter -= pvr2_time_per_frame;
|
nkeynes@23 | 63 | }
|
nkeynes@30 | 64 | return nanosecs;
|
nkeynes@23 | 65 | }
|
nkeynes@23 | 66 |
|
nkeynes@1 | 67 | uint32_t vid_stride, vid_lpf, vid_ppl, vid_hres, vid_vres, vid_col;
|
nkeynes@1 | 68 | int interlaced, bChanged = 1, bEnabled = 0, vid_size = 0;
|
nkeynes@1 | 69 | char *frame_start; /* current video start address (in real memory) */
|
nkeynes@1 | 70 |
|
nkeynes@1 | 71 | /*
|
nkeynes@1 | 72 | * Display the next frame, copying the current contents of video ram to
|
nkeynes@1 | 73 | * the window. If the video configuration has changed, first recompute the
|
nkeynes@1 | 74 | * new frame size/depth.
|
nkeynes@1 | 75 | */
|
nkeynes@94 | 76 | void pvr2_display_frame( void )
|
nkeynes@1 | 77 | {
|
nkeynes@94 | 78 | int dispsize = MMIO_READ( PVR2, DISPSIZE );
|
nkeynes@94 | 79 | int dispmode = MMIO_READ( PVR2, DISPMODE );
|
nkeynes@94 | 80 | int vidcfg = MMIO_READ( PVR2, VIDCFG );
|
nkeynes@94 | 81 | int vid_stride = ((dispsize & DISPSIZE_MODULO) >> 20) - 1;
|
nkeynes@94 | 82 | int vid_lpf = ((dispsize & DISPSIZE_LPF) >> 10) + 1;
|
nkeynes@94 | 83 | int vid_ppl = ((dispsize & DISPSIZE_PPL)) + 1;
|
nkeynes@94 | 84 | gboolean bEnabled = (dispmode & DISPMODE_DE) && (vidcfg & VIDCFG_VO ) ? TRUE : FALSE;
|
nkeynes@94 | 85 | gboolean interlaced = (vidcfg & VIDCFG_I ? TRUE : FALSE);
|
nkeynes@1 | 86 | if( bEnabled ) {
|
nkeynes@94 | 87 | video_buffer_t buffer = &video_buffer[video_buffer_idx];
|
nkeynes@94 | 88 | video_buffer_idx = !video_buffer_idx;
|
nkeynes@94 | 89 | video_buffer_t last = &video_buffer[video_buffer_idx];
|
nkeynes@94 | 90 | buffer->colour_format = (dispmode & DISPMODE_COL);
|
nkeynes@94 | 91 | buffer->rowstride = (vid_ppl + vid_stride) << 2;
|
nkeynes@94 | 92 | buffer->data = frame_start = video_base + MMIO_READ( PVR2, DISPADDR1 );
|
nkeynes@94 | 93 | buffer->vres = vid_lpf;
|
nkeynes@94 | 94 | if( interlaced ) buffer->vres <<= 1;
|
nkeynes@94 | 95 | switch( buffer->colour_format ) {
|
nkeynes@94 | 96 | case COLFMT_RGB15:
|
nkeynes@94 | 97 | case COLFMT_RGB16: buffer->hres = vid_ppl << 1; break;
|
nkeynes@94 | 98 | case COLFMT_RGB24: buffer->hres = (vid_ppl << 2) / 3; break;
|
nkeynes@94 | 99 | case COLFMT_RGB32: buffer->hres = vid_ppl; break;
|
nkeynes@94 | 100 | }
|
nkeynes@94 | 101 |
|
nkeynes@94 | 102 | if( video_driver != NULL ) {
|
nkeynes@94 | 103 | if( buffer->hres != last->hres ||
|
nkeynes@94 | 104 | buffer->vres != last->vres ||
|
nkeynes@94 | 105 | buffer->colour_format != last->colour_format) {
|
nkeynes@94 | 106 | video_driver->set_output_format( buffer->hres, buffer->vres,
|
nkeynes@94 | 107 | buffer->colour_format );
|
nkeynes@94 | 108 | }
|
nkeynes@94 | 109 | if( MMIO_READ( PVR2, VIDCFG2 ) & 0x08 ) { /* Blanked */
|
nkeynes@94 | 110 | uint32_t colour = MMIO_READ( PVR2, BORDERCOL );
|
nkeynes@94 | 111 | video_driver->display_blank_frame( colour );
|
nkeynes@94 | 112 | } else {
|
nkeynes@94 | 113 | video_driver->display_frame( buffer );
|
nkeynes@94 | 114 | }
|
nkeynes@65 | 115 | }
|
nkeynes@1 | 116 | } else {
|
nkeynes@94 | 117 | video_buffer_idx = 0;
|
nkeynes@94 | 118 | video_buffer[0].hres = video_buffer[0].vres = 0;
|
nkeynes@1 | 119 | }
|
nkeynes@94 | 120 | pvr2_frame_counter++;
|
nkeynes@1 | 121 | asic_event( EVENT_SCANLINE1 );
|
nkeynes@1 | 122 | asic_event( EVENT_SCANLINE2 );
|
nkeynes@1 | 123 | asic_event( EVENT_RETRACE );
|
nkeynes@1 | 124 | }
|
nkeynes@1 | 125 |
|
nkeynes@1 | 126 | void mmio_region_PVR2_write( uint32_t reg, uint32_t val )
|
nkeynes@1 | 127 | {
|
nkeynes@1 | 128 | if( reg >= 0x200 && reg < 0x600 ) { /* Fog table */
|
nkeynes@1 | 129 | MMIO_WRITE( PVR2, reg, val );
|
nkeynes@1 | 130 | /* I don't want to hear about these */
|
nkeynes@1 | 131 | return;
|
nkeynes@1 | 132 | }
|
nkeynes@1 | 133 |
|
nkeynes@1 | 134 | INFO( "PVR2 write to %08X <= %08X [%s: %s]", reg, val,
|
nkeynes@1 | 135 | MMIO_REGID(PVR2,reg), MMIO_REGDESC(PVR2,reg) );
|
nkeynes@1 | 136 |
|
nkeynes@1 | 137 | switch(reg) {
|
nkeynes@65 | 138 | case RENDSTART:
|
nkeynes@65 | 139 | if( val == 0xFFFFFFFF )
|
nkeynes@65 | 140 | pvr2_render_scene();
|
nkeynes@65 | 141 | break;
|
nkeynes@1 | 142 | }
|
nkeynes@1 | 143 | MMIO_WRITE( PVR2, reg, val );
|
nkeynes@1 | 144 | }
|
nkeynes@1 | 145 |
|
nkeynes@1 | 146 | MMIO_REGION_READ_FN( PVR2, reg )
|
nkeynes@1 | 147 | {
|
nkeynes@1 | 148 | switch( reg ) {
|
nkeynes@1 | 149 | case BEAMPOS:
|
nkeynes@2 | 150 | return sh4r.icount&0x20 ? 0x2000 : 1;
|
nkeynes@1 | 151 | default:
|
nkeynes@1 | 152 | return MMIO_READ( PVR2, reg );
|
nkeynes@1 | 153 | }
|
nkeynes@1 | 154 | }
|
nkeynes@19 | 155 |
|
nkeynes@85 | 156 | MMIO_REGION_DEFFNS( PVR2PAL )
|
nkeynes@85 | 157 |
|
nkeynes@19 | 158 | void pvr2_set_base_address( uint32_t base )
|
nkeynes@19 | 159 | {
|
nkeynes@19 | 160 | mmio_region_PVR2_write( DISPADDR1, base );
|
nkeynes@19 | 161 | }
|
nkeynes@56 | 162 |
|
nkeynes@56 | 163 |
|
nkeynes@65 | 164 | void pvr2_render_scene( void )
|
nkeynes@65 | 165 | {
|
nkeynes@65 | 166 | /* Actual rendering goes here :) */
|
nkeynes@65 | 167 | asic_event( EVENT_PVR_RENDER_DONE );
|
nkeynes@94 | 168 | DEBUG( "Rendered frame %d", pvr2_frame_counter );
|
nkeynes@65 | 169 | }
|
nkeynes@65 | 170 |
|
nkeynes@65 | 171 | /** Tile Accelerator */
|
nkeynes@65 | 172 |
|
nkeynes@65 | 173 | struct tacmd {
|
nkeynes@65 | 174 | uint32_t command;
|
nkeynes@65 | 175 | uint32_t param1;
|
nkeynes@65 | 176 | uint32_t param2;
|
nkeynes@65 | 177 | uint32_t texture;
|
nkeynes@65 | 178 | float alpha;
|
nkeynes@65 | 179 | float red;
|
nkeynes@65 | 180 | float green;
|
nkeynes@65 | 181 | float blue;
|
nkeynes@65 | 182 | };
|
nkeynes@65 | 183 |
|
nkeynes@98 | 184 | struct vertex_type1 {
|
nkeynes@98 | 185 | uint32_t command;
|
nkeynes@98 | 186 | float x, y, z;
|
nkeynes@98 | 187 | uint32_t blank, blank2;
|
nkeynes@98 | 188 | uint32_t col;
|
nkeynes@98 | 189 | float f;
|
nkeynes@98 | 190 | };
|
nkeynes@98 | 191 |
|
nkeynes@56 | 192 | int32_t mmio_region_PVR2TA_read( uint32_t reg )
|
nkeynes@56 | 193 | {
|
nkeynes@56 | 194 | return 0xFFFFFFFF;
|
nkeynes@56 | 195 | }
|
nkeynes@56 | 196 |
|
nkeynes@56 | 197 | char pvr2ta_remainder[8];
|
nkeynes@56 | 198 |
|
nkeynes@56 | 199 | void mmio_region_PVR2TA_write( uint32_t reg, uint32_t val )
|
nkeynes@56 | 200 | {
|
nkeynes@65 | 201 | DEBUG( "Direct write to TA %08X", val );
|
nkeynes@56 | 202 | }
|
nkeynes@56 | 203 |
|
nkeynes@85 | 204 | unsigned int pvr2_last_poly_type = 0;
|
nkeynes@85 | 205 |
|
nkeynes@56 | 206 | void pvr2ta_write( char *buf, uint32_t length )
|
nkeynes@56 | 207 | {
|
nkeynes@65 | 208 | int i;
|
nkeynes@65 | 209 | struct tacmd *cmd_list = (struct tacmd *)buf;
|
nkeynes@65 | 210 | int count = length >> 5;
|
nkeynes@65 | 211 | for( i=0; i<count; i++ ){
|
nkeynes@65 | 212 | unsigned int type = (cmd_list[i].command >> 24) & 0xFF;
|
nkeynes@98 | 213 | if( type == 0xE0 || type == 0xF0 ) {
|
nkeynes@98 | 214 | struct vertex_type1 *vert = (struct vertex_type1 *)&cmd_list[i];
|
nkeynes@98 | 215 | DEBUG( "PVR2 vrt: %f %f %f %08X %08X %08X %f", vert->x, vert->y, vert->z, vert->blank, vert->blank2, vert->col, vert->f );
|
nkeynes@98 | 216 | } else {
|
nkeynes@98 | 217 | DEBUG( "PVR2 cmd: %08X %08X %08X %08X %08X %08X %08X %08X", cmd_list[i].command, cmd_list[i].param1, cmd_list[i].param2, cmd_list[i].texture, cmd_list[i].alpha, cmd_list[i].red, cmd_list[i].green, cmd_list[i].blue );
|
nkeynes@98 | 218 | }
|
nkeynes@65 | 219 | if( type == 0 ) {
|
nkeynes@65 | 220 | /* End of list */
|
nkeynes@85 | 221 | switch( pvr2_last_poly_type ) {
|
nkeynes@65 | 222 | case 0x80: /* Opaque polys */
|
nkeynes@65 | 223 | asic_event( EVENT_PVR_OPAQUE_DONE );
|
nkeynes@65 | 224 | break;
|
nkeynes@65 | 225 | case 0x81: /* Opaque poly modifier */
|
nkeynes@65 | 226 | asic_event( EVENT_PVR_OPAQUEMOD_DONE );
|
nkeynes@65 | 227 | break;
|
nkeynes@65 | 228 | case 0x82: /* Transparent polys */
|
nkeynes@65 | 229 | asic_event( EVENT_PVR_TRANS_DONE );
|
nkeynes@65 | 230 | break;
|
nkeynes@65 | 231 | case 0x83: /* Transparent poly modifier */
|
nkeynes@65 | 232 | asic_event( EVENT_PVR_TRANSMOD_DONE );
|
nkeynes@65 | 233 | break;
|
nkeynes@65 | 234 | case 0x84: /* Punchthrough */
|
nkeynes@65 | 235 | asic_event( EVENT_PVR_PUNCHOUT_DONE );
|
nkeynes@65 | 236 | break;
|
nkeynes@65 | 237 | }
|
nkeynes@85 | 238 | pvr2_last_poly_type = 0;
|
nkeynes@85 | 239 | } else if( type >= 0x80 && type <= 0x84 ) {
|
nkeynes@85 | 240 | pvr2_last_poly_type = type;
|
nkeynes@65 | 241 | }
|
nkeynes@65 | 242 | }
|
nkeynes@56 | 243 | }
|
nkeynes@65 | 244 |
|