using MAME.Core; using System; using System.IO; using System.Runtime.InteropServices; namespace MAME.Core { public struct screen_state { public int width; /* current width (HTOTAL) */ public int height; /* current height (VTOTAL) */ public RECT visarea; /* current visible area (HBLANK end/start, VBLANK end/start) */ public int last_partial_scan; /* scanline of last partial update */ public long frame_period; /* attoseconds per frame */ public long vblank_period; /* attoseconds per VBLANK period */ public long scantime; /* attoseconds per scanline */ public long pixeltime; /* attoseconds per pixel */ public Atime vblank_start_time; public Atime vblank_end_time; public long frame_number; }; unsafe partial class Video { public static bool flip_screen_x, flip_screen_y; public static long frame_number_obj; public static Atime frame_update_time; public static screen_state screenstate; public static int video_attributes; private static int PAUSED_REFRESH_RATE = 30, VIDEO_UPDATE_AFTER_VBLANK = 4; public static EmuTimer.emu_timer vblank_begin_timer, vblank_end_timer; public static EmuTimer.emu_timer scanline0_timer, scanline_timer; private static Atime speed_last_emutime, overall_emutime; private static long speed_last_realtime, overall_real_ticks; private static double speed_percent; private static uint throttle_history, overall_valid_counter, overall_real_seconds; private static int[] popcount; //public static ushort[][] bitmapbase; //public static int[][] bitmapbaseN; //public static int[] bitmapcolor; /** bitmapcolor的指针管理 **/ public static ushort[][] bitmapbase; //还有 部分 Array.Copy 在引用 static GCHandle[] bitmapbase_handles; public static ushort*[] bitmapbase_Ptrs; /** end **/ /** bitmapbaseN的指针管理 **/ public static int[][] bitmapbaseN; //还有 部分 Array.Copy 在引用 static GCHandle[] bitmapbaseN_handles; public static int*[] bitmapbaseN_Ptrs; /** end **/ /** bitmapcolor的指针管理 **/ //不再拷贝完整画布 //public static int[] bitmapcolor; //static GCHandle bitmapcolor_handle; //public static IntPtr bitmapcolor_Ptr; public static int[] bitmapcolorRect; static GCHandle bitmapcolorRect_handle; public static IntPtr bitmapcolorRect_Ptr; public static int* bitmapcolorRect_Ptrunsafe; /** end **/ public static int fullwidth, fullheight; public static bool global_throttle; public static int scanline_param; public static RECT new_clip; public static int curbitmap; public static string sDrawText; public static long popup_text_end; public static int iMode, nMode; //private static BitmapData bitmapData; public static int offsetx, offsety, width, height; public delegate void video_delegate(); public static video_delegate video_update_callback, video_eof_callback; private static int NEOGEO_HBEND = 0x01e;//30 /* this should really be 29.5 */ private static int NEOGEO_HBSTART = 0x15e;//350 /* this should really be 349.5 */ private static int NEOGEO_VTOTAL = 0x108;//264 private static int NEOGEO_VBEND = 0x010; private static int NEOGEO_VBSTART = 0x0f0;//240 private static int NEOGEO_VBLANK_RELOAD_HPOS = 0x11f;//287 #region 抽象出去 static Action Act_SubmitVideo; public static void BindFunc(IVideoPlayer Ivp) { Act_SubmitVideo -= Act_SubmitVideo; Act_SubmitVideo += Ivp.SubmitVideo; } static void SubmitVideo(int[] Bitmap, long frame_number) { Act_SubmitVideo.Invoke(Bitmap, frame_number); } #endregion public static void video_init() { Wintime.wintime_init(); global_throttle = true; Motion.motion_handler_callback = Motion.handler_ingame; sDrawText = ""; popup_text_end = 0; popcount = new int[256]{ 0,1,1,2,1,2,2,3, 1,2,2,3,2,3,3,4, 1,2,2,3,2,3,3,4, 2,3,3,4,3,4,4,5, 1,2,2,3,2,3,3,4, 2,3,3,4,3,4,4,5, 2,3,3,4,3,4,4,5, 3,4,4,5,4,5,5,6, 1,2,2,3,2,3,3,4, 2,3,3,4,3,4,4,5, 2,3,3,4,3,4,4,5, 3,4,4,5,4,5,5,6, 2,3,3,4,3,4,4,5, 3,4,4,5,4,5,5,6, 3,4,4,5,4,5,5,6, 4,5,5,6,5,6,6,7, 1,2,2,3,2,3,3,4, 2,3,3,4,3,4,4,5, 2,3,3,4,3,4,4,5, 3,4,4,5,4,5,5,6, 2,3,3,4,3,4,4,5, 3,4,4,5,4,5,5,6, 3,4,4,5,4,5,5,6, 4,5,5,6,5,6,6,7, 2,3,3,4,3,4,4,5, 3,4,4,5,4,5,5,6, 3,4,4,5,4,5,5,6, 4,5,5,6,5,6,6,7, 3,4,4,5,4,5,5,6, 4,5,5,6,5,6,6,7, 4,5,5,6,5,6,6,7, 5,6,6,7,6,7,7,8 }; switch (Machine.sBoard) { case "CPS-1": case "CPS-1(QSound)": screenstate.width = 0x200; screenstate.height = 0x100; screenstate.visarea.min_x = 0; screenstate.visarea.max_x = 0x1ff; screenstate.visarea.min_y = 0; screenstate.visarea.max_y = 0x1ff; fullwidth = 0x200; fullheight = 0x200; frame_update_time = new Atime(0, (long)(1e18 / 59.61));//59.61Hz screenstate.vblank_period = 0; video_attributes = 0; Motion.motion_update_callback = Motion.ui_updateC; bitmapbase = new ushort[2][]; bitmapbase[0] = new ushort[0x200 * 0x200]; bitmapbase[1] = new ushort[0x200 * 0x200]; video_update_callback = CPS.video_update_cps1; video_eof_callback = CPS.video_eof_cps1; break; case "CPS2": screenstate.width = 0x200; screenstate.height = 0x100; screenstate.visarea.min_x = 0; screenstate.visarea.max_x = 0x1ff; screenstate.visarea.min_y = 0; screenstate.visarea.max_y = 0x1ff; fullwidth = 0x200; fullheight = 0x200; frame_update_time = new Atime(0, (long)(1e18 / 8000000) * 512 * 262);//59.637404580152669Hz screenstate.vblank_period = 0; video_attributes = 0; Motion.motion_update_callback = Motion.ui_updateC; bitmapbase = new ushort[2][]; bitmapbase[0] = new ushort[0x200 * 0x200]; bitmapbase[1] = new ushort[0x200 * 0x200]; video_update_callback = CPS.video_update_cps1; video_eof_callback = CPS.video_eof_cps1; break; case "Data East": screenstate.width = 0x100; screenstate.height = 0x100; screenstate.visarea.min_x = 0; screenstate.visarea.max_x = 0xff; screenstate.visarea.min_y = 0x10; screenstate.visarea.max_y = 0xef; fullwidth = 0x100; fullheight = 0x100; frame_update_time = new Atime(0, (long)(1e18 / 60)); screenstate.vblank_period = 0; video_attributes = 0; Motion.motion_update_callback = Motion.ui_updateC; bitmapbase = new ushort[2][]; bitmapbase[0] = new ushort[0x100 * 0x100]; bitmapbase[1] = new ushort[0x100 * 0x100]; video_update_callback = Dataeast.video_update_pcktgal; video_eof_callback = Dataeast.video_eof_pcktgal; switch (Machine.sName) { case "pcktgal": case "pcktgalb": case "pcktgal2": case "pcktgal2j": case "spool3": case "spool3i": Dataeast.palette_init_pcktgal(Dataeast.prom); break; } break; case "Tehkan": screenstate.width = 0x100; screenstate.height = 0x100; screenstate.visarea.min_x = 0; screenstate.visarea.max_x = 0xff; screenstate.visarea.min_y = 0x10; screenstate.visarea.max_y = 0xef; fullwidth = 0x100; fullheight = 0x100; frame_update_time = new Atime(0, (long)(1e18 / 60)); screenstate.vblank_period = 0; video_attributes = 0; Motion.motion_update_callback = Motion.ui_updateTehkan; bitmapbase = new ushort[2][]; bitmapbase[0] = new ushort[0x100 * 0x100]; bitmapbase[1] = new ushort[0x100 * 0x100]; video_update_callback = Tehkan.video_update_pbaction; video_eof_callback = Tehkan.video_eof_pbaction; break; case "Neo Geo": screenstate.width = 384; screenstate.height = 264; screenstate.visarea.min_x = NEOGEO_HBEND;//30 screenstate.visarea.max_x = NEOGEO_HBSTART - 1;//349 screenstate.visarea.min_y = NEOGEO_VBEND;//16 screenstate.visarea.max_y = NEOGEO_VBSTART - 1;//239 fullwidth = 384; fullheight = 264; frame_update_time = new Atime(0, (long)(1e18 / 6000000) * screenstate.width * screenstate.height);//59.1856060608428Hz screenstate.vblank_period = (long)(1e18 / 6000000) * 384 * (264 - 224); video_attributes = 0; Motion.motion_update_callback = Motion.ui_updateN; bitmapbaseN = new int[2][]; bitmapbaseN[0] = new int[384 * 264]; bitmapbaseN[1] = new int[384 * 264]; video_update_callback = Neogeo.video_update_neogeo; video_eof_callback = Neogeo.video_eof_neogeo; break; case "SunA8": screenstate.width = 0x100; screenstate.height = 0x100; screenstate.visarea.min_x = 0; screenstate.visarea.min_x = 0xff; screenstate.visarea.min_y = 0x10; screenstate.visarea.max_y = 0xef; fullwidth = 0x100; fullheight = 0x100; frame_update_time = new Atime(0, (long)(1e18 / 60)); screenstate.vblank_period = (long)(1e12 * 2500); video_attributes = 0; Motion.motion_update_callback = Motion.ui_updatePGM; bitmapbase = new ushort[2][]; bitmapbase[0] = new ushort[0x100 * 0x100]; bitmapbase[1] = new ushort[0x100 * 0x100]; video_update_callback = SunA8.video_update_suna8; video_eof_callback = SunA8.video_eof_suna8; break; case "Namco System 1": screenstate.width = 0x200; screenstate.height = 0x200; screenstate.visarea.min_x = 0; screenstate.visarea.max_x = 0x1ff; screenstate.visarea.min_y = 0; screenstate.visarea.max_y = 0x1ff; fullwidth = 0x200; fullheight = 0x200; frame_update_time = new Atime(0, (long)(1e18 / 60.606060)); screenstate.vblank_period = 0; video_attributes = 0; Motion.motion_update_callback = Motion.ui_updateNa; bitmapbase = new ushort[2][]; bitmapbase[0] = new ushort[0x200 * 0x200]; bitmapbase[1] = new ushort[0x200 * 0x200]; video_update_callback = Namcos1.video_update_namcos1; video_eof_callback = Namcos1.video_eof_namcos1; break; case "IGS011": screenstate.width = 0x200; screenstate.height = 0x100; screenstate.visarea.min_x = 0; screenstate.visarea.max_x = 0x1ff; screenstate.visarea.min_y = 0; screenstate.visarea.max_y = 0xef; fullwidth = 0x200; fullheight = 0x200; frame_update_time = new Atime(0, (long)(1e18 / 60)); screenstate.vblank_period = 0; video_attributes = 0; Motion.motion_update_callback = Motion.ui_updateIGS011; bitmapbase = new ushort[2][]; bitmapbase[0] = new ushort[0x200 * 0x200]; bitmapbase[1] = new ushort[0x200 * 0x200]; video_update_callback = IGS011.video_update_igs011; video_eof_callback = IGS011.video_eof_igs011; break; case "PGM": screenstate.width = 0x200; screenstate.height = 0x200; screenstate.visarea.min_x = 0; screenstate.visarea.max_x = 0x1bf; screenstate.visarea.min_y = 0; screenstate.visarea.max_y = 0xdf; fullwidth = 0x200; fullheight = 0x200; frame_update_time = new Atime(0, (long)(1e18 / 60)); screenstate.vblank_period = 0; video_attributes = 0; Motion.motion_update_callback = Motion.ui_updatePGM; bitmapbase = new ushort[2][]; bitmapbase[0] = new ushort[0x200 * 0x200]; bitmapbase[1] = new ushort[0x200 * 0x200]; video_update_callback = PGM.video_update_pgm; video_eof_callback = PGM.video_eof_pgm; break; case "M72": screenstate.width = 0x200; screenstate.height = 0x11c; screenstate.visarea.min_x = 0x40; screenstate.visarea.max_x = 0x1bf; screenstate.visarea.min_y = 0; screenstate.visarea.max_y = 0xff; fullwidth = 0x200; fullheight = 0x200; frame_update_time = new Atime(0, (long)(1e18 / 8000000) * screenstate.width * screenstate.height); screenstate.vblank_period = (long)(1e18 / 8000000) * 512 * (284 - 256); video_attributes = 0; Motion.motion_update_callback = Motion.ui_updatePGM; bitmapbase = new ushort[2][]; bitmapbase[0] = new ushort[0x200 * 0x200];//0x11c bitmapbase[1] = new ushort[0x200 * 0x200];//0x11c video_update_callback = M72.video_update_m72; video_eof_callback = M72.video_eof_m72; break; case "M92": screenstate.width = 0x200; screenstate.height = 0x100; screenstate.visarea.min_x = 0x50; screenstate.visarea.max_x = 0x18f; screenstate.visarea.min_y = 0x8; screenstate.visarea.max_y = 0xf7; fullwidth = 0x200; fullheight = 0x200; frame_update_time = new Atime(0, (long)(1e18 / 60)); screenstate.vblank_period = 0; video_attributes = 0; Motion.motion_update_callback = Motion.ui_updatePGM; bitmapbase = new ushort[2][]; bitmapbase[0] = new ushort[0x200 * 0x200]; bitmapbase[1] = new ushort[0x200 * 0x200]; video_update_callback = M92.video_update_m92; video_eof_callback = M92.video_eof_m92; break; case "Taito": video_attributes = 0; Motion.motion_update_callback = Motion.ui_updatePGM; switch (Machine.sName) { case "tokio": case "tokioo": case "tokiou": case "tokiob": case "bublbobl": case "bublbobl1": case "bublboblr": case "bublboblr1": case "boblbobl": case "sboblbobl": case "sboblbobla": case "sboblboblb": case "sboblbobld": case "sboblboblc": case "bub68705": case "dland": case "bbredux": case "bublboblb": case "bublcave": case "boblcave": case "bublcave11": case "bublcave10": screenstate.width = 0x100; screenstate.height = 0x100; screenstate.visarea.min_x = 0; screenstate.visarea.max_x = 255; screenstate.visarea.min_y = 16; screenstate.visarea.max_y = 240 - 1; fullwidth = 0x100; fullheight = 0x100; frame_update_time = new Atime(0, 0x003c372a18883411); screenstate.vblank_period = 0;// (long)(1e18 * 0.00256); bitmapbase = new ushort[2][]; bitmapbase[0] = new ushort[0x100 * 0x100]; bitmapbase[1] = new ushort[0x100 * 0x100]; video_update_callback = Taito.video_update_bublbobl; video_eof_callback = Taito.video_eof_taito; break; case "opwolf": case "opwolfa": case "opwolfj": case "opwolfu": case "opwolfb": case "opwolfp": screenstate.width = 0x140; screenstate.height = 0x100; screenstate.visarea.min_x = 0; screenstate.visarea.max_x = 0x13f; screenstate.visarea.min_y = 8; screenstate.visarea.max_y = 0xf7; fullwidth = 0x140; fullheight = 0x100; frame_update_time = new Atime(0, (long)(1e18 / 60)); screenstate.vblank_period = 0;// (long)(1e18 * 0.00256); bitmapbase = new ushort[2][]; bitmapbase[0] = new ushort[0x140 * 0x100]; bitmapbase[1] = new ushort[0x140 * 0x100]; video_update_callback = Taito.video_update_opwolf; video_eof_callback = Taito.video_eof_taito; break; } break; case "Taito B": screenstate.width = 0x200; screenstate.height = 0x100; screenstate.visarea.min_x = 0;//0 screenstate.visarea.max_x = 320 - 1;//319 screenstate.visarea.min_y = 16;//16 screenstate.visarea.max_y = 240 - 1;//239 fullwidth = 0x200; fullheight = 0x100; frame_update_time = new Atime(0, (long)(1e18 / 60)); screenstate.vblank_period = 0; video_attributes = 0; Motion.motion_update_callback = Motion.ui_updatePGM; bitmapbase = new ushort[2][]; bitmapbase[0] = new ushort[0x200 * 0x100]; bitmapbase[1] = new ushort[0x200 * 0x100]; video_update_callback = Taitob.video_update_taitob; video_eof_callback = Taitob.video_eof_taitob; break; case "Konami 68000": screenstate.width = 0x200; screenstate.height = 0x100; fullwidth = 0x200; fullheight = 0x100; frame_update_time = new Atime(0, (long)(1e18 / 60)); screenstate.vblank_period = (long)(1e12 * 2500); video_attributes = 0x34; Motion.motion_update_callback = Motion.ui_updatePGM; bitmapbase = new ushort[2][]; bitmapbase[0] = new ushort[0x200 * 0x100]; bitmapbase[1] = new ushort[0x200 * 0x100]; video_eof_callback = Konami68000.video_eof; switch (Machine.sName) { case "cuebrick": screenstate.visarea.min_x = 0x68; screenstate.visarea.max_x = 0x197; screenstate.visarea.min_y = 0x10; screenstate.visarea.max_y = 0xef; video_update_callback = Konami68000.video_update_mia; break; case "mia": case "mia2": screenstate.visarea.min_x = 0x68; screenstate.visarea.max_x = 0x197; screenstate.visarea.min_y = 0x10; screenstate.visarea.max_y = 0xef; video_attributes = 0x30; video_update_callback = Konami68000.video_update_mia; break; case "tmnt": case "tmntu": case "tmntua": case "tmntub": case "tmht": case "tmhta": case "tmhtb": case "tmntj": case "tmnta": case "tmht2p": case "tmht2pa": case "tmnt2pj": case "tmnt2po": screenstate.visarea.min_x = 0x60; screenstate.visarea.max_x = 0x19f; screenstate.visarea.min_y = 0x10; screenstate.visarea.max_y = 0xef; video_attributes = 0x30; video_update_callback = Konami68000.video_update_mia; break; case "punkshot": case "punkshot2": case "punkshotj": screenstate.visarea.min_x = 0x70; screenstate.visarea.max_x = 0x18f; screenstate.visarea.min_y = 0x10; screenstate.visarea.max_y = 0xef; video_update_callback = Konami68000.video_update_punkshot; break; case "lgtnfght": case "lgtnfghta": case "lgtnfghtu": case "trigon": screenstate.visarea.min_x = 0x60; screenstate.visarea.max_x = 0x19f; screenstate.visarea.min_y = 0x10; screenstate.visarea.max_y = 0xef; video_update_callback = Konami68000.video_update_lgtnfght; break; case "blswhstl": case "blswhstla": case "detatwin": screenstate.visarea.min_x = 0x60; screenstate.visarea.max_x = 0x19f; screenstate.visarea.min_y = 0x10; screenstate.visarea.max_y = 0xef; video_update_callback = Konami68000.video_update_lgtnfght; video_eof_callback = Konami68000.video_eof_blswhstl; break; case "glfgreat": case "glfgreatj": case "prmrsocr": case "prmrsocrj": screenstate.visarea.min_x = 0x70; screenstate.visarea.max_x = 0x18f; screenstate.visarea.min_y = 0x10; screenstate.visarea.max_y = 0xef; video_update_callback = Konami68000.video_update_glfgreat; break; case "tmnt2": case "tmnt2a": case "tmht22pe": case "tmht24pe": case "tmnt22pu": case "qgakumon": screenstate.visarea.min_x = 0x68; screenstate.visarea.max_x = 0x197; screenstate.visarea.min_y = 0x10; screenstate.visarea.max_y = 0xef; video_update_callback = Konami68000.video_update_tmnt2; break; case "ssriders": case "ssriderseaa": case "ssridersebd": case "ssridersebc": case "ssridersuda": case "ssridersuac": case "ssridersuab": case "ssridersubc": case "ssridersadd": case "ssridersabd": case "ssridersjad": case "ssridersjac": case "ssridersjbd": screenstate.visarea.min_x = 0x70; screenstate.visarea.max_x = 0x18f; screenstate.visarea.min_y = 0x10; screenstate.visarea.max_y = 0xef; video_update_callback = Konami68000.video_update_tmnt2; break; case "thndrx2": case "thndrx2a": case "thndrx2j": screenstate.visarea.min_x = 0x70; screenstate.visarea.max_x = 0x18f; screenstate.visarea.min_y = 0x10; screenstate.visarea.max_y = 0xef; video_attributes = 0x30; video_update_callback = Konami68000.video_update_thndrx2; break; } break; case "Capcom": switch (Machine.sName) { case "gng": case "gnga": case "gngbl": case "gngprot": case "gngblita": case "gngc": case "gngt": case "makaimur": case "makaimurc": case "makaimurg": case "diamond": screenstate.width = 0x100; screenstate.height = 0x100; screenstate.visarea.min_x = 0; screenstate.visarea.max_x = 0xff; screenstate.visarea.min_y = 0x10; screenstate.visarea.max_y = 0xef; fullwidth = 0x100; fullheight = 0x100; frame_update_time = new Atime(0, (long)(1e18 / 60)); screenstate.vblank_period = 0; video_attributes = 0; Motion.motion_update_callback = Motion.ui_updatePGM; bitmapbase = new ushort[2][]; bitmapbase[0] = new ushort[0x100 * 0x100]; bitmapbase[1] = new ushort[0x100 * 0x100]; video_update_callback = Capcom.video_update_gng; video_eof_callback = Capcom.video_eof_gng; break; case "sf": case "sfua": case "sfj": case "sfjan": case "sfan": case "sfp": screenstate.width = 0x200; screenstate.height = 0x100; screenstate.visarea.min_x = 0x40; screenstate.visarea.max_x = 0x1bf; screenstate.visarea.min_y = 0x10; screenstate.visarea.max_y = 0xef; fullwidth = 0x200; fullheight = 0x100; frame_update_time = new Atime(0, (long)(1e18 / 60)); screenstate.vblank_period = 0; video_attributes = 0; Motion.motion_update_callback = Motion.ui_updatePGM; bitmapbase = new ushort[2][]; bitmapbase[0] = new ushort[0x200 * 0x100]; bitmapbase[1] = new ushort[0x200 * 0x100]; video_update_callback = Capcom.video_update_sf; video_eof_callback = Capcom.video_eof; break; } break; } screenstate.frame_period = frame_update_time.attoseconds; screenstate.scantime = screenstate.frame_period / screenstate.height; screenstate.pixeltime = screenstate.frame_period / (screenstate.height * screenstate.width); screenstate.frame_number = 0; /** bitmapbase的指针管理 **/ // 释放句柄 if (bitmapbase_handles != null) { for (int i = 0; i < bitmapbase_handles.Length; i++) { if (bitmapbase_handles[i].IsAllocated) bitmapbase_handles[i].Free(); } bitmapbase_handles = null; bitmapbase_Ptrs = null; } if (bitmapbase != null) { bitmapbase_handles = new GCHandle[bitmapbase.Length]; bitmapbase_Ptrs = new ushort*[bitmapbase.Length]; for (int i = 0; i < bitmapbase.Length; i++) { bitmapbase_handles[i] = GCHandle.Alloc(bitmapbase[i], GCHandleType.Pinned); bitmapbase_Ptrs[i] = (ushort*)bitmapbase_handles[i].AddrOfPinnedObject(); } } if (bitmapbaseN_handles != null) { for (int i = 0; i < bitmapbaseN_handles.Length; i++) { if (bitmapbaseN_handles[i].IsAllocated) bitmapbaseN_handles[i].Free(); } bitmapbaseN_handles = null; bitmapbaseN_Ptrs = null; } if (bitmapbaseN != null) { bitmapbaseN_handles = new GCHandle[bitmapbaseN.Length]; bitmapbaseN_Ptrs = new int*[bitmapbaseN.Length]; for (int i = 0; i < bitmapbaseN.Length; i++) { bitmapbaseN_handles[i] = GCHandle.Alloc(bitmapbaseN[i], GCHandleType.Pinned); bitmapbaseN_Ptrs[i] = (int*)bitmapbaseN_handles[i].AddrOfPinnedObject(); } } /** end **/ //bitmapcolor = new int[Video.fullwidth * Video.fullheight]; /** bitmapcolor的指针管理 **/ //不再拷贝完整画布 //if (bitmapcolor != null) //{ // // 释放句柄 // if (bitmapcolor_handle.IsAllocated) // { // bitmapcolor_handle.Free(); // } //} //bitmapcolor = new int[Video.fullwidth * Video.fullheight]; //// 固定数组,防止垃圾回收器移动它 //bitmapcolor_handle = GCHandle.Alloc(bitmapcolor, GCHandleType.Pinned); //// 获取数组的指针 //bitmapcolor_Ptr = bitmapcolor_handle.AddrOfPinnedObject(); // 释放句柄 if (bitmapcolorRect != null && bitmapcolorRect_handle.IsAllocated) bitmapcolorRect_handle.Free(); bitmapcolorRect = new int[width * height]; // 固定数组,防止垃圾回收器移动它 bitmapcolorRect_handle = GCHandle.Alloc(bitmapcolorRect, GCHandleType.Pinned); // 获取数组的指针 bitmapcolorRect_Ptr = bitmapcolorRect_handle.AddrOfPinnedObject(); bitmapcolorRect_Ptrunsafe = (int*)bitmapcolorRect_Ptr; /** end **/ vblank_begin_timer = EmuTimer.timer_alloc_common(EmuTimer.TIME_ACT.Video_vblank_begin_callback, false); EmuTimer.timer_adjust_periodic(vblank_begin_timer, video_screen_get_time_until_vblank_start(), Attotime.ATTOTIME_NEVER); scanline0_timer = EmuTimer.timer_alloc_common(EmuTimer.TIME_ACT.Video_scanline0_callback, false); EmuTimer.timer_adjust_periodic(scanline0_timer, video_screen_get_time_until_pos(0, 0), Attotime.ATTOTIME_NEVER); vblank_end_timer = EmuTimer.timer_alloc_common(EmuTimer.TIME_ACT.Video_vblank_end_callback, false); switch (Machine.sBoard) { case "CPS-1": case "CPS-1(QSound)": case "Namco System 1": case "M92": case "Taito B": break; case "CPS2": Cpuexec.cpu[0].partial_frame_period = Attotime.attotime_div(Video.frame_update_time, 262); Cpuexec.cpu[0].partial_frame_timer = EmuTimer.timer_alloc_common(EmuTimer.TIME_ACT.Cpuexec_trigger_partial_frame_interrupt, false); break; case "Tehkan": Cpuexec.cpu[1].partial_frame_period = Attotime.attotime_div(Video.frame_update_time, 2); Cpuexec.cpu[1].partial_frame_timer = EmuTimer.timer_alloc_common(EmuTimer.TIME_ACT.Cpuexec_trigger_partial_frame_interrupt, false); break; case "Neo Geo": break; case "SunA8": Cpuexec.cpu[0].partial_frame_period = Attotime.attotime_div(Video.frame_update_time, 0x100); Cpuexec.cpu[0].partial_frame_timer = EmuTimer.timer_alloc_common(EmuTimer.TIME_ACT.Cpuexec_trigger_partial_frame_interrupt, false); Cpuexec.cpu[1].partial_frame_period = Attotime.attotime_div(Video.frame_update_time, 4); Cpuexec.cpu[1].partial_frame_timer = EmuTimer.timer_alloc_common(EmuTimer.TIME_ACT.Cpuexec_trigger2, false); break; case "IGS011": switch (Machine.sName) { case "drgnwrld": case "drgnwrldv30": case "drgnwrldv21": case "drgnwrldv21j": case "drgnwrldv20j": case "drgnwrldv10c": case "drgnwrldv11h": case "drgnwrldv40k": case "lhb2": Cpuexec.cpu[0].partial_frame_period = Attotime.attotime_div(Video.frame_update_time, 5); Cpuexec.cpu[0].partial_frame_timer = EmuTimer.timer_alloc_common(EmuTimer.TIME_ACT.Cpuexec_trigger_partial_frame_interrupt, false); break; case "lhb": case "lhbv33c": case "dbc": case "ryukobou": Cpuexec.cpu[0].partial_frame_period = Attotime.attotime_div(Video.frame_update_time, 4); Cpuexec.cpu[0].partial_frame_timer = EmuTimer.timer_alloc_common(EmuTimer.TIME_ACT.Cpuexec_trigger_partial_frame_interrupt, false); break; } break; case "M72": Cpuexec.cpu[1].partial_frame_period = Attotime.attotime_div(Video.frame_update_time, 128); Cpuexec.cpu[1].partial_frame_timer = EmuTimer.timer_alloc_common(EmuTimer.TIME_ACT.Cpuexec_trigger_partial_frame_interrupt, false); break; case "Taito": switch (Machine.sName) { case "bub68705": Cpuexec.cpu[3].partial_frame_period = Attotime.attotime_div(Video.frame_update_time, 2); Cpuexec.cpu[3].partial_frame_timer = EmuTimer.timer_alloc_common(EmuTimer.TIME_ACT.Cpuexec_trigger_partial_frame_interrupt, false); break; } break; case "Konami 68000": switch (Machine.sName) { case "cuebrick": Cpuexec.cpu[0].partial_frame_period = Attotime.attotime_div(Video.frame_update_time, 10); Cpuexec.cpu[0].partial_frame_timer = EmuTimer.timer_alloc_common(EmuTimer.TIME_ACT.Cpuexec_trigger_partial_frame_interrupt, false); break; } break; case "Capcom": switch (Machine.sName) { case "gng": case "gnga": case "gngbl": case "gngprot": case "gngblita": case "gngc": case "gngt": case "makaimur": case "makaimurc": case "makaimurg": case "diamond": Cpuexec.cpu[1].partial_frame_period = Attotime.attotime_div(Video.frame_update_time, 4); Cpuexec.cpu[1].partial_frame_timer = EmuTimer.timer_alloc_common(EmuTimer.TIME_ACT.Cpuexec_trigger_partial_frame_interrupt, false); break; } break; } screenstate.vblank_start_time = Attotime.ATTOTIME_ZERO; screenstate.vblank_end_time = new Atime(0, screenstate.vblank_period); } public static void video_screen_configure(int width, int height, RECT visarea, long frame_period) { screenstate.width = width; screenstate.height = height; screenstate.visarea = visarea; //realloc_screen_bitmaps(screen); screenstate.frame_period = frame_period; screenstate.scantime = frame_period / height; screenstate.pixeltime = frame_period / (height * width); /*if (config->vblank == 0 && !config->oldstyle_vblank_supplied) state->vblank_period = state->scantime * (height - (visarea->max_y + 1 - visarea->min_y)); else state->vblank_period = config->vblank; if (video_screen_get_vpos(screen) == 0) timer_adjust_oneshot(state->scanline0_timer, attotime_zero, 0); else timer_adjust_oneshot(state->scanline0_timer, video_screen_get_time_until_pos(screen, 0, 0), 0); timer_adjust_oneshot(state->vblank_begin_timer, video_screen_get_time_until_vblank_start(screen), 0); update_refresh_speed(screen->machine);*/ } public static bool video_screen_update_partial(int scanline) { new_clip = screenstate.visarea; bool result = false; if (screenstate.last_partial_scan > new_clip.min_y) { new_clip.min_y = screenstate.last_partial_scan; } if (scanline < new_clip.max_y) { new_clip.max_y = scanline; } if (new_clip.min_y <= new_clip.max_y) { video_update_callback(); result = true; } screenstate.last_partial_scan = scanline + 1; return result; } public static int video_screen_get_vpos() { long delta = Attotime.attotime_to_attoseconds(Attotime.attotime_sub(EmuTimer.get_current_time(), screenstate.vblank_start_time)); int vpos; delta += screenstate.pixeltime / 2; vpos = (int)(delta / screenstate.scantime); return (screenstate.visarea.max_y + 1 + vpos) % screenstate.height; } public static bool video_screen_get_vblank() { return (Attotime.attotime_compare(EmuTimer.get_current_time(), screenstate.vblank_end_time) < 0); } public static Atime video_screen_get_time_until_pos(int vpos, int hpos) { long curdelta = Attotime.attotime_to_attoseconds(Attotime.attotime_sub(EmuTimer.get_current_time(), screenstate.vblank_start_time)); long targetdelta; vpos += screenstate.height - (screenstate.visarea.max_y + 1); vpos %= screenstate.height; targetdelta = vpos * screenstate.scantime + hpos * screenstate.pixeltime; if (targetdelta <= curdelta + screenstate.pixeltime / 2) targetdelta += screenstate.frame_period; while (targetdelta <= curdelta) targetdelta += screenstate.frame_period; return new Atime(0, targetdelta - curdelta); } public static Atime video_screen_get_time_until_vblank_start() { return video_screen_get_time_until_pos(Video.screenstate.visarea.max_y + 1, 0); } public static Atime video_screen_get_time_until_vblank_end() { Atime ret; Atime current_time = EmuTimer.get_current_time(); if (video_screen_get_vblank()) { ret = Attotime.attotime_sub(screenstate.vblank_end_time, current_time); } else { ret = Attotime.attotime_sub(Attotime.attotime_add_attoseconds(screenstate.vblank_end_time, screenstate.frame_period), current_time); } return ret; } private static bool effective_throttle() { // if (mame_is_paused(machine) || ui_is_menu_active()) // return true; // if (global.fastforward) // return false; return global_throttle; } public static void vblank_begin_callback() { screenstate.vblank_start_time = EmuTimer.global_basetime;// Timer.get_current_time(); screenstate.vblank_end_time = Attotime.attotime_add_attoseconds(screenstate.vblank_start_time, screenstate.vblank_period); Cpuexec.on_vblank(); //垂直同步 if ((video_attributes & VIDEO_UPDATE_AFTER_VBLANK) == 0) { video_frame_update(); } EmuTimer.timer_adjust_periodic(vblank_begin_timer, video_screen_get_time_until_vblank_start(), Attotime.ATTOTIME_NEVER); if (screenstate.vblank_period == 0) { vblank_end_callback(); } else { EmuTimer.timer_adjust_periodic(vblank_end_timer, video_screen_get_time_until_vblank_end(), Attotime.ATTOTIME_NEVER); } } public static void vblank_end_callback() { int i; //垂直同步 if ((video_attributes & VIDEO_UPDATE_AFTER_VBLANK) != 0) { video_frame_update(); } } public static void scanline0_callback() { screenstate.last_partial_scan = 0; EmuTimer.timer_adjust_periodic(scanline0_timer, video_screen_get_time_until_pos(0, 0), Attotime.ATTOTIME_NEVER); } public static void scanline_update_callback() { int scanline = scanline_param; video_screen_update_partial(scanline); scanline++; if (scanline > screenstate.visarea.max_y) { scanline = screenstate.visarea.min_y; } scanline_param = scanline; EmuTimer.timer_adjust_periodic(scanline_timer, video_screen_get_time_until_pos(scanline, 0), Attotime.ATTOTIME_NEVER); } public static void video_frame_update() { Atime current_time = EmuTimer.global_basetime; if (!Mame.paused) { finish_screen_updates(); } Keyboard.Update(); Mouse.Update(); Inptport.frame_update_callback(); Motion.ui_update_and_render(); //if (Machine.mainMotion.cheatmotion.lockState == CheatMotion.LockState.LOCK_FRAME) //{ // Machine.mainMotion.cheatmotion.ApplyCheat(); //} GDIDraw(); if (effective_throttle()) { //不执行该函数,避免Thread.Sleep (暂时确认无逻辑依赖) //废弃 //update_throttle(current_time); } Window.osd_update(false); //UI.ui_input_frame_update(); recompute_speed(current_time); if (Mame.paused) { //Thread.Sleep(5); } else { video_eof_callback(); } } private static void finish_screen_updates() { video_screen_update_partial(screenstate.visarea.max_y); curbitmap = 1 - curbitmap; } //废弃 //private static void update_throttle(Atime emutime) //{ // long real_delta_attoseconds; // long emu_delta_attoseconds; // long real_is_ahead_attoseconds; // long attoseconds_per_tick; // long ticks_per_second; // long target_ticks; // long diff_ticks; // ticks_per_second = Wintime.ticks_per_second; // attoseconds_per_tick = Attotime.ATTOSECONDS_PER_SECOND / ticks_per_second; // if (Mame.mame_is_paused()) // { // throttle_emutime = Attotime.attotime_sub_attoseconds(emutime, Attotime.ATTOSECONDS_PER_SECOND / PAUSED_REFRESH_RATE); // throttle_realtime = throttle_emutime; // } // emu_delta_attoseconds = Attotime.attotime_to_attoseconds(Attotime.attotime_sub(emutime, throttle_emutime)); // if (emu_delta_attoseconds < 0 || emu_delta_attoseconds > Attotime.ATTOSECONDS_PER_SECOND / 10) // { // goto resync; // } // diff_ticks = Wintime.osd_ticks() - throttle_last_ticks; // throttle_last_ticks += diff_ticks; // if (diff_ticks >= ticks_per_second) // { // goto resync; // } // real_delta_attoseconds = diff_ticks * attoseconds_per_tick; // throttle_emutime = emutime; // throttle_realtime = Attotime.attotime_add_attoseconds(throttle_realtime, real_delta_attoseconds); // throttle_history = (throttle_history << 1) | Convert.ToUInt32(emu_delta_attoseconds > real_delta_attoseconds); // real_is_ahead_attoseconds = Attotime.attotime_to_attoseconds(Attotime.attotime_sub(throttle_emutime, throttle_realtime)); // if ((real_is_ahead_attoseconds < -Attotime.ATTOSECONDS_PER_SECOND / 10) || (real_is_ahead_attoseconds < 0 && popcount[throttle_history & 0xff] < 6)) // { // goto resync; // } // if (real_is_ahead_attoseconds < 0) // { // return; // } // target_ticks = throttle_last_ticks + real_is_ahead_attoseconds / attoseconds_per_tick; // diff_ticks = throttle_until_ticks(target_ticks) - throttle_last_ticks; // throttle_last_ticks += diff_ticks; // throttle_realtime = Attotime.attotime_add_attoseconds(throttle_realtime, diff_ticks * attoseconds_per_tick); // return; //resync: // throttle_realtime = throttle_emutime = emutime; //} //废弃 //private static long throttle_until_ticks(long target_ticks) //{ // long minimum_sleep = Wintime.ticks_per_second / 1000; // long current_ticks = Wintime.osd_ticks(); // long new_ticks; // while (current_ticks < target_ticks) // { // long delta; // bool slept = false; // delta = (target_ticks - current_ticks) * 1000 / (1000 + average_oversleep); // if (delta >= minimum_sleep) // { // Wintime.osd_sleep(delta); // slept = true; // } // new_ticks = Wintime.osd_ticks(); // if (slept) // { // long actual_ticks = new_ticks - current_ticks; // if (actual_ticks > delta) // { // long oversleep_milliticks = 1000 * (actual_ticks - delta) / delta; // average_oversleep = (average_oversleep * 99 + oversleep_milliticks) / 100; // } // } // current_ticks = new_ticks; // } // return current_ticks; //} private static void recompute_speed(Atime emutime) { long delta_emutime; if (speed_last_realtime == 0 || Mame.mame_is_paused()) { speed_last_realtime = Wintime.osd_ticks(); speed_last_emutime = emutime; } delta_emutime = Attotime.attotime_to_attoseconds(Attotime.attotime_sub(emutime, speed_last_emutime)); if (delta_emutime > Attotime.ATTOSECONDS_PER_SECOND / 4) { long realtime = Wintime.osd_ticks(); long delta_realtime = realtime - speed_last_realtime; long tps = Wintime.ticks_per_second; speed_percent = (double)delta_emutime * (double)tps / ((double)delta_realtime * (double)Attotime.ATTOSECONDS_PER_SECOND); speed_last_realtime = realtime; speed_last_emutime = emutime; overall_valid_counter++; if (overall_valid_counter >= 4) { overall_real_ticks += delta_realtime; while (overall_real_ticks >= tps) { overall_real_ticks -= tps; overall_real_seconds++; } overall_emutime = Attotime.attotime_add_attoseconds(overall_emutime, delta_emutime); } } } public static void flip_screen_set_no_update(bool on) { flip_screen_x = on; } public static bool flip_screen_get() { return flip_screen_x; } public static void SaveStateBinary(BinaryWriter writer) { writer.Write(scanline_param); writer.Write(screenstate.last_partial_scan); writer.Write(screenstate.vblank_start_time.seconds); writer.Write(screenstate.vblank_start_time.attoseconds); writer.Write(screenstate.vblank_end_time.seconds); writer.Write(screenstate.vblank_end_time.attoseconds); writer.Write(screenstate.frame_number); } public static void LoadStateBinary(BinaryReader reader) { scanline_param = reader.ReadInt32(); screenstate.last_partial_scan = reader.ReadInt32(); screenstate.vblank_start_time.seconds = reader.ReadInt32(); screenstate.vblank_start_time.attoseconds = reader.ReadInt64(); screenstate.vblank_end_time.seconds = reader.ReadInt32(); screenstate.vblank_end_time.attoseconds = reader.ReadInt64(); screenstate.frame_number = reader.ReadInt64(); } } }