private void UpdateVideo() { BufferWidth = LibMAME.mame_lua_get_int(MAMELuaCommand.GetWidth); BufferHeight = LibMAME.mame_lua_get_int(MAMELuaCommand.GetHeight); int expectedSize = BufferWidth * BufferHeight; int bytesPerPixel = 4; IntPtr ptr = LibMAME.mame_lua_get_string(MAMELuaCommand.GetPixels, out var lengthInBytes); if (ptr == IntPtr.Zero) { Console.WriteLine("LibMAME ERROR: frame buffer pointer is null"); return; } if (expectedSize * bytesPerPixel != lengthInBytes) { Console.WriteLine( "LibMAME ERROR: frame buffer has wrong size\n" + $"width: { BufferWidth } pixels\n" + $"height: { BufferHeight } pixels\n" + $"expected: { expectedSize * bytesPerPixel } bytes\n" + $"received: { lengthInBytes } bytes\n"); return; } _frameBuffer = new int[expectedSize]; Marshal.Copy(ptr, _frameBuffer, 0, expectedSize); if (!LibMAME.mame_lua_free_string(ptr)) { Console.WriteLine("LibMAME ERROR: frame buffer wasn't freed"); } }
private void InitMemoryDomains() { var domains = new List <MemoryDomain>(); _systemBusAddressShift = LibMAME.mame_lua_get_int(MAMELuaCommand.GetSpaceAddressShift); var dataWidth = LibMAME.mame_lua_get_int(MAMELuaCommand.GetSpaceDataWidth) >> 3; // mame returns in bits var size = (long)LibMAME.mame_lua_get_double(MAMELuaCommand.GetSpaceAddressMask) + dataWidth; var endianString = MameGetString(MAMELuaCommand.GetSpaceEndianness); var deviceName = MameGetString(MAMELuaCommand.GetMainCPUName); //var addrSize = (size * 2).ToString(); MemoryDomain.Endian endian = MemoryDomain.Endian.Unknown; if (endianString == "little") { endian = MemoryDomain.Endian.Little; } else if (endianString == "big") { endian = MemoryDomain.Endian.Big; } var mapCount = LibMAME.mame_lua_get_int(MAMELuaCommand.GetSpaceMapCount); for (int i = 1; i <= mapCount; i++) { var read = MameGetString($"return { MAMELuaCommand.SpaceMap }[{ i }].readtype"); var write = MameGetString($"return { MAMELuaCommand.SpaceMap }[{ i }].writetype"); if (read == "ram" && write == "ram" || read == "rom") { var firstOffset = LibMAME.mame_lua_get_int($"return { MAMELuaCommand.SpaceMap }[{ i }].offset"); var lastOffset = LibMAME.mame_lua_get_int($"return { MAMELuaCommand.SpaceMap }[{ i }].endoff"); var name = $"{ deviceName } : { read } : 0x{ firstOffset:X}-0x{ lastOffset:X}"; domains.Add(new MemoryDomainDelegate(name, lastOffset - firstOffset + 1, endian, delegate(long addr) { return(_peek(addr, firstOffset, size)); }, read == "rom" ? (Action <long, byte>) null : delegate(long addr, byte val) { _poke(addr, val, firstOffset, size); }, dataWidth)); } } domains.Add(new MemoryDomainDelegate(deviceName + " : System Bus", size, endian, delegate(long addr) { return(_peek(addr, 0, size)); }, null, dataWidth)); _memoryDomains = new MemoryDomainList(domains); (ServiceProvider as BasicServiceProvider).Register <IMemoryDomains>(_memoryDomains); }
private void MAMEBootCallback() { LibMAME.mame_lua_execute(MAMELuaCommand.Pause); CheckVersions(); GetInputFields(); UpdateVideo(); UpdateAspect(); UpdateFramerate(); UpdateGameName(); InitMemoryDomains(); int length = LibMAME.mame_lua_get_int("return string.len(manager:machine():buffer_save())"); _mameSaveBuffer = new byte[length]; _hawkSaveBuffer = new byte[length + 4 + 4 + 4 + 1]; _mameStartupComplete.Set(); }
private byte _peek(long addr, int firstOffset, long size) { if (addr < 0 || addr >= size) { throw new ArgumentOutOfRangeException(); } if (!_memAccess) { _memAccess = true; _mamePeriodicComplete.WaitOne(); } addr += firstOffset; var val = (byte)LibMAME.mame_lua_get_int($"{ MAMELuaCommand.GetSpace }:read_u8({ addr << _systemBusAddressShift })"); _memoryAccessComplete.Set(); return(val); }
/* * FrameAdvance() and MAME * * MAME fires the periodic callback on every video and debugger update, * which happens every VBlank and also repeatedly at certain time * intervals while paused. Since MAME's luaengine runs in a separate * thread, it's only safe to update everything we need per frame during * this callback, when it's explicitly waiting for further lua commands. * * If we disable throttling and pass -update_in_pause, there will be no * delay between video updates. This allows to run at full speed while * frame-stepping. * * MAME only captures new frame data once per VBlank, while unpaused. * But it doesn't have an exclusive VBlank callback we could attach to. * It has a LUA_ON_FRAME_DONE callback, but that fires even more * frequently and updates all sorts of other non-video stuff, and we * need none of that here. * * So we filter out all the calls that happen while paused (non-VBlank * updates). Then, when Hawk asks us to advance a frame, we virtually * unpause and declare the new frame unfinished. This informs MAME that * it should advance one frame internally. Hawk starts waiting for the * MAME thread to complete the request. * * After MAME's done advancing, it fires the periodic callback again. * That's when we update everything and declare the new frame finished, * filtering out any further updates again. Then we allow Hawk to * complete frame-advancing. */ private void MAMEPeriodicCallback() { if (exiting) { LibMAME.mame_lua_execute(MAMELuaCommand.Exit); exiting = false; } int MAMEFrame = LibMAME.mame_lua_get_int(MAMELuaCommand.GetFrameNumber); if (!paused) { LibMAME.mame_lua_execute(MAMELuaCommand.Step); frameDone = false; paused = true; } else if (!frameDone) { Update(); frameDone = true; MAMEFrameComplete.Set(); } }
public void FetchDefaultGameSettings() { string DIPSwitchTags = MameGetString(MAMELuaCommand.GetDIPSwitchTags); string[] tags = DIPSwitchTags.Split(new char[] { ';' }, StringSplitOptions.RemoveEmptyEntries); foreach (string tag in tags) { string DIPSwitchFields = MameGetString(MAMELuaCommand.GetDIPSwitchFields(tag)); string[] fieldNames = DIPSwitchFields.Split(new char[] { '^' }, StringSplitOptions.RemoveEmptyEntries); foreach (string fieldName in fieldNames) { DriverSetting setting = new DriverSetting() { Name = fieldName, GameName = _gameShortName, LuaCode = MAMELuaCommand.InputField(tag, fieldName), Type = SettingType.DIPSWITCH, DefaultValue = LibMAME.mame_lua_get_int( $"return { MAMELuaCommand.InputField(tag, fieldName) }.defvalue").ToString() }; string DIPSwitchOptions = MameGetString(MAMELuaCommand.GetDIPSwitchOptions(tag, fieldName)); string[] options = DIPSwitchOptions.Split(new char[] { '@' }, StringSplitOptions.RemoveEmptyEntries); foreach (string option in options) { string[] opt = option.Split(new char[] { '~' }, StringSplitOptions.RemoveEmptyEntries); setting.Options.Add(opt[0], opt[1]); } CurrentDriverSettings.Add(setting); } } }
private void InitMemoryDomains() { var domains = new List <MemoryDomain>(); _systemBusAddressShift = LibMAME.mame_lua_get_int(MAMELuaCommand.GetSpaceAddressShift); var size = (long)LibMAME.mame_lua_get_double(MAMELuaCommand.GetSpaceAddressMask) + 1; var dataWidth = LibMAME.mame_lua_get_int(MAMELuaCommand.GetSpaceDataWidth) >> 3; // mame returns in bits var endianString = MameGetString(MAMELuaCommand.GetSpaceEndianness); MemoryDomain.Endian endian = MemoryDomain.Endian.Unknown; if (endianString == "little") { endian = MemoryDomain.Endian.Little; } else if (endianString == "big") { endian = MemoryDomain.Endian.Big; } var mapCount = LibMAME.mame_lua_get_int(MAMELuaCommand.GetSpaceMapCount); for (int i = 1; i <= mapCount; i++) { var read = MameGetString($"return { MAMELuaCommand.SpaceMap }[{ i }].readtype"); var write = MameGetString($"return { MAMELuaCommand.SpaceMap }[{ i }].writetype"); if (read == "ram" && write == "ram" /* || read == "rom" */) { var firstOffset = LibMAME.mame_lua_get_int($"return { MAMELuaCommand.SpaceMap }[{ i }].offset"); var lastOffset = LibMAME.mame_lua_get_int($"return { MAMELuaCommand.SpaceMap }[{ i }].endoff"); var name = $"{ read.ToUpper() } { firstOffset:X}-{ lastOffset:X}"; domains.Add(new MemoryDomainDelegate(name, lastOffset - firstOffset + 1, endian, delegate(long addr) { if (addr < 0 || addr >= size) { throw new ArgumentOutOfRangeException(); } _memAccess = true; _mamePeriodicComplete.WaitOne(); addr += firstOffset; var val = (byte)LibMAME.mame_lua_get_int($"{ MAMELuaCommand.GetSpace }:read_u8({ addr << _systemBusAddressShift })"); _memoryAccessComplete.Set(); _memAccess = false; return(val); }, read == "rom" ? (Action <long, byte>) null : delegate(long addr, byte val) { if (addr < 0 || addr >= size) { throw new ArgumentOutOfRangeException(); } _memAccess = true; _mamePeriodicComplete.WaitOne(); addr += firstOffset; LibMAME.mame_lua_execute($"{ MAMELuaCommand.GetSpace }:write_u8({ addr << _systemBusAddressShift }, { val })"); _memoryAccessComplete.Set(); _memAccess = false; }, dataWidth)); } } domains.Add(new MemoryDomainDelegate("System Bus", size, endian, delegate(long addr) { if (addr < 0 || addr >= size) { throw new ArgumentOutOfRangeException(); } _memAccess = true; _mamePeriodicComplete.WaitOne(); var val = (byte)LibMAME.mame_lua_get_int($"{ MAMELuaCommand.GetSpace }:read_u8({ addr << _systemBusAddressShift })"); _memoryAccessComplete.Set(); _memAccess = false; return(val); }, null, dataWidth)); _memoryDomains = new MemoryDomainList(domains); (ServiceProvider as BasicServiceProvider).Register <IMemoryDomains>(_memoryDomains); }