#include "spg.h" #include "Renderer_if.h" #include "pvr_regs.h" #include "hw/holly/holly_intc.h" #include "oslib/oslib.h" #include "hw/sh4/sh4_sched.h" //SPG emulation; Scanline/Raster beam registers & interrupts //Time to emulate that stuff correctly ;) u32 in_vblank=0; u32 clc_pvr_scanline; u32 pvr_numscanlines=512; u32 prv_cur_scanline=-1; u32 vblk_cnt=0; float last_fps=0; //54 mhz pixel clock :) #define PIXEL_CLOCK (54*1000*1000/2) u32 Line_Cycles=0; u32 Frame_Cycles=0; int render_end_schid; int vblank_schid; int time_sync; void CalculateSync() { u32 pixel_clock; float scale_x=1,scale_y=1; pixel_clock=PIXEL_CLOCK / (FB_R_CTRL.vclk_div?1:2); //We need to calculate the pixel clock u32 sync_cycles=(SPG_LOAD.hcount+1)*(SPG_LOAD.vcount+1); pvr_numscanlines=SPG_LOAD.vcount+1; Line_Cycles=(u32)((u64)SH4_MAIN_CLOCK*(u64)(SPG_LOAD.hcount+1)/(u64)pixel_clock); if (SPG_CONTROL.interlace) { //this is a temp hack Line_Cycles/=2; u32 interl_mode=VO_CONTROL.field_mode; //if (interl_mode==2)//3 will be funny =P // scale_y=0.5f;//single interlace //else scale_y=1; } else { if (FB_R_CTRL.vclk_div) { scale_y = 1.0f;//non interlaced VGA mode has full resolution :) } else { scale_y = 0.5f;//non interlaced modes have half resolution } } rend_set_fb_scale(scale_x,scale_y); //Frame_Cycles=(u64)DCclock*(u64)sync_cycles/(u64)pixel_clock; Frame_Cycles=pvr_numscanlines*Line_Cycles; prv_cur_scanline=0; sh4_sched_request(vblank_schid,Line_Cycles); } void os_wait_cycl(u32 c); int elapse_time(int tag, int cycl, int jit) { #if HOST_OS==OS_WINDOWS //os_wait_cycl(cycl); #endif return min(max(Frame_Cycles,(u32)1*1000*1000),(u32)8*1000*1000); } #if HOST_OS==OS_WINDOWS extern double speed_load_mspdf; #else double speed_load_mspdf; #endif double full_rps; u32 fskip=0; //called from sh4 context , should update pvr/ta state and everything else int spg_line_sched(int tag, int cycl, int jit) { clc_pvr_scanline += cycl; while (clc_pvr_scanline >= Line_Cycles)//60 ~hertz = 200 mhz / 60=3333333.333 cycles per screen refresh { //ok .. here , after much effort , we did one line //now , we must check for raster beam interrupts and vblank prv_cur_scanline=(prv_cur_scanline+1)%pvr_numscanlines; clc_pvr_scanline -= Line_Cycles; //Check for scanline interrupts -- really need to test the scanline values if (SPG_VBLANK_INT.vblank_in_interrupt_line_number == prv_cur_scanline) asic_RaiseInterrupt(holly_SCANINT1); if (SPG_VBLANK_INT.vblank_out_interrupt_line_number == prv_cur_scanline) asic_RaiseInterrupt(holly_SCANINT2); if (SPG_VBLANK.vstart == prv_cur_scanline) in_vblank=1; if (SPG_VBLANK.vbend == prv_cur_scanline) in_vblank=0; SPG_STATUS.vsync=in_vblank; SPG_STATUS.scanline=prv_cur_scanline; //Vblank start -- really need to test the scanline values if (prv_cur_scanline==0) { if (SPG_CONTROL.interlace) SPG_STATUS.fieldnum=~SPG_STATUS.fieldnum; else SPG_STATUS.fieldnum=0; //Vblank counter vblk_cnt++; asic_RaiseInterrupt(holly_HBLank);// -> This turned out to be HBlank btw , needs to be emulated ;( //TODO : rend_if_VBlank(); rend_vblank();//notify for vblank :) if ((os_GetSeconds()-last_fps)>2) { static int Last_FC; double ts=os_GetSeconds()-last_fps; double spd_fps=(FrameCount-Last_FC)/ts; double spd_vbs=vblk_cnt/ts; double spd_cpu=spd_vbs*Frame_Cycles; spd_cpu/=1000000; //mrhz kthx double fullvbs=(spd_vbs/spd_cpu)*200; double mv=VertexCount/ts/(spd_cpu/200); char mv_c=' '; Last_FC=FrameCount; if (mv>750) { mv/=1000; //KV mv_c='K'; } if (mv>750) { mv/=1000; // mv_c='M'; } VertexCount=0; vblk_cnt=0; char fpsStr[256]; const char* mode=0; const char* res=0; res=SPG_CONTROL.interlace?"480i":"240p"; if (SPG_CONTROL.NTSC==0 && SPG_CONTROL.PAL==1) mode="PAL"; else if (SPG_CONTROL.NTSC==1 && SPG_CONTROL.PAL==0) mode="NTSC"; else { res=SPG_CONTROL.interlace?"480i":"480p"; mode="VGA"; } double frames_done=spd_cpu/2; double mspdf=1/frames_done*1000; full_rps=(spd_fps+fskip/ts); sprintf(fpsStr,"%s/%c - %4.2f (%4.2f) - %4.2f - V: %4.2f (%.2f, %s%s%4.2f) R: %4.2f+%4.2f VTX: %4.2f%c", VER_SHORTNAME,'n',mspdf,speed_load_mspdf,spd_cpu*100/200,spd_vbs, spd_vbs/full_rps,mode,res,fullvbs, spd_fps,fskip/ts ,mv,mv_c); fskip=0; os_SetWindowText(fpsStr); last_fps=os_GetSeconds(); } } } //interrupts //0 //vblank_in_interrupt_line_number //vblank_out_interrupt_line_number //vstart //vbend //pvr_numscanlines u32 min_scanline=prv_cur_scanline+1; u32 min_active=pvr_numscanlines; if (min_scanlinerend.Overrun) { VertexCount+= cntx->rend.verts.used(); PVR_VTXC+= cntx->rend.verts.used(); int render_end_pending_cycles= cntx->rend.verts.used()*60; //if (render_end_pending_cycles<500000) render_end_pending_cycles+=500000*3; sh4_sched_request(render_end_schid,render_end_pending_cycles); } else { sh4_sched_request(render_end_schid, 4096); } }