nkeynes@31: /** nkeynes@94: * $Id: pvr2.c,v 1.14 2006-02-05 04:05:27 nkeynes Exp $ nkeynes@31: * nkeynes@31: * PVR2 (Video) MMIO and supporting functions. nkeynes@31: * nkeynes@31: * Copyright (c) 2005 Nathan Keynes. nkeynes@31: * nkeynes@31: * This program is free software; you can redistribute it and/or modify nkeynes@31: * it under the terms of the GNU General Public License as published by nkeynes@31: * the Free Software Foundation; either version 2 of the License, or nkeynes@31: * (at your option) any later version. nkeynes@31: * nkeynes@31: * This program is distributed in the hope that it will be useful, nkeynes@31: * but WITHOUT ANY WARRANTY; without even the implied warranty of nkeynes@31: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the nkeynes@31: * GNU General Public License for more details. nkeynes@31: */ nkeynes@35: #define MODULE pvr2_module nkeynes@31: nkeynes@1: #include "dream.h" nkeynes@1: #include "video.h" nkeynes@1: #include "mem.h" nkeynes@1: #include "asic.h" nkeynes@1: #include "pvr2.h" nkeynes@56: #include "sh4/sh4core.h" nkeynes@1: #define MMIO_IMPL nkeynes@1: #include "pvr2.h" nkeynes@1: nkeynes@1: char *video_base; nkeynes@1: nkeynes@15: void pvr2_init( void ); nkeynes@30: uint32_t pvr2_run_slice( uint32_t ); nkeynes@94: void pvr2_display_frame( void ); nkeynes@94: nkeynes@94: video_driver_t video_driver = NULL; nkeynes@94: struct video_buffer video_buffer[2]; nkeynes@94: int video_buffer_idx = 0; nkeynes@15: nkeynes@23: struct dreamcast_module pvr2_module = { "PVR2", pvr2_init, NULL, NULL, nkeynes@23: pvr2_run_slice, NULL, nkeynes@15: NULL, NULL }; nkeynes@15: nkeynes@1: void pvr2_init( void ) nkeynes@1: { nkeynes@1: register_io_region( &mmio_region_PVR2 ); nkeynes@85: register_io_region( &mmio_region_PVR2PAL ); nkeynes@56: register_io_region( &mmio_region_PVR2TA ); nkeynes@1: video_base = mem_get_region_by_name( MEM_REGION_VIDEO ); nkeynes@94: video_driver = &video_gtk_driver; nkeynes@1: } nkeynes@1: nkeynes@23: uint32_t pvr2_time_counter = 0; nkeynes@94: uint32_t pvr2_frame_counter = 0; nkeynes@30: uint32_t pvr2_time_per_frame = 20000000; nkeynes@23: nkeynes@30: uint32_t pvr2_run_slice( uint32_t nanosecs ) nkeynes@23: { nkeynes@30: pvr2_time_counter += nanosecs; nkeynes@30: while( pvr2_time_counter >= pvr2_time_per_frame ) { nkeynes@94: pvr2_display_frame(); nkeynes@23: pvr2_time_counter -= pvr2_time_per_frame; nkeynes@23: } nkeynes@30: return nanosecs; nkeynes@23: } nkeynes@23: nkeynes@1: uint32_t vid_stride, vid_lpf, vid_ppl, vid_hres, vid_vres, vid_col; nkeynes@1: int interlaced, bChanged = 1, bEnabled = 0, vid_size = 0; nkeynes@1: char *frame_start; /* current video start address (in real memory) */ nkeynes@1: nkeynes@1: /* nkeynes@1: * Display the next frame, copying the current contents of video ram to nkeynes@1: * the window. If the video configuration has changed, first recompute the nkeynes@1: * new frame size/depth. nkeynes@1: */ nkeynes@94: void pvr2_display_frame( void ) nkeynes@1: { nkeynes@94: int dispsize = MMIO_READ( PVR2, DISPSIZE ); nkeynes@94: int dispmode = MMIO_READ( PVR2, DISPMODE ); nkeynes@94: int vidcfg = MMIO_READ( PVR2, VIDCFG ); nkeynes@94: int vid_stride = ((dispsize & DISPSIZE_MODULO) >> 20) - 1; nkeynes@94: int vid_lpf = ((dispsize & DISPSIZE_LPF) >> 10) + 1; nkeynes@94: int vid_ppl = ((dispsize & DISPSIZE_PPL)) + 1; nkeynes@94: gboolean bEnabled = (dispmode & DISPMODE_DE) && (vidcfg & VIDCFG_VO ) ? TRUE : FALSE; nkeynes@94: gboolean interlaced = (vidcfg & VIDCFG_I ? TRUE : FALSE); nkeynes@1: if( bEnabled ) { nkeynes@94: video_buffer_t buffer = &video_buffer[video_buffer_idx]; nkeynes@94: video_buffer_idx = !video_buffer_idx; nkeynes@94: video_buffer_t last = &video_buffer[video_buffer_idx]; nkeynes@94: buffer->colour_format = (dispmode & DISPMODE_COL); nkeynes@94: buffer->rowstride = (vid_ppl + vid_stride) << 2; nkeynes@94: buffer->data = frame_start = video_base + MMIO_READ( PVR2, DISPADDR1 ); nkeynes@94: buffer->vres = vid_lpf; nkeynes@94: if( interlaced ) buffer->vres <<= 1; nkeynes@94: switch( buffer->colour_format ) { nkeynes@94: case COLFMT_RGB15: nkeynes@94: case COLFMT_RGB16: buffer->hres = vid_ppl << 1; break; nkeynes@94: case COLFMT_RGB24: buffer->hres = (vid_ppl << 2) / 3; break; nkeynes@94: case COLFMT_RGB32: buffer->hres = vid_ppl; break; nkeynes@94: } nkeynes@94: nkeynes@94: if( video_driver != NULL ) { nkeynes@94: if( buffer->hres != last->hres || nkeynes@94: buffer->vres != last->vres || nkeynes@94: buffer->colour_format != last->colour_format) { nkeynes@94: video_driver->set_output_format( buffer->hres, buffer->vres, nkeynes@94: buffer->colour_format ); nkeynes@94: } nkeynes@94: if( MMIO_READ( PVR2, VIDCFG2 ) & 0x08 ) { /* Blanked */ nkeynes@94: uint32_t colour = MMIO_READ( PVR2, BORDERCOL ); nkeynes@94: video_driver->display_blank_frame( colour ); nkeynes@94: } else { nkeynes@94: video_driver->display_frame( buffer ); nkeynes@94: } nkeynes@65: } nkeynes@1: } else { nkeynes@94: video_buffer_idx = 0; nkeynes@94: video_buffer[0].hres = video_buffer[0].vres = 0; nkeynes@1: } nkeynes@94: pvr2_frame_counter++; nkeynes@1: asic_event( EVENT_SCANLINE1 ); nkeynes@1: asic_event( EVENT_SCANLINE2 ); nkeynes@1: asic_event( EVENT_RETRACE ); nkeynes@1: } nkeynes@1: nkeynes@1: void mmio_region_PVR2_write( uint32_t reg, uint32_t val ) nkeynes@1: { nkeynes@1: if( reg >= 0x200 && reg < 0x600 ) { /* Fog table */ nkeynes@1: MMIO_WRITE( PVR2, reg, val ); nkeynes@1: /* I don't want to hear about these */ nkeynes@1: return; nkeynes@1: } nkeynes@1: nkeynes@1: INFO( "PVR2 write to %08X <= %08X [%s: %s]", reg, val, nkeynes@1: MMIO_REGID(PVR2,reg), MMIO_REGDESC(PVR2,reg) ); nkeynes@1: nkeynes@1: switch(reg) { nkeynes@65: case RENDSTART: nkeynes@65: if( val == 0xFFFFFFFF ) nkeynes@65: pvr2_render_scene(); nkeynes@65: break; nkeynes@1: } nkeynes@1: MMIO_WRITE( PVR2, reg, val ); nkeynes@1: } nkeynes@1: nkeynes@1: MMIO_REGION_READ_FN( PVR2, reg ) nkeynes@1: { nkeynes@1: switch( reg ) { nkeynes@1: case BEAMPOS: nkeynes@2: return sh4r.icount&0x20 ? 0x2000 : 1; nkeynes@1: default: nkeynes@1: return MMIO_READ( PVR2, reg ); nkeynes@1: } nkeynes@1: } nkeynes@19: nkeynes@85: MMIO_REGION_DEFFNS( PVR2PAL ) nkeynes@85: nkeynes@19: void pvr2_set_base_address( uint32_t base ) nkeynes@19: { nkeynes@19: mmio_region_PVR2_write( DISPADDR1, base ); nkeynes@19: } nkeynes@56: nkeynes@56: nkeynes@65: void pvr2_render_scene( void ) nkeynes@65: { nkeynes@65: /* Actual rendering goes here :) */ nkeynes@65: asic_event( EVENT_PVR_RENDER_DONE ); nkeynes@94: DEBUG( "Rendered frame %d", pvr2_frame_counter ); nkeynes@65: } nkeynes@65: nkeynes@65: /** Tile Accelerator */ nkeynes@65: nkeynes@65: struct tacmd { nkeynes@65: uint32_t command; nkeynes@65: uint32_t param1; nkeynes@65: uint32_t param2; nkeynes@65: uint32_t texture; nkeynes@65: float alpha; nkeynes@65: float red; nkeynes@65: float green; nkeynes@65: float blue; nkeynes@65: }; nkeynes@65: nkeynes@56: int32_t mmio_region_PVR2TA_read( uint32_t reg ) nkeynes@56: { nkeynes@56: return 0xFFFFFFFF; nkeynes@56: } nkeynes@56: nkeynes@56: char pvr2ta_remainder[8]; nkeynes@56: nkeynes@56: void mmio_region_PVR2TA_write( uint32_t reg, uint32_t val ) nkeynes@56: { nkeynes@65: DEBUG( "Direct write to TA %08X", val ); nkeynes@56: } nkeynes@56: nkeynes@85: unsigned int pvr2_last_poly_type = 0; nkeynes@85: nkeynes@56: void pvr2ta_write( char *buf, uint32_t length ) nkeynes@56: { nkeynes@65: int i; nkeynes@65: struct tacmd *cmd_list = (struct tacmd *)buf; nkeynes@65: int count = length >> 5; nkeynes@65: for( i=0; i> 24) & 0xFF; nkeynes@94: 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@65: if( type == 0 ) { nkeynes@65: /* End of list */ nkeynes@85: switch( pvr2_last_poly_type ) { nkeynes@65: case 0x80: /* Opaque polys */ nkeynes@65: asic_event( EVENT_PVR_OPAQUE_DONE ); nkeynes@65: break; nkeynes@65: case 0x81: /* Opaque poly modifier */ nkeynes@65: asic_event( EVENT_PVR_OPAQUEMOD_DONE ); nkeynes@65: break; nkeynes@65: case 0x82: /* Transparent polys */ nkeynes@65: asic_event( EVENT_PVR_TRANS_DONE ); nkeynes@65: break; nkeynes@65: case 0x83: /* Transparent poly modifier */ nkeynes@65: asic_event( EVENT_PVR_TRANSMOD_DONE ); nkeynes@65: break; nkeynes@65: case 0x84: /* Punchthrough */ nkeynes@65: asic_event( EVENT_PVR_PUNCHOUT_DONE ); nkeynes@65: break; nkeynes@65: } nkeynes@85: pvr2_last_poly_type = 0; nkeynes@85: } else if( type >= 0x80 && type <= 0x84 ) { nkeynes@85: pvr2_last_poly_type = type; nkeynes@65: } nkeynes@65: } nkeynes@56: } nkeynes@65: