diff --git a/package/batocera/core/batocera-configgen/configgen/configgen/generators/libretro/libretroGenerator.py b/package/batocera/core/batocera-configgen/configgen/configgen/generators/libretro/libretroGenerator.py index 72392be7ae7..225390846ea 100755 --- a/package/batocera/core/batocera-configgen/configgen/configgen/generators/libretro/libretroGenerator.py +++ b/package/batocera/core/batocera-configgen/configgen/configgen/generators/libretro/libretroGenerator.py @@ -56,9 +56,6 @@ def getHotkeysContext(self) -> HotkeysContext: # Main entry of the module # Configure retroarch and return a command def generate(self, system, rom, playersControllers, metadata, guns, wheels, gameResolution): - # Fix for the removed MESS/MAMEVirtual cores - if system.config.core in [ 'mess', 'mamevirtual' ]: - system.config['core'] = 'mame' # Get the graphics backend first gfxBackend = getGFXBackend(system) @@ -126,11 +123,13 @@ def generate(self, system, rom, playersControllers, metadata, guns, wheels, game shutil.copyfile(RETROARCH_CUSTOM, remapconfigDir / "common.rmp") # Retroarch core on the filesystem - retroarchCore = RETROARCH_CORES / f"{system.config.core}_libretro.so" + # 'mess' uses the mame core binary + coreBinary = "mame" if system.config.core == "mess" else system.config.core + retroarchCore = RETROARCH_CORES / f"{coreBinary}_libretro.so" # for each core, a file /usr/lib/.info must exit, otherwise, info such as rewinding/netplay will not work # to do a global check : cd /usr/lib/libretro && for i in *.so; do INF=$(echo $i | sed -e s+/usr/lib/libretro+/usr/share/libretro/info+ -e s+\.so+.info+); test -e "$INF" || echo $i; done - infoFile = RETROARCH_SHARE / "info" / f"{system.config.core}_libretro.info" + infoFile = RETROARCH_SHARE / "info" / f"{coreBinary}_libretro.info" if not infoFile.exists(): raise MissingCore diff --git a/package/batocera/core/batocera-configgen/configgen/configgen/generators/libretro/libretroMAMEConfig.py b/package/batocera/core/batocera-configgen/configgen/configgen/generators/libretro/libretroMAMEConfig.py index 8d0db4f0880..18369966b68 100644 --- a/package/batocera/core/batocera-configgen/configgen/configgen/generators/libretro/libretroMAMEConfig.py +++ b/package/batocera/core/batocera-configgen/configgen/configgen/generators/libretro/libretroMAMEConfig.py @@ -2,17 +2,26 @@ import codecs import csv +import json import logging import os import shutil -import xml.etree.ElementTree as ET import zipfile from pathlib import Path from typing import TYPE_CHECKING from xml.dom import minidom -from ...batoceraPaths import BIOS, CONFIGS, DEFAULTS_DIR, ROMS, SAVES, USER_DECORATIONS, mkdir_if_not_exists -from ..mame.mameCommon import is_atom_floppy +from ...batoceraPaths import BIOS, CONFIGS, DEFAULTS_DIR, SAVES, USER_DECORATIONS, mkdir_if_not_exists +from ...exceptions import BatoceraException +from ..mame.mamePaths import MESS_AUTOBOOT_SCRIPTS, MESS_SYSTEMS_MAPPING +from ..mame.messUtils import ( + _build_config_args, + _build_rom_ext_args, + _compute_sha1, + _load_softlist_map, + _lookup_rom, + _machine_from_softlist, +) if TYPE_CHECKING: from collections.abc import Sequence @@ -51,379 +60,17 @@ "start": "START" } -def generateMAMEConfigs(playersControllers: Controllers, system: Emulator, rom: Path, guns: Guns) -> None: - # Generate command line for MAME/MESS/MAMEVirtual - commandLine: list[str | Path] = [] - romDrivername = rom.stem - specialController = 'none' - if system.config.core in [ 'mame', 'mess', 'mamevirtual' ]: - corePath = f"lr-{system.config.core}" - else: - corePath = str(system.config.core) +def _corePath(system: Emulator) -> str: + if system.config.core in ['mame', 'mess', 'mamevirtual']: + return f"lr-mame" + return str(system.config.core) - if system.name in [ 'mame', 'neogeo', 'lcdgames', 'tvgames', 'vis', 'namco22', 'model2', 'cave3rd', 'gaelco', 'hikaru' ]: - # Set up command line for basic systems - # ie. no media, softlists, etc. - if system.config.get_bool("customcfg"): - cfgPath = CONFIGS / corePath / "custom" - else: - cfgPath = SAVES / "mame" / "mame" / "cfg" - mkdir_if_not_exists(cfgPath) - if system.name == 'vis': - commandLine += [ 'vis', '-cdrom', f'"{rom}"' ] - else: - commandLine += [ romDrivername ] - commandLine += [ '-cfg_directory', f'"{cfgPath}"' ] - commandLine += [ '-rompath', f'"{rom.parent};/userdata/bios/mame/;/userdata/bios/"' ] - pluginsToLoad: list[str] = [] - if system.config.get_bool("hiscoreplugin", True): - pluginsToLoad += [ "hiscore" ] - if system.config.get_bool("coindropplugin"): - pluginsToLoad += [ "coindrop" ] - if pluginsToLoad: - commandLine += [ "-plugins", "-plugin", ",".join(pluginsToLoad) ] - messMode = -1 - messModel = '' - else: - # Set up command line for MESS or MAMEVirtual - softDir = Path("/var/run/mame_software") - subdirSoftList = [ "mac_hdd", "bbc_hdd", "cdi", "archimedes_hdd", "fmtowns_cd" ] - softList = system.config.get("softList", "none") - if softList == "none": - softList = "" - - # Auto softlist for FM Towns if there is a zip that matches the folder name - # Used for games that require a CD and floppy to both be inserted - if system.name == 'fmtowns' and softList == '' and (ROMS / 'fmtowns' / f'{rom.parent.name}.zip').exists(): - softList = 'fmtowns_cd' - - # Determine MESS system name (if needed) - openFile = (DEFAULTS_DIR / "data" / "mame" / "messSystems.csv").open() - messSystems: list[str] = [] - messSysName: list[str] = [] - messRomType: list[str] = [] - messAutoRun: list[str] = [] - with openFile: - messDataList = csv.reader(openFile, delimiter=';', quotechar="'") - for row in messDataList: - messSystems.append(row[0]) - messSysName.append(row[1]) - messRomType.append(row[2]) - messAutoRun.append(row[3]) - messMode = messSystems.index(system.name) - - # Alternate system for machines that have different configs (ie computers with different hardware) - messModel = messSysName[messMode] - if altmodel := system.config.get("altmodel"): - messModel = altmodel - commandLine += [ messModel ] - - if messSysName[messMode] == "": - # Command line for non-arcade, non-system ROMs (lcdgames, plugnplay) - if system.config.get_bool("customcfg"): - cfgPath = CONFIGS / corePath / "custom" - else: - cfgPath = SAVES / "mame" / "mame" / "cfg" - mkdir_if_not_exists(cfgPath) - commandLine += [ romDrivername ] - commandLine += [ '-cfg_directory', f'"{cfgPath}"' ] - commandLine += [ '-rompath', f'"{rom.parent};/userdata/bios/"' ] - else: - # Command line for MESS consoles/computers - # TI-99 32k RAM expansion & speech modules - # Don't enable 32k by default - if system.name == "ti99": - commandLine += [ "-ioport", "peb" ] - if system.config.get_bool("ti99_32kram"): - commandLine += ["-ioport:peb:slot2", "32kmem"] - if system.config.get_bool("ti99_speech", True): - commandLine += ["-ioport", "speechsyn"] - - #Laser 310 Memory Expansion & joystick - if system.name == "laser310": - commandLine += ['-io', 'joystick', "-mem", system.config.get('memslot', 'laser_64k')] - - # BBC Joystick - if system.name == "bbcmicro" and system.config.get('sticktype', 'none') != 'none': - commandLine += ["-analogue", system.config['sticktype']] - specialController = system.config['sticktype'] - - # Apple II - if system.name == "apple2": - rom_extension = rom.suffix.lower() - # only add SD/IDE control if provided a hard drive image - if rom_extension in {".hdv", ".2mg", ".chd", ".iso", ".bin", ".cue"}: - commandLine += ["-sl7", "cffa202"] - if (gameio := system.config.get('gameio', 'none')) != 'none': - if gameio == 'joyport' and messModel != 'apple2p': - _logger.debug("Joyport is only compatible with Apple II Plus") - else: - commandLine += ["-gameio", gameio] - specialController = gameio - - # RAM size (Mac excluded, special handling below) - ramSize = system.config.get_int('ramsize') - if system.name != "macintosh" and ramSize: - commandLine += [ '-ramsize', str(ramSize) + 'M' ] - - # Mac RAM & Image Reader (if applicable) - if system.name == "macintosh" and ramSize: - if messModel in [ 'maciix', 'maclc3' ]: - if messModel == 'maclc3' and ramSize == 2: - ramSize = 4 - if messModel == 'maclc3' and ramSize > 80: - ramSize = 80 - if messModel == 'maciix' and ramSize == 16: - ramSize = 32 - if messModel == 'maciix' and ramSize == 48: - ramSize = 64 - commandLine += [ '-ramsize', str(ramSize) + 'M' ] - if messModel == 'maciix': - imageSlot = system.config.get('imagereader', 'nba') - if imageSlot != "disabled": - commandLine += [ f"-{imageSlot}", "image" ] - - altromtype = system.config.get_str("altromtype") - boot_disk = system.config.get("bootdisk") - - if softList != "": - # Software list ROM commands - prepSoftwareList(subdirSoftList, softList, softDir, BIOS / "mame" / "hash", rom.parent) - if softList in subdirSoftList: - commandLine += [ rom.parent.name ] - else: - commandLine += [ romDrivername ] - commandLine += [ "-rompath", f'"{softDir};/userdata/bios/"' ] - commandLine += [ "-swpath", f'"{softDir}"' ] - commandLine += [ "-verbose" ] - else: - # Alternate ROM type for systems with mutiple media (ie cassette & floppy) - # Mac will auto change floppy 1 to 2 if a boot disk is enabled - if system.name != "macintosh": - if altromtype: - if altromtype == "flop1" and messModel == "fmtmarty": - commandLine += [ "-flop" ] - else: - commandLine += [ "-" + altromtype ] - elif system.name == "adam": - # add some logic based on the extension - rom_extension = rom.suffix.lower() - if rom_extension == ".ddp": - commandLine += [ "-cass1" ] - elif rom_extension == ".dsk": - commandLine += [ "-flop1" ] - else: - commandLine += [ "-cart1" ] - elif system.name == "coco": - if rom.suffix.casefold() == ".cas": - commandLine += [ "-cass" ] - elif rom.suffix.casefold() == ".dsk": - commandLine += [ "-flop1" ] - else: - commandLine += [ "-cart" ] - # try to choose the right floppy for Apple2gs - elif system.name == "apple2gs": - rom_extension = rom.suffix.lower() - if rom_extension == ".zip": - with zipfile.ZipFile(rom, 'r') as zip_file: - file_list = zip_file.namelist() - # assume only one file in zip - if len(file_list) == 1: - filename = file_list[0] - rom_extension = Path(filename).suffix.lower() - if rom_extension in [".2mg", ".2img", ".img", ".image"]: - commandLine += [ "-flop3" ] - else: - commandLine += [ "-flop1" ] - else: - commandLine += [ "-" + messRomType[messMode] ] - else: - if boot_disk: - if (altromtype == "flop1" or not altromtype) and boot_disk in [ "macos30", "macos608", "macos701", "macos75" ]: - commandLine += [ "-flop2" ] - elif altromtype: - commandLine += [ "-" + altromtype ] - else: - commandLine += [ "-" + messRomType[messMode] ] - else: - if altromtype: - commandLine += [ "-" + altromtype ] - else: - commandLine += [ "-" + messRomType[messMode] ] - # Use the full filename for MESS non-softlist ROMs - commandLine += [ f'"{rom}"' ] - commandLine += [ "-rompath", f'"{rom.parent};/userdata/bios/"' ] - - # Boot disk for Macintosh - # Will use Floppy 1 or Hard Drive, depending on the disk. - if system.name == "macintosh" and boot_disk: - if system.config["bootdisk"] in [ "macos30", "macos608", "macos701", "macos75" ]: - bootType = "-flop1" - bootDisk = '"/userdata/bios/' + boot_disk + '.img"' - else: - bootType = "-hard" - bootDisk = '"/userdata/bios/' + boot_disk + '.chd"' - commandLine += [ bootType, bootDisk ] - - # Create & add a blank disk if needed, insert into drive 2 - # or drive 1 if drive 2 is selected manually or FM Towns Marty. - if system.config.get_bool('addblankdisk'): - if system.name == 'fmtowns': - blankDisk = Path('/usr/share/mame/blank.fmtowns') - targetFolder = SAVES / 'mame' / system.name - targetDisk = targetFolder / f'{rom.stem}.fmtowns' - # Add elif statements here for other systems if enabled - else: - blankDisk = Path('/usr/share/mame/blank.default') - targetFolder = SAVES / 'mame' / system.name - targetDisk = targetFolder / f'{rom.stem}.default' - mkdir_if_not_exists(targetFolder) - if not targetDisk.exists(): - shutil.copy2(blankDisk, targetDisk) - # Add other single floppy systems to this if statement - if messModel == "fmtmarty": - commandLine += [ '-flop', f'"{targetDisk}"' ] - elif altromtype == 'flop2': - commandLine += [ '-flop1', f'"{targetDisk}"' ] - else: - commandLine += [ '-flop2', f'"{targetDisk}"' ] - - # UI enable - for computer systems, the default sends all keys to the emulated system. - # This will enable hotkeys, but some keys may pass through to MAME and not be usable in the emulated system. - if system.config.get_bool("enableui", True): - commandLine += [ "-ui_active" ] - - # MESS config folder - if system.config.get_bool("customcfg"): - cfgPath = CONFIGS / corePath / messSysName[messMode] / "custom" - else: - cfgPath = SAVES / "mame" / "cfg" / messSysName[messMode] - if system.config.get_bool("pergamecfg"): - cfgPath = CONFIGS / corePath / messSysName[messMode] / rom.name - mkdir_if_not_exists(cfgPath) - commandLine += [ '-cfg_directory', f'"{cfgPath}"' ] - - # Autostart via ini file - # Init variables, delete old ini if it exists, prepare ini path - # lr-mame does NOT support multiple ini paths - autoRunCmd = "" - autoRunDelay = 0 - mameIniDir = SAVES / "mame" / "mame" / "ini" - mkdir_if_not_exists(mameIniDir) - if (mameIniDir / "batocera.ini").exists(): - (mameIniDir / "batocera.ini").unlink() - # bbc has different boots for floppy & cassette, no special boot for carts - if system.name == "bbcmicro": - if altromtype or softList: - if altromtype == "cass" or softList[-4:] == "cass": - autoRunCmd = '*tape\\nchain""\\n' - autoRunDelay = 2 - elif (altromtype and altromtype.startswith("flop")) or "flop" in softList: - autoRunCmd = '*cat\\n\\n\\n\\n*exec !boot\\n' - autoRunDelay = 3 - else: - autoRunCmd = '*cat\\n\\n\\n\\n*exec !boot\\n' - autoRunDelay = 3 - # fm7 boots floppies, needs cassette loading - elif system.name == "fm7": - if ( - system.config.get("altromtype") == "cass" - or (softList != "" and softList[-4:] == "cass") - ): - autoRunCmd = 'LOADM”“,,R\\n' - autoRunDelay = 5 - elif system.name == "coco": - romType = 'cart' - autoRunDelay = 2 - - # if using software list, use "usage" for autoRunCmd (if provided) - if softList != "": - softListFile = Path(f'/usr/bin/mame/hash/{softList}.xml') - if softListFile.exists(): - softwarelist = ET.parse(softListFile) - for software in softwarelist.findall('software'): - if software.attrib and software.get('name') == romDrivername: - for info in software.iter('info'): - if info.get('name') == 'usage': - autoRunCmd = f'{info.get('value')}\\n' - - # if still undefined, default autoRunCmd based on media type - if autoRunCmd == "": - if altromtype == "cass" or (softList and softList.endswith("cass")) or rom.suffix.casefold() == ".cas": - romType = 'cass' - if romDrivername.casefold().endswith(".bas"): - autoRunCmd = 'CLOAD:RUN\\n' - else: - autoRunCmd = 'CLOADM:EXEC\\n' - if altromtype == "flop1" or (softList and softList.endswith("flop")) or rom.suffix.casefold() == ".dsk": - romType = 'flop' - if romDrivername.casefold().endswith(".bas"): - autoRunCmd = f'RUN \"{romDrivername}\"\\n' - else: - autoRunCmd = f'LOADM \"{romDrivername}\":EXEC\\n' - - # check for a user override - autoRunFile = CONFIGS / 'mame' / 'autoload' / f'{system.name}_{romType}_autoload.csv' - if autoRunFile.exists(): - with autoRunFile.open() as openARFile: - autoRunList = csv.reader(openARFile, delimiter=';', quotechar="'") - for row in autoRunList: - if row and not row[0].startswith('#') and row[0].casefold() == romDrivername.casefold(): - autoRunCmd = row[1] + "\\n" - elif system.name == "atom": - autoRunDelay = 2 - autoRunCmd = messAutoRun[messMode] - if ( - (altromtype == "flop1") or - (softList and softList.endswith("flop")) or - is_atom_floppy(rom) - ): - autoRunFile = DEFAULTS_DIR / 'data' / 'mame' / 'atom_flop_autoload.csv' - if autoRunFile.exists(): - with autoRunFile.open() as openARFile: - autoRunList = csv.reader(openARFile, delimiter=';', quotechar="'") - for row in autoRunList: - if row and not row[0].startswith('#') and row[0].casefold() == rom.stem.casefold(): - autoRunCmd = row[1] + "\\n" - break - else: - # Check for an override file, otherwise use generic (if it exists) - autoRunCmd = messAutoRun[messMode] - autoRunFile = DEFAULTS_DIR / 'data' / 'mame' / f'{softList}_autoload.csv' - if autoRunFile.exists(): - with autoRunFile.open() as openARFile: - autoRunList = csv.reader(openARFile, delimiter=';', quotechar="'") - for row in autoRunList: - if row[0].casefold() == rom.stem.casefold(): - autoRunCmd = row[1] + "\\n" - autoRunDelay = 3 - - inipath = SAVES / 'mame' / 'mame' / 'ini' - commandLine += [ '-inipath', f'"{inipath}"' ] - if autoRunCmd != "": - if autoRunCmd.startswith("'"): - autoRunCmd.replace("'", "") - iniFile = (SAVES / 'mame' / 'mame' / 'ini' / 'batocera.ini').open("w") - iniFile.write('autoboot_command ' + autoRunCmd + "\n") - iniFile.write('autoboot_delay ' + str(autoRunDelay)) - iniFile.close() - # Create & add a blank disk if needed, insert into drive 2 - # or drive 1 if drive 2 is selected manually. - if system.config.get_bool('addblankdisk'): - lr_mess_dsk = SAVES / 'lr-mess' / system.name / rom.stem - if not lr_mess_dsk.exists(): - lr_mess_dsk.parent.mkdir(parents=True) - shutil.copy2('/usr/share/mame/blank.dsk', lr_mess_dsk) - if altromtype == 'flop2': - commandLine += [ '-flop1', f'"{lr_mess_dsk}"' ] - else: - commandLine += [ '-flop2', f'"{lr_mess_dsk}"' ] +def appendCommonCommandArgs(commandLine: list, system: Emulator, rom: Path) -> None: # Lightgun reload option if system.config.get_bool('offscreenreload'): commandLine += [ "-offscreen_reload" ] - # Art paths - lr-mame displays artwork in the game area and not in the bezel area, so using regular MAME artwork + shaders is not recommended. # By default, will ignore standalone MAME's art paths. if system.config.core != 'same_cdi': @@ -433,7 +80,6 @@ def generateMAMEConfigs(playersControllers: Controllers, system: Emulator, rom: artPath = f"/var/run/mame_artwork/;/usr/bin/mame/artwork/;{BIOS / 'lr-mame' / 'artwork'}" if system.name != "ti99": commandLine += [ '-artpath', f'"{artPath}"' ] - # Artwork crop - default to On for lr-mame # Exceptions for PDP-1 (status lights) and VGM Player (indicators) if "artworkcrop" not in system.config: @@ -442,7 +88,6 @@ def generateMAMEConfigs(playersControllers: Controllers, system: Emulator, rom: else: if system.config.get_bool("artworkcrop"): commandLine += [ "-artwork_crop" ] - # Share plugins with standalone MAME (except TI99) if system.name != "ti99": commandLine += [ "-pluginspath", f"/usr/bin/mame/plugins/;{SAVES / 'mame' / 'plugins'}" ] @@ -453,51 +98,209 @@ def generateMAMEConfigs(playersControllers: Controllers, system: Emulator, rom: mkdir_if_not_exists(SAVES / "mame" / "plugins") mkdir_if_not_exists(BIOS / "mame" / "samples") - # Delete old cmd files & prepare path + +def writeCmdFile(commandLine: list, rom: Path) -> None: cmdPath = Path("/var/run/cmdfiles") mkdir_if_not_exists(cmdPath) for file in cmdPath.iterdir(): if file.suffix == ".cmd": file.unlink() - - # Write command line file - cmdFilename = cmdPath / f"{romDrivername}.cmd" - # Check to see whether user provided a custom cmd file at the default location + cmdFilename = cmdPath / f"{rom.stem}.cmd" if Path(defaultCustomCmdFilepath := f"{rom}.cmd").is_file(): shutil.copyfile(defaultCustomCmdFilepath, cmdFilename) else: - # User did not provide a custom .cmd file. Use the logic above to create a new .cmd file cmdFile = cmdFilename.open("w") cmdFile.write(' '.join(str(item) for item in commandLine)) cmdFile.close() - # Call Controller Config - if messMode == -1: - generateMAMEPadConfig(cfgPath, playersControllers, system, "", rom, specialController, guns) + +def generateMAMEConfigs(playersControllers: Controllers, system: Emulator, rom: Path, guns: Guns) -> None: + if system.config.core == 'mess' or system.config.core == 'same_cdi': + return generateMessConfigs(playersControllers, system, rom, guns) + + # Generate command line for MAME/MAMEVirtual + commandLine: list[str | Path] = [] + romDrivername = rom.stem + specialController = 'none' + + corePath = _corePath(system) + + if system.config.get_bool("customcfg"): + cfgPath = CONFIGS / corePath / "custom" else: - generateMAMEPadConfig(cfgPath, playersControllers, system, messModel, rom, specialController, guns) - -def prepSoftwareList(subdirSoftList: Sequence[str], softList: str, softDir: Path, hashDir: Path, romParent: Path): - mkdir_if_not_exists(softDir) - # Check for/remove existing symlinks, remove hashfile folder - for checkFile in softDir.iterdir(): - if checkFile.is_symlink(): - checkFile.unlink() - if checkFile.is_dir(): - shutil.rmtree(checkFile) - # Prepare hashfile path - mkdir_if_not_exists(hashDir) - # Remove existing xml files - for file in hashDir.iterdir(): - if file.suffix == ".xml": - file.unlink() - # Copy hashfile - shutil.copy2(Path("/usr/bin/mame/hash") / f"{softList}.xml", hashDir / f"{softList}.xml") - # Link ROM's parent folder if needed, ROM's folder otherwise - if softList in subdirSoftList: - (softDir / softList).symlink_to(romParent.parent, target_is_directory=True) + cfgPath = SAVES / "mame" / "mame" / "cfg" + mkdir_if_not_exists(cfgPath) + commandLine += [ romDrivername ] + commandLine += [ '-cfg_directory', f'"{cfgPath}"' ] + commandLine += [ '-rompath', f'"{rom.parent};/userdata/bios/mame/;/userdata/bios/"' ] + pluginsToLoad: list[str] = [] + if system.config.get_bool("hiscoreplugin", True): + pluginsToLoad += [ "hiscore" ] + if system.config.get_bool("coindropplugin"): + pluginsToLoad += [ "coindrop" ] + if pluginsToLoad: + commandLine += [ "-plugins", "-plugin", ",".join(pluginsToLoad) ] + + appendCommonCommandArgs(commandLine, system, rom) + writeCmdFile(commandLine, rom) + + # Call Controller Config (arcade path always uses "" as messModel and 'none' as specialController) + generateMAMEPadConfig(cfgPath, playersControllers, system, "", rom, specialController, guns) + + +def generateMessConfigs(playersControllers: Controllers, system: Emulator, rom: Path, guns: Guns) -> None: + corePath = _corePath(system) + specialController = 'none' + + # ------------------------------------------------------------------ # + # 1. Identify system: attempt SHA1 autodetection first + # ------------------------------------------------------------------ # + softlist_map = _load_softlist_map() + + rom_sha1 = _compute_sha1(rom) + _logger.debug("lr-MESS: SHA1 of %s = %s", rom.name, rom_sha1) + + rom_info = _lookup_rom(rom_sha1) + if rom_info is not None: + softlist = rom_info["softlist"] + xml_media = rom_info["media"] + _logger.info( + "lr-MESS: identified %s as softlist=%s software=%s xml_media=%s", + rom.name, softlist, rom_info["software"], xml_media, + ) + sys_info = softlist_map.get(softlist, {}) + machine = system.config.get_str("altmodel") or sys_info.get("machine") + if not machine: + machine = _machine_from_softlist(softlist) + _logger.info( + "lr-MESS: softlist '%s' not in messSoftlistMap.json, inferred machine=%s", + softlist, machine, + ) + # Priority: user override > messSoftlistMap > MAME hash XML + media = system.config.get_str("altromtype") or sys_info.get("media") or xml_media + _logger.info( + "lr-MESS: media resolved to %s (softlistmap=%s xml=%s)", + media, sys_info.get("media"), xml_media, + ) + else: + # ROM not found — try messSystems.json lookup by system name + extension + softlist = "" + sys_info = {} + try: + systems_mapping = json.loads(MESS_SYSTEMS_MAPPING.read_text()) + sys_ext_map = systems_mapping.get(system.name, {}) + rom_ext = rom.suffix.lower() + if rom_ext == ".zip": + try: + with zipfile.ZipFile(rom, "r") as zf: + names = [n for n in zf.namelist() if not n.endswith("/")] + if names: + rom_ext = Path(names[0]).suffix.lower() + except (zipfile.BadZipFile, OSError): + pass + softlist = sys_ext_map.get(rom_ext) or sys_ext_map.get("*") or "" + if softlist: + _logger.info( + "lr-MESS: ROM not autodetected — found system '%s' ext '%s' in messSystems.json: softlist=%s", + system.name, rom_ext, softlist, + ) + except OSError: + _logger.warning("lr-MESS: messSystems.json not found at %s", MESS_SYSTEMS_MAPPING) + + if softlist: + sys_info = softlist_map.get(softlist, {}) + machine = system.config.get_str("altmodel") or sys_info.get("machine") + if not machine: + machine = _machine_from_softlist(softlist) + _logger.info( + "lr-MESS: softlist '%s' not in messSoftlistMap.json, inferred machine=%s", + softlist, machine, + ) + else: + machine = None + + machine = system.config.get_str("machine") or machine + # Priority: user override > messSoftlistMap + media = system.config.get_str("media") or sys_info.get("media") + if not machine or not media: + raise BatoceraException( + f"ROM '{rom.name}' (sha1={rom_sha1}) was not found in the MAME " + "software-list database. " + "Machine and media must be configured manually " + "(set 'machine' and 'media' in the system options)." + ) + _logger.info("lr-MESS: ROM not autodetected — using machine=%s media=%s", machine, media) + + # ------------------------------------------------------------------ # + # 2. Config path + # ------------------------------------------------------------------ # + if system.config.get_bool("customcfg"): + cfgPath = CONFIGS / corePath / machine / "custom" else: - (softDir / softList).symlink_to(romParent, target_is_directory=True) + cfgPath = SAVES / "mame" / "cfg" / machine + if system.config.get_bool("pergamecfg"): + cfgPath = CONFIGS / corePath / machine / rom.name + mkdir_if_not_exists(cfgPath) + + # ------------------------------------------------------------------ # + # 3. Build command line + # ------------------------------------------------------------------ # + commandLine: list[str | Path] = [] + + # Machine + commandLine += [machine] + + # Extra static args from softlist map + for arg in sys_info.get("extra_args", []): + commandLine.append(arg) + + # Config-driven args + commandLine += _build_config_args(sys_info.get("config_args", []), system, machine) + + # Extension-driven args + commandLine += _build_rom_ext_args(sys_info.get("rom_ext_args", []), rom) + + # Media flag and ROM path (quoted) + commandLine += [f"-{media}", f'"{rom}"'] + + # ROM search path + commandLine += ["-rompath", f'"{rom.parent};/userdata/bios/"'] + + # Config directory + commandLine += ["-cfg_directory", f'"{cfgPath}"'] + + # UI active (enabled by default for computer systems) + if system.config.get_bool("enableui", True): + commandLine += ["-ui_active"] + + # Autoboot Lua script + lua_script_name = sys_info.get("lua_script") + if lua_script_name: + lua_path = MESS_AUTOBOOT_SCRIPTS / lua_script_name + if lua_path.exists(): + commandLine += ["-autoboot_script", f'"{lua_path}"'] + else: + _logger.warning("lr-MESS: Lua autoboot script not found: %s", lua_path) + + # Blank disk handling + if system.config.get_bool("addblankdisk"): + altromtype = system.config.get_str("altromtype") + lr_mess_dsk = SAVES / 'lr-mess' / system.name / rom.stem + if not lr_mess_dsk.exists(): + lr_mess_dsk.parent.mkdir(parents=True) + shutil.copy2('/usr/share/mame/blank.dsk', lr_mess_dsk) + if altromtype == 'flop2': + commandLine += ['-flop1', f'"{lr_mess_dsk}"'] + else: + commandLine += ['-flop2', f'"{lr_mess_dsk}"'] + + # ------------------------------------------------------------------ # + # 4. Common suffix args, write cmd file, pad config + # ------------------------------------------------------------------ # + appendCommonCommandArgs(commandLine, system, rom) + writeCmdFile(commandLine, rom) + generateMAMEPadConfig(cfgPath, playersControllers, system, machine, rom, specialController, guns) + def getMameControlScheme(system: Emulator, rom: Path) -> str: # Game list files diff --git a/package/batocera/core/batocera-configgen/configgen/configgen/generators/mame/mameGenerator.py b/package/batocera/core/batocera-configgen/configgen/configgen/generators/mame/mameGenerator.py index 8a84c326e6b..677bfb5528a 100644 --- a/package/batocera/core/batocera-configgen/configgen/configgen/generators/mame/mameGenerator.py +++ b/package/batocera/core/batocera-configgen/configgen/configgen/generators/mame/mameGenerator.py @@ -1,12 +1,11 @@ from __future__ import annotations -import csv import json import logging import os import shutil import subprocess -import xml.etree.ElementTree as ET +import zipfile from pathlib import Path from typing import TYPE_CHECKING from xml.dom import minidom @@ -18,8 +17,6 @@ BATOCERA_SHARE_DIR, BIOS, CONFIGS, - DEFAULTS_DIR, - ROMS, SAVES, SCREENSHOTS, USER_DECORATIONS, @@ -29,8 +26,15 @@ from ...utils import bezels as bezelsUtil, videoMode from ..Generator import Generator from . import mameControllers -from .mameCommon import is_atom_floppy -from .mamePaths import MAME_BIOS, MAME_CHEATS, MAME_CONFIG, MAME_DEFAULT_DATA, MAME_ROMS, MAME_SAVES +from .mamePaths import MAME_BIOS, MAME_CHEATS, MAME_CONFIG, MAME_DEFAULT_DATA, MAME_SAVES, MESS_AUTOBOOT_SCRIPTS, MESS_SYSTEMS_MAPPING +from .messUtils import ( + _build_config_args, + _build_rom_ext_args, + _compute_sha1, + _load_softlist_map, + _lookup_rom, + _machine_from_softlist, +) if TYPE_CHECKING: from ...Emulator import Emulator @@ -59,584 +63,10 @@ def getHotkeysContext(self) -> HotkeysContext: } def generate(self, system, rom, playersControllers, metadata, guns, wheels, gameResolution): - # Extract "" - romBasename = rom.name - romDirname = rom.parent - romName = rom.stem - romExt = rom.suffix - - softDir = Path("/var/run/mame_software/") - softList = "" - messModel = "" - specialController = "none" - subdirSoftList = [ "mac_hdd", "bbc_hdd", "cdi", "archimedes_hdd", "fmtowns_cd" ] - - # Generate userdata folders if needed - mamePaths = [ - MAME_CONFIG, - MAME_SAVES / "nvram", - MAME_SAVES / "cfg", - MAME_SAVES / "input", - MAME_SAVES / "state", - MAME_SAVES / "diff", - MAME_SAVES / "comments", - MAME_BIOS / "artwork" / "crosshairs", - MAME_CHEATS, - MAME_SAVES / "plugins", - MAME_CONFIG / "ctrlr", - MAME_CONFIG / "ini", - ] - for checkPath in mamePaths: - mkdir_if_not_exists(checkPath) - - messSystems: list[str] = [] - messSysName: list[str] = [] - messRomType: list[str] = [] - messAutoRun: list[str] = [] - - with (DEFAULTS_DIR / 'data' / 'mame' / 'messSystems.csv').open() as openFile: - messDataList = csv.reader(openFile, delimiter=';', quotechar="'") - for row in messDataList: - messSystems.append(row[0]) - messSysName.append(row[1]) - messRomType.append(row[2]) - messAutoRun.append(row[3]) - - # Identify the current system - try: - messMode = messSystems.index(system.name) - except ValueError: - messMode = -1 - - softList = system.config.get_str("softList", "none") - softList = softList if softList != "none" else "" - - # Auto softlist for FM Towns if there is a zip that matches the folder name - # Used for games that require a CD and floppy to both be inserted - if system.name == 'fmtowns' and softList == '' and (ROMS / "fmtowns" / f"{romDirname.name}.zip").exists(): - softList = 'fmtowns_cd' - - commandArray: list[str | Path] = [ "/usr/bin/mame/mame" ] - - # MAME options used here are explained as it's not always straightforward - # A lot more options can be configured, just run mame -showusage and have a look - - # set audio to pipewire to fix audio from 0.278 - commandArray += [ "-sound", "pipewire" ] - # skip game info at start - commandArray += [ "-skip_gameinfo" ] - - if messMode == -1: - commandArray += [ "-rompath", f"{romDirname};{MAME_BIOS};{BIOS}" ] + if system.config.core == 'mess': + return _generate_mess(system, rom, playersControllers, metadata, guns, wheels, gameResolution) else: - if softList in subdirSoftList: - commandArray += [ "-rompath", f"{romDirname};{MAME_BIOS};{BIOS};{MAME_ROMS};{softDir}" ] - else: - commandArray += [ "-rompath", f"{romDirname};{MAME_BIOS};{BIOS};{MAME_ROMS}" ] - - # MAME various paths we can probably do better - commandArray += [ "-bgfx_path", "/usr/bin/mame/bgfx/" ] # Core bgfx files can be left on ROM filesystem - commandArray += [ "-fontpath", "/usr/bin/mame/" ] # Fonts can be left on ROM filesystem - commandArray += [ "-languagepath", "/usr/bin/mame/language/" ] # Translations can be left on ROM filesystem - commandArray += [ "-pluginspath", f"/usr/bin/mame/plugins/;{MAME_SAVES / 'plugins'}" ] - commandArray += [ "-samplepath", MAME_BIOS / "samples" ] # Current batocera storage location for MAME samples - commandArray += [ "-artpath", f"/var/run/mame_artwork/;/usr/bin/mame/artwork/;{MAME_BIOS / 'artwork'};{USER_DECORATIONS}" ] # first for systems ; second for overlays - - # Enable cheats - commandArray += [ "-cheat" ] - commandArray += [ "-cheatpath", MAME_CHEATS ] # Should this point to path containing the cheat.7z file - - # Logs and Swithres ini read by default (including its own verbose) - commandArray += [ "-verbose" ] - commandArray += [ "-switchres_ini" ] - - # MAME saves a lot of stuff, we need to map this on /userdata/saves/mame/ for each one - commandArray += [ "-nvram_directory" , MAME_SAVES / "nvram" ] - - # Set custom config path if option is selected or default path if not - customCfg = system.config.get_bool("customcfg") - - if messMode == -1: - if customCfg: - cfgPath = MAME_CONFIG / "custom" - else: - cfgPath = MAME_CONFIG - mkdir_if_not_exists(MAME_CONFIG) - else: - if customCfg: - cfgPath = MAME_CONFIG / messSysName[messMode] / "custom" - else: - cfgPath = MAME_CONFIG / messSysName[messMode] - mkdir_if_not_exists(MAME_CONFIG / messSysName[messMode]) - mkdir_if_not_exists(cfgPath) - - # MAME will create custom configs per game for MAME ROMs and MESS ROMs with no system attached (LCD games, TV games, etc.) - # This will allow an alternate config path per game for MESS console/computer ROMs that may need additional config. - if system.config.get_bool("pergamecfg") and messMode != -1 and messSysName[messMode] != "": - base_path = MAME_CONFIG / messSysName[messMode] - mkdir_if_not_exists(base_path) - cfgPath = base_path / romBasename - mkdir_if_not_exists(cfgPath) - commandArray += [ "-cfg_directory" , cfgPath ] - commandArray += [ "-input_directory" , MAME_SAVES / "input" ] - commandArray += [ "-state_directory" , MAME_SAVES / "state" ] - commandArray += [ "-snapshot_directory" , SCREENSHOTS ] - commandArray += [ "-diff_directory" , MAME_SAVES / "diff" ] - commandArray += [ "-comment_directory", MAME_SAVES / "comments" ] - commandArray += [ "-homepath" , MAME_SAVES / "plugins" ] - commandArray += [ "-ctrlrpath" , MAME_CONFIG / "ctrlr" ] - commandArray += [ "-inipath" , f"{MAME_CONFIG};{MAME_CONFIG / 'ini'}" ] - commandArray += [ "-crosshairpath" , MAME_BIOS / "artwork" / "crosshairs" ] - if softList != "": - commandArray += [ "-swpath" , softDir ] - commandArray += [ "-hashpath" , softDir / "hash" ] - - # TODO These paths are not handled yet - # TODO -swpath path to loose software - might use if we want software list MESS support - - # BGFX video engine : https://docs.mamedev.org/advanced/bgfx.html - video = system.config.get("video") - if video == "bgfx": - commandArray += [ "-video", "bgfx" ] - - # BGFX backend - bgfxbackend = system.config.get("bgfxbackend", "automatic") - commandArray += [ "-bgfx_backend", "auto" if bgfxbackend == "automatic" else bgfxbackend ] - - # BGFX shaders effects - commandArray += [ "-bgfx_screen_chains", system.config.get("bgfxshaders", "default") ] - - # Other video modes - elif video == "accel": - commandArray += ["-video", "accel" ] - else: - commandArray += [ "-video", "auto" ] - - # CRT / SwitchRes support - if system.config.get_bool("switchres"): - commandArray += [ "-modeline_generation" ] - commandArray += [ "-changeres" ] - commandArray += [ "-modesetting" ] - commandArray += [ "-readconfig" ] - else: - commandArray += [ "-resolution", f"{gameResolution['width']}x{gameResolution['height']}" ] - - # Refresh rate options to help with screen tearing - # syncrefresh is unlisted, it requires specific display timings and 99.9% of users will get unplayable games. - # Leaving it so it can be set manually, for CRT or other arcade-specific display users. - if system.config.get_bool("vsync"): - commandArray += [ "-waitvsync" ] - if system.config.get_bool("syncrefresh"): - commandArray += [ "-syncrefresh" ] - - # Rotation / TATE options - if (rotation := system.config.get("rotation")) in ["autoror", "autorol"]: - commandArray += [ f"-{rotation}" ] - - # Artwork crop - if system.config.get_bool("artworkcrop"): - commandArray += [ "-artwork_crop" ] - - # UI enable - for computer systems, the default sends all keys to the emulated system. - # This will enable hotkeys, but some keys may pass through to MAME and not be usable in the emulated system. - # Hotkey + D-Pad Up will toggle this when in use (scroll lock key) - if system.config.get_bool("enableui", True): - commandArray += [ "-ui_active" ] - - # Load selected plugins - pluginsToLoad = [] - if system.config.get_bool("hiscoreplugin", True): - pluginsToLoad += [ "hiscore" ] - if system.config.get_bool("coindropplugin"): - pluginsToLoad += [ "coindrop" ] - if system.config.get_bool("dataplugin"): - pluginsToLoad += [ "data" ] - if system.config.get_bool('offscreenreload'): # new offscreenreload for light guns games - pluginsToLoad += [ "offscreenreload" ] - if pluginsToLoad: - commandArray += [ "-plugins", "-plugin", ",".join(pluginsToLoad) ] - - # Mouse - useMouse = False - if system.config.get_bool('use_mouse') or not (messSysName[messMode] == "" or messMode == -1): - useMouse = True - commandArray += [ "-dial_device", "mouse" ] - commandArray += [ "-trackball_device", "mouse" ] - commandArray += [ "-paddle_device", "mouse" ] - commandArray += [ "-positional_device", "mouse" ] - commandArray += [ "-mouse_device", "mouse" ] - commandArray += [ "-ui_mouse" ] - if not system.config.use_guns: - commandArray += [ "-lightgun_device", "mouse" ] - commandArray += [ "-adstick_device", "mouse" ] - else: - commandArray += [ "-dial_device", "joystick" ] - commandArray += [ "-trackball_device", "joystick" ] - commandArray += [ "-paddle_device", "joystick" ] - commandArray += [ "-positional_device", "joystick" ] - commandArray += [ "-mouse_device", "joystick" ] - if not system.config.use_guns: - commandArray += [ "-lightgun_device", "joystick" ] - commandArray += [ "-adstick_device", "joystick" ] - # Multimouse option currently hidden in ES, SDL only detects one mouse. - # Leaving code intact for testing & possible ManyMouse integration - multiMouse = system.config.get_bool('multimouse') - if multiMouse: - commandArray += [ "-multimouse" ] - - # guns - useGuns = system.config.use_guns - if useGuns: - commandArray += [ "-lightgunprovider", "udev" ] - commandArray += [ "-lightgun_device", "lightgun" ] - commandArray += [ "-adstick_device", "lightgun" ] - - # wheels - useWheels = system.config.use_wheels - - if system.config.get_bool('multiscreens'): - screens = videoMode.getScreensInfos(system.config) - if len(screens) > 1: - commandArray += [ "-numscreens", str(len(screens)) ] - - # Finally we pass game name - # MESS will use the full filename and pass the system & rom type parameters if needed. - if messSysName[messMode] == "" or messMode == -1: - commandArray += [ romBasename ] - else: - messModel = messSysName[messMode] - # Alternate system for machines that have different configs (ie computers with different hardware) - if altmodel := system.config.get("altmodel"): - messModel = altmodel - commandArray += [ messModel ] - - #TI-99 32k RAM expansion & speech modules - enabled by default - if system.name == "ti99": - commandArray += [ "-ioport", "peb" ] - if system.config.get_bool("ti99_32kram", True): - commandArray += ["-ioport:peb:slot2", "32kmem"] - if system.config.get_bool("ti99_speech", True): - commandArray += ["-ioport", "speechsyn"] - - #Laser 310 Memory Expansion & Joystick - if system.name == "laser310": - commandArray += ['-io', 'joystick', "-mem", system.config.get('memslot', 'laser_64k')] - - # BBC Joystick - if system.name == "bbcmicro" and (sticktype := system.config.get('sticktype', 'none')) != 'none': - commandArray += ["-analogue", sticktype] - specialController = sticktype - - # Enterprise - if system.name == "enterprise": - commandArray += ['-exp', 'exdos'] - - # Apple II - if system.name == "apple2": - rom_extension = rom.suffix.lower() - # only add SD/IDE control if provided a hard drive image - if rom_extension in {".hdv", ".2mg", ".chd", ".iso", ".bin", ".cue"}: - commandArray += ["-sl7", "cffa202"] - if (gameio := system.config.get('gameio', 'none')) != 'none': - if gameio == 'joyport' and messModel != 'apple2p': - _logger.debug("Joyport joystick is only compatible with Apple II Plus") - else: - commandArray += ["-gameio", gameio] - specialController = gameio - - # RAM size (Mac excluded, special handling below) - ramSize = system.config.get_int('ramsize') - if system.name != "macintosh" and ramSize: - commandArray += [ '-ramsize', f'{ramSize}M' ] - - # Mac RAM & Image Reader (if applicable) - if system.name == "macintosh" and ramSize: - if messModel in [ 'maciix', 'maclc3' ]: - if messModel == 'maclc3' and ramSize == 2: - ramSize = 4 - if messModel == 'maclc3' and ramSize > 80: - ramSize = 80 - if messModel == 'maciix' and ramSize == 16: - ramSize = 32 - if messModel == 'maciix' and ramSize == 48: - ramSize = 64 - commandArray += [ '-ramsize', f'{ramSize}M' ] - if messModel == 'maciix': - imageSlot = system.config.get('imagereader', 'nba') - if imageSlot != "disabled": - commandArray += [ f"-{imageSlot}", "image" ] - - altromtype = system.config.get_str("altromtype") - - if softList == "": - # Boot disk for Macintosh - # Will use Floppy 1 or Hard Drive, depending on the disk. - boot_disk = system.config.get("bootdisk") - if system.name == "macintosh" and boot_disk: - if boot_disk in [ "macos30", "macos608", "macos701", "macos75" ]: - bootType = "-flop1" - bootDisk = f"/userdata/bios/{boot_disk}.img" - else: - bootType = "-hard" - bootDisk = f"/userdata/bios/{boot_disk}.chd" - commandArray += [ bootType, bootDisk ] - - # Alternate ROM type for systems with mutiple media (ie cassette & floppy) - # Mac will auto change floppy 1 to 2 if a boot disk is enabled - # Only one drive on FMTMarty - if system.name != "macintosh": - if altromtype: - if messModel == "fmtmarty" and altromtype == "flop1": - commandArray += [ "-flop" ] - else: - commandArray += [ f'-{altromtype}' ] - elif system.name == "adam": - # add some logic based on the rom extension - rom_extension = rom.suffix - if rom_extension == ".ddp": - commandArray += [ "-cass1" ] - elif rom_extension == ".dsk": - commandArray += [ "-flop1" ] - else: - commandArray += [ "-cart1" ] - elif system.name in ("coco", "dragon64"): - if romExt.casefold() == ".cas": - commandArray += [ "-cass" ] - elif romExt.casefold() == ".dsk": - commandArray += [ "-flop1" ] - else: - commandArray += [ "-cart" ] - elif system.name == "sc3000": - if romExt.casefold() in (".cas", ".wav", ".bit"): - commandArray += [ "-cass" ] - else: - commandArray += [ "-cart" ] - elif system.name == "segaai": - if romExt.casefold() in (".wav", ".flac", ".cas"): - commandArray += [ "-cass" ] - else: - commandArray += [ "-card" ] - elif system.name == "mc10": - if romExt.casefold() == ".cas": - commandArray += [ "-cass" ] - else: - commandArray += [ "-cart" ] - else: - commandArray += [ f'-{messRomType[messMode]}' ] - else: - if boot_disk: - if (altromtype == "flop1" or not altromtype) and boot_disk in [ "macos30", "macos608", "macos701", "macos75" ]: - commandArray += [ "-flop2" ] - elif altromtype: - commandArray += [ f'-{altromtype}' ] - else: - commandArray += [ f'-{messRomType[messMode]}' ] - else: - if altromtype: - commandArray += [ f'-{altromtype}' ] - else: - commandArray += [ f'-{messRomType[messMode]}' ] - # Use the full filename for MESS ROMs - commandArray += [ rom ] - else: - # Prepare software lists - if softList != "": - mkdir_if_not_exists(softDir) - for checkFile in softDir.iterdir(): - if checkFile.is_symlink(): - checkFile.unlink() - if checkFile.is_dir(): - shutil.rmtree(checkFile) - mkdir_if_not_exists(softDir / "hash") - (softDir / "hash" / f"{softList}.xml").symlink_to(f"/usr/bin/mame/hash/{softList}.xml") - if softList in subdirSoftList: - (softDir / softList).symlink_to(romDirname.parents[0], target_is_directory=True) - commandArray += [ romDirname.name ] - else: - (softDir / softList).symlink_to(romDirname, target_is_directory=True) - commandArray += [ romName ] - - # Create & add a blank disk if needed, insert into drive 2 - # or drive 1 if drive 2 is selected manually or FM Towns Marty. - if system.config.get_bool('addblankdisk'): - if system.name == 'fmtowns': - blankDisk = Path('/usr/share/mame/blank.fmtowns') - targetFolder = MAME_SAVES / system.name - targetDisk = targetFolder / romName - # Add elif statements here for other systems if enabled - else: - blankDisk = Path('/usr/share/mame/blank.default') - targetFolder = MAME_SAVES / system.name - targetDisk = targetFolder / f'{romName}.default' - mkdir_if_not_exists(targetFolder) - if not targetDisk.exists(): - shutil.copy2(blankDisk, targetDisk) - # Add other single floppy systems to this if statement - if messModel == "fmtmarty": - commandArray += [ '-flop', targetDisk ] - elif system.config.get('altromtype') == 'flop2': - commandArray += [ '-flop1', targetDisk ] - else: - commandArray += [ '-flop2', targetDisk ] - - autoRunCmd = "" - autoRunDelay = 0 - # Autostart computer games where applicable - # bbc has different boots for floppy & cassette, no special boot for carts - if system.name == "bbcmicro": - if altromtype or softList: - if altromtype == "cass" or softList.endswith("cass"): - autoRunCmd = '*tape\\nchain""\\n' - autoRunDelay = 2 - elif (altromtype and altromtype.startswith("flop")) or softList.endswith("flop"): - autoRunCmd = '*cat\\n\\n\\n\\n*exec !boot\\n' - autoRunDelay = 3 - else: - autoRunCmd = '*cat\\n\\n\\n\\n*exec !boot\\n' - autoRunDelay = 3 - # fm7 boots floppies, needs cassette loading - elif system.name == "fm7": - if ( - altromtype == "cass" - or (softList and softList[-4:] == "cass") - ): - autoRunCmd = 'LOADM”“,,R\\n' - autoRunDelay = 5 - elif system.name in ("coco", "dragon64"): - romType = 'cart' - autoRunDelay = 2 - - # if using software list, use "usage" for autoRunCmd (if provided) - if softList != "": - softListFile = Path('/usr/bin/mame/hash') / f'{softList}.xml' - if softListFile.exists(): - softwarelist = ET.parse(softListFile) - for software in softwarelist.findall('software'): - if software.attrib and software.get('name') == romName: - for info in software.iter('info'): - if info.get('name') == 'usage': - autoRunCmd = f"{info.get('value')}\\n" - - # if still undefined, default autoRunCmd based on media type - if autoRunCmd == "": - if altromtype == "cass" or (softList and softList.endswith("cass")) or romExt.casefold() == ".cas": - romType = 'cass' - if romName.casefold().endswith(".bas"): - autoRunCmd = 'CLOAD:RUN\\n' - else: - autoRunCmd = 'CLOADM:EXEC\\n' - if (altromtype == "flop1") or (softList and softList.endswith("flop")) or romExt.casefold() == ".dsk": - romType = 'flop' - if romName.casefold().endswith(".bas"): - autoRunCmd = f'RUN \"{romName}\"\\n' - else: - autoRunCmd = f'LOADM \"{romName}\":EXEC\\n' - - # check for a user override - autoRunFile = MAME_CONFIG / 'autoload' / f'{system.name}_{romType}_autoload.csv' - if autoRunFile.exists(): - with autoRunFile.open() as openARFile: - autoRunList = csv.reader(openARFile, delimiter=';', quotechar="'") - for row in autoRunList: - if row and not row[0].startswith('#') and row[0].casefold() == romName.casefold(): - autoRunCmd = f"{row[1]}\\n" - elif system.name == "mc10": - romType = 'cart' - autoRunDelay = 2 - - # if using software list, use "usage" for autoRunCmd (if provided) - if softList != "": - softListFile = Path('/usr/bin/mame/hash') / f'{softList}.xml' - if softListFile.exists(): - softwarelist = ET.parse(softListFile) - for software in softwarelist.findall('software'): - if software.attrib and software.get('name') == romName: - for info in software.iter('info'): - if info.get('name') == 'usage': - autoRunCmd = f"{info.get('value')}\\n" - - # if still undefined, default autoRunCmd based on media type - if autoRunCmd == "": - if altromtype == "cass" or (softList and softList.endswith("cass")) or romExt.casefold() == ".cas": - romType = 'cass' - autoRunCmd = 'CLOAD\\n' - - # check for a user override - autoRunFile = MAME_CONFIG / 'autoload' / f'{system.name}_{romType}_autoload.csv' - if autoRunFile.exists(): - with autoRunFile.open() as openARFile: - autoRunList = csv.reader(openARFile, delimiter=';', quotechar="'") - for row in autoRunList: - if row and not row[0].startswith('#') and row[0].casefold() == romName.casefold(): - autoRunCmd = f"{row[1]}\\n" - elif system.name == "atom": - autoRunDelay = 1 - autoRunCmd = messAutoRun[messMode] - # Check if the media being used is a floppy type - if ( - (altromtype == "flop1") or - (softList and softList.endswith("flop")) or - is_atom_floppy(rom) - ): - autoRunFile = MAME_DEFAULT_DATA / 'atom_flop_autoload.csv' - if autoRunFile.exists(): - with autoRunFile.open() as openARFile: - autoRunList = csv.reader(openARFile, delimiter=';', quotechar="'") - for row in autoRunList: - if row and not row[0].startswith('#') and row[0].casefold() == romName.casefold(): - autoRunCmd = f"{row[1]}\\n" - break - else: - # Check for an override file, otherwise use generic (if it exists) - autoRunCmd = messAutoRun[messMode] - autoRunFile = MAME_DEFAULT_DATA / f'{softList}_autoload.csv' - if autoRunFile.exists(): - with autoRunFile.open() as openARFile: - autoRunList = csv.reader(openARFile, delimiter=';', quotechar="'") - for row in autoRunList: - if row[0].casefold() == romName.casefold(): - autoRunCmd = f"{row[1]}\\n" - autoRunDelay = 3 - if autoRunCmd != "": - if autoRunCmd.startswith("'"): - autoRunCmd.replace("'", "") - commandArray += [ "-autoboot_delay", str(autoRunDelay), "-autoboot_command", autoRunCmd ] - - # bezels - bezelSet = system.config.get_str('bezel') or None - if system.config.get_bool('forceNoBezel'): - bezelSet = None - - try: - if messMode != -1: - MameGenerator.writeBezelConfig(bezelSet, system, rom, messSysName[messMode], gameResolution, system.guns_borders_size_name(guns), system.guns_border_ratio_type(guns)) - else: - MameGenerator.writeBezelConfig(bezelSet, system, rom, "", gameResolution, system.guns_borders_size_name(guns), system.guns_border_ratio_type(guns)) - except Exception: - MameGenerator.writeBezelConfig(None, system, rom, "", gameResolution, system.guns_borders_size_name(guns), system.guns_border_ratio_type(guns)) - - buttonLayout = getMameControlScheme(system, rom) - - if messMode == -1: - mameControllers.generatePadsConfig(cfgPath, playersControllers, "", buttonLayout, customCfg, specialController, bezelSet, useGuns, guns, useWheels, wheels, useMouse, multiMouse, system) - else: - mameControllers.generatePadsConfig(cfgPath, playersControllers, messModel, buttonLayout, customCfg, specialController, bezelSet, useGuns, guns, useWheels, wheels, useMouse, multiMouse, system) - - # If user provided a custom cmd file at the default location, use that as the customized commandArray - if (defaultCustomCmdFilepath := Path(f"{rom}.cmd")).is_file(): - with defaultCustomCmdFilepath.open() as f: - commandArray = f.read().splitlines() # pyright: ignore - - # Change directory to MAME folder (allows data plugin to load properly) - os.chdir('/usr/bin/mame') - return Command.Command( - array=commandArray, - env={ - "PWD":"/usr/bin/mame/", - "XDG_CONFIG_HOME": CONFIGS, - "XDG_CACHE_HOME": SAVES - } - ) + return _generate_mame(system, rom, playersControllers, metadata, guns, wheels, gameResolution) @staticmethod def writeBezelConfig(bezelSet: str | None, system: Emulator, rom: Path, messSys: str, gameResolution: Resolution, gunsBordersSize: str | None, gunsBordersRatio: str | None) -> None: @@ -821,6 +251,359 @@ def getMameMachineSize(machine: str, tmpdir: Path): raise BatoceraException("Display element not found") +def _generate_mame(system, rom, playersControllers, metadata, guns, wheels, gameResolution): + romBasename = rom.name + customCfg = system.config.get_bool("customcfg") + cfgPath = MAME_CONFIG / "custom" if customCfg else MAME_CONFIG + + extra_plugins = [ "offscreenreload" ] if system.config.get_bool('offscreenreload') else [] + commandArray = _mame_common_options(system, rom, cfgPath, gameResolution, extra_plugins) + + # Mouse + useMouse = False + if system.config.get_bool('use_mouse'): + useMouse = True + commandArray += [ "-dial_device", "mouse", "-trackball_device", "mouse", + "-paddle_device", "mouse", "-positional_device", "mouse", + "-mouse_device", "mouse", "-ui_mouse" ] + if not system.config.use_guns: + commandArray += [ "-lightgun_device", "mouse", "-adstick_device", "mouse" ] + else: + commandArray += [ "-dial_device", "joystick", "-trackball_device", "joystick", + "-paddle_device", "joystick", "-positional_device", "joystick", + "-mouse_device", "joystick" ] + if not system.config.use_guns: + commandArray += [ "-lightgun_device", "joystick", "-adstick_device", "joystick" ] + + # Multimouse option currently hidden in ES, SDL only detects one mouse. + # Leaving code intact for testing & possible ManyMouse integration + multiMouse = system.config.get_bool('multimouse') + if multiMouse: + commandArray += [ "-multimouse" ] + + useGuns = system.config.use_guns + if useGuns: + commandArray += [ "-lightgunprovider", "udev", + "-lightgun_device", "lightgun", "-adstick_device", "lightgun" ] + + useWheels = system.config.use_wheels + + commandArray += [ romBasename ] + + bezelSet = system.config.get_str('bezel') or None + if system.config.get_bool('forceNoBezel'): + bezelSet = None + try: + MameGenerator.writeBezelConfig(bezelSet, system, rom, "", gameResolution, system.guns_borders_size_name(guns), system.guns_border_ratio_type(guns)) + except Exception: + MameGenerator.writeBezelConfig(None, system, rom, "", gameResolution, system.guns_borders_size_name(guns), system.guns_border_ratio_type(guns)) + + buttonLayout = getMameControlScheme(system, rom) + mameControllers.generatePadsConfig(cfgPath, playersControllers, "", buttonLayout, customCfg, "none", bezelSet, useGuns, guns, useWheels, wheels, useMouse, multiMouse, system) + + if (defaultCustomCmdFilepath := Path(f"{rom}.cmd")).is_file(): + with defaultCustomCmdFilepath.open() as f: + commandArray = f.read().splitlines() + + os.chdir('/usr/bin/mame') + return Command.Command( + array=commandArray, + env={ + "PWD":"/usr/bin/mame/", + "XDG_CONFIG_HOME": CONFIGS, + "XDG_CACHE_HOME": SAVES, + } + ) + + +def _mame_common_options(system, rom, cfgPath, gameResolution, extra_plugins=None): + """Build the command-line args and create dirs common to MAME arcade and MESS.""" + romDirname = rom.parent + + for path in [ + MAME_CONFIG, + MAME_SAVES / "nvram", + MAME_SAVES / "cfg", + MAME_SAVES / "input", + MAME_SAVES / "state", + MAME_SAVES / "diff", + MAME_SAVES / "comments", + MAME_BIOS / "artwork" / "crosshairs", + MAME_CHEATS, + MAME_SAVES / "plugins", + MAME_CONFIG / "ctrlr", + MAME_CONFIG / "ini", + ]: + mkdir_if_not_exists(path) + mkdir_if_not_exists(cfgPath) + + # MAME options used here are explained as it's not always straightforward + # A lot more options can be configured, just run mame -showusage and have a look + commandArray: list[str | Path] = [ "/usr/bin/mame/mame" ] + + # set audio to pipewire to fix audio from 0.278 + commandArray += [ "-sound", "pipewire" ] + # skip game info at start + commandArray += [ "-skip_gameinfo" ] + commandArray += [ "-rompath", f"{romDirname};{MAME_BIOS};{BIOS}" ] + + # MAME various paths we can probably do better + commandArray += [ "-bgfx_path", "/usr/bin/mame/bgfx/" ] # Core bgfx files can be left on ROM filesystem + commandArray += [ "-fontpath", "/usr/bin/mame/" ] # Fonts can be left on ROM filesystem + commandArray += [ "-languagepath", "/usr/bin/mame/language/" ] # Translations can be left on ROM filesystem + commandArray += [ "-pluginspath", f"/usr/bin/mame/plugins/;{MAME_SAVES / 'plugins'}" ] + commandArray += [ "-samplepath", MAME_BIOS / "samples" ] # Current batocera storage location for MAME samples + commandArray += [ "-artpath", f"/var/run/mame_artwork/;/usr/bin/mame/artwork/;{MAME_BIOS / 'artwork'};{USER_DECORATIONS}" ] # first for systems ; second for overlays + + # Enable cheats + commandArray += [ "-cheat" ] + commandArray += [ "-cheatpath", MAME_CHEATS ] # Should this point to path containing the cheat.7z file + + # Logs and SwitchRes ini read by default (including its own verbose) + commandArray += [ "-verbose" ] + commandArray += [ "-switchres_ini" ] + + # MAME saves a lot of stuff, we need to map this on /userdata/saves/mame/ for each one + commandArray += [ "-nvram_directory", MAME_SAVES / "nvram" ] + commandArray += [ "-cfg_directory", cfgPath ] + commandArray += [ "-input_directory", MAME_SAVES / "input" ] + commandArray += [ "-state_directory", MAME_SAVES / "state" ] + commandArray += [ "-snapshot_directory", SCREENSHOTS ] + commandArray += [ "-diff_directory", MAME_SAVES / "diff" ] + commandArray += [ "-comment_directory", MAME_SAVES / "comments" ] + commandArray += [ "-homepath", MAME_SAVES / "plugins" ] + commandArray += [ "-ctrlrpath", MAME_CONFIG / "ctrlr" ] + commandArray += [ "-inipath", f"{MAME_CONFIG};{MAME_CONFIG / 'ini'}" ] + commandArray += [ "-crosshairpath", MAME_BIOS / "artwork" / "crosshairs" ] + + # BGFX video engine : https://docs.mamedev.org/advanced/bgfx.html + video = system.config.get("video") + if video == "bgfx": + commandArray += [ "-video", "bgfx" ] + # BGFX backend + bgfxbackend = system.config.get("bgfxbackend", "automatic") + commandArray += [ "-bgfx_backend", "auto" if bgfxbackend == "automatic" else bgfxbackend ] + # BGFX shaders effects + commandArray += [ "-bgfx_screen_chains", system.config.get("bgfxshaders", "default") ] + # Other video modes + elif video == "accel": + commandArray += [ "-video", "accel" ] + else: + commandArray += [ "-video", "auto" ] + + # CRT / SwitchRes support + if system.config.get_bool("switchres"): + commandArray += [ "-modeline_generation", "-changeres", "-modesetting", "-readconfig" ] + else: + commandArray += [ "-resolution", f"{gameResolution['width']}x{gameResolution['height']}" ] + + # Refresh rate options to help with screen tearing + # syncrefresh is unlisted, it requires specific display timings and 99.9% of users will get unplayable games. + # Leaving it so it can be set manually, for CRT or other arcade-specific display users. + if system.config.get_bool("vsync"): + commandArray += [ "-waitvsync" ] + if system.config.get_bool("syncrefresh"): + commandArray += [ "-syncrefresh" ] + + # Rotation / TATE options + if (rotation := system.config.get("rotation")) in ["autoror", "autorol"]: + commandArray += [ f"-{rotation}" ] + + # Artwork crop + if system.config.get_bool("artworkcrop"): + commandArray += [ "-artwork_crop" ] + + # UI enable - for computer systems, the default sends all keys to the emulated system. + # This will enable hotkeys, but some keys may pass through to MAME and not be usable in the emulated system. + # Hotkey + D-Pad Up will toggle this when in use (scroll lock key) + if system.config.get_bool("enableui", True): + commandArray += [ "-ui_active" ] + + # Load selected plugins + pluginsToLoad = [] + if system.config.get_bool("hiscoreplugin", True): + pluginsToLoad += [ "hiscore" ] + if system.config.get_bool("coindropplugin"): + pluginsToLoad += [ "coindrop" ] + if system.config.get_bool("dataplugin"): + pluginsToLoad += [ "data" ] + if extra_plugins: + pluginsToLoad.extend(extra_plugins) + if pluginsToLoad: + commandArray += [ "-plugins", "-plugin", ",".join(pluginsToLoad) ] + + if system.config.get_bool("multiscreens"): + screens = videoMode.getScreensInfos(system.config) + if len(screens) > 1: + commandArray += [ "-numscreens", str(len(screens)) ] + + return commandArray + + +def _generate_mess(system, rom, playersControllers, metadata, guns, wheels, gameResolution): + for path in [ + MAME_CONFIG, + MAME_SAVES / "nvram", + MAME_SAVES / "cfg", + MAME_SAVES / "input", + MAME_SAVES / "state", + MAME_SAVES / "diff", + MAME_SAVES / "comments", + MAME_BIOS / "artwork" / "crosshairs", + MAME_CHEATS, + MAME_SAVES / "plugins", + MAME_CONFIG / "ctrlr", + MAME_CONFIG / "ini", + ]: + mkdir_if_not_exists(path) + + softlist_map = _load_softlist_map() + rom_sha1 = _compute_sha1(rom) + _logger.debug("MESS: SHA1 of %s = %s", rom.name, rom_sha1) + + rom_info = _lookup_rom(rom_sha1) + if rom_info is not None: + softlist = rom_info["softlist"] + xml_media = rom_info["media"] + _logger.info("MESS: identified %s as softlist=%s software=%s xml_media=%s", + rom.name, softlist, rom_info["software"], xml_media) + sys_info = softlist_map.get(softlist, {}) + machine = system.config.get_str("altmodel") or sys_info.get("machine") + if not machine: + machine = _machine_from_softlist(softlist) + _logger.info("MESS: softlist '%s' not in messSoftlistMap.json, inferred machine=%s", + softlist, machine) + media = system.config.get_str("altromtype") or sys_info.get("media") or xml_media + _logger.info("MESS: media resolved to %s (softlistmap=%s xml=%s)", + media, sys_info.get("media"), xml_media) + else: + softlist = "" + sys_info = {} + try: + systems_mapping = json.loads(MESS_SYSTEMS_MAPPING.read_text()) + sys_ext_map = systems_mapping.get(system.name, {}) + rom_ext = rom.suffix.lower() + if rom_ext == ".zip": + try: + with zipfile.ZipFile(rom, "r") as zf: + names = [n for n in zf.namelist() if not n.endswith("/")] + if names: + rom_ext = Path(names[0]).suffix.lower() + except (zipfile.BadZipFile, OSError): + pass + softlist = sys_ext_map.get(rom_ext) or sys_ext_map.get("*") or "" + if softlist: + _logger.info("MESS: ROM not autodetected — found system '%s' ext '%s' in messSystems.json: softlist=%s", + system.name, rom_ext, softlist) + except OSError: + _logger.warning("MESS: messSystems.json not found at %s", MESS_SYSTEMS_MAPPING) + + if softlist: + sys_info = softlist_map.get(softlist, {}) + machine = system.config.get_str("altmodel") or sys_info.get("machine") + if not machine: + machine = _machine_from_softlist(softlist) + _logger.info("MESS: softlist '%s' not in messSoftlistMap.json, inferred machine=%s", + softlist, machine) + else: + machine = None + + machine = system.config.get_str("machine") or machine + media = system.config.get_str("media") or sys_info.get("media") + if not machine or not media: + raise BatoceraException( + f"ROM '{rom.name}' (sha1={rom_sha1}) was not found in the MAME " + "software-list database. " + "Machine and media must be configured manually " + "(set 'machine' and 'media' in the system options)." + ) + _logger.info("MESS: ROM not autodetected — using machine=%s media=%s", machine, media) + + customCfg = system.config.get_bool("customcfg") + mkdir_if_not_exists(MAME_CONFIG / machine) + cfgPath = MAME_CONFIG / machine / "custom" if customCfg else MAME_CONFIG / machine + + commandArray = _mame_common_options(system, rom, cfgPath, gameResolution) + + # Computer systems always use mouse + commandArray += [ + "-dial_device", "mouse", + "-trackball_device", "mouse", + "-paddle_device", "mouse", + "-positional_device", "mouse", + "-mouse_device", "mouse", + "-ui_mouse", + ] + if not system.config.use_guns: + commandArray += ["-lightgun_device", "mouse", "-adstick_device", "mouse"] + if system.config.use_guns: + commandArray += ["-lightgunprovider", "udev", + "-lightgun_device", "lightgun", + "-adstick_device", "lightgun"] + + commandArray += [machine] + for arg in sys_info.get("extra_args", []): + commandArray.append(arg) + commandArray += _build_config_args(sys_info.get("config_args", []), system, machine) + commandArray += _build_rom_ext_args(sys_info.get("rom_ext_args", []), rom) + + commandArray += [f"-{media}", str(rom)] + + if system.config.get_bool("addblankdisk"): + machine_blank = Path(f"/usr/share/mame/blank.{machine}") + blankDisk = machine_blank if machine_blank.exists() else Path("/usr/share/mame/blank.default") + targetFolder = MAME_SAVES / system.name + targetDisk = targetFolder / f"{rom.stem}.{blankDisk.name.split('.', 1)[-1]}" + mkdir_if_not_exists(targetFolder) + if not targetDisk.exists(): + shutil.copy2(blankDisk, targetDisk) + if machine in ("fmtmarty", "fmtowns"): + commandArray += ["-flop", targetDisk] + elif system.config.get_str("altromtype") == "flop2": + commandArray += ["-flop1", targetDisk] + else: + commandArray += ["-flop2", targetDisk] + + lua_script_name = sys_info.get("lua_script") + if lua_script_name: + lua_path = MESS_AUTOBOOT_SCRIPTS / lua_script_name + if lua_path.exists(): + commandArray += ["-autoboot_script", str(lua_path)] + else: + _logger.warning("MESS: Lua autoboot script not found: %s", lua_path) + + if (custom_cmd_file := Path(f"{rom}.cmd")).is_file(): + commandArray = custom_cmd_file.read_text().splitlines() + + bezelSet = system.config.get_str("bezel") or None + if system.config.get_bool("forceNoBezel"): + bezelSet = None + try: + MameGenerator.writeBezelConfig(bezelSet, system, rom, machine, gameResolution, + system.guns_borders_size_name(guns), system.guns_border_ratio_type(guns)) + except Exception: + MameGenerator.writeBezelConfig(None, system, rom, machine, gameResolution, + system.guns_borders_size_name(guns), system.guns_border_ratio_type(guns)) + + mameControllers.generatePadsConfig( + cfgPath, playersControllers, machine, + "default", customCfg, "none", bezelSet, + system.config.use_guns, guns, + system.config.use_wheels, wheels, + True, False, system, + ) + + os.chdir("/usr/bin/mame") + return Command.Command( + array=commandArray, + env={ + "PWD": "/usr/bin/mame/", + "XDG_CONFIG_HOME": CONFIGS, + "XDG_CACHE_HOME": SAVES, + }, + ) + + def getMameControlScheme(system: Emulator, rom_path: Path) -> MameControlScheme: # Game list files mameCapcom = MAME_DEFAULT_DATA / 'mameCapcom.txt' diff --git a/package/batocera/core/batocera-configgen/configgen/configgen/generators/mame/mamePaths.py b/package/batocera/core/batocera-configgen/configgen/configgen/generators/mame/mamePaths.py index db102e8d679..7df5c8c1dc6 100644 --- a/package/batocera/core/batocera-configgen/configgen/configgen/generators/mame/mamePaths.py +++ b/package/batocera/core/batocera-configgen/configgen/configgen/generators/mame/mamePaths.py @@ -10,3 +10,10 @@ MAME_CHEATS: Final = CHEATS / "mame" MAME_ROMS: Final = ROMS / "mame" MAME_DEFAULT_DATA: Final = DEFAULTS_DIR / "data" / "mame" + +MESS_ROMS: Final = ROMS / "mess" +MESS_SOFTLIST_MAP: Final = MAME_DEFAULT_DATA / "messSoftlistMap.json" +MESS_SYSTEMS_MAPPING: Final = MAME_DEFAULT_DATA / "messSystems.json" +MESS_AUTOBOOT_SCRIPTS: Final = MAME_DEFAULT_DATA / "autoboot_scripts" +MESS_HASH_CACHE: Final = MAME_SAVES / "mess_hash_cache.json" +MAME_HASH_DIR: Final = MAME_BIOS / "hash" diff --git a/package/batocera/core/batocera-configgen/configgen/configgen/generators/mame/messUtils.py b/package/batocera/core/batocera-configgen/configgen/configgen/generators/mame/messUtils.py new file mode 100644 index 00000000000..eda3ca6acf5 --- /dev/null +++ b/package/batocera/core/batocera-configgen/configgen/configgen/generators/mame/messUtils.py @@ -0,0 +1,241 @@ +from __future__ import annotations + +import hashlib +import json +import logging +import re +import time +import xml.etree.ElementTree as ET +import zipfile +from pathlib import Path +from typing import TypedDict + +from ...batoceraPaths import mkdir_if_not_exists +from .mamePaths import ( + MAME_HASH_DIR, + MESS_HASH_CACHE, + MESS_SOFTLIST_MAP, +) + +_logger = logging.getLogger(__name__) + +_MAME_BUILTIN_HASH_DIR = Path("/usr/bin/mame/hash") +_CACHE_VERSION = 1 + + +class RomInfo(TypedDict): + softlist: str + software: str + media: str + + +# cdrom must precede cd so that _cdrom is not partially matched as _cd +_SOFTLIST_SUFFIX_RE = re.compile( + r'_(cdrom|flop|cart|cass|disk|cd|rom|snap|hdd|quik|qd|cyl|ptp|ctape|card|ssd).*$' +) + + +def _machine_from_softlist(softlist: str) -> str: + """Derive a machine name from a softlist name by stripping trailing media suffixes.""" + return _SOFTLIST_SUFFIX_RE.sub('', softlist) + + +def _lookup_rom(sha1: str) -> RomInfo | None: + """Look up a SHA1 in the MAME software-list cache, building it if needed.""" + cache = _load_or_build_hash_cache() + return cache.get(sha1) + + +def _build_config_args(config_args: list[dict], system, machine: str) -> list[str]: + """ + Evaluate a config_args spec from messSoftlistMap.json against the live system + config and return the resulting MAME command-line tokens. + """ + result: list[str] = [] + for item in config_args: + key = item["key"] + kind = item.get("type", "str") + skip_if = item.get("skip_if") + + only = item.get("only_machines") + if only is not None and machine not in only: + continue + + overrides: dict = item.get("machine_overrides", {}).get(machine, {}) + + if kind == "bool": + value = system.config.get_bool(key, item.get("default", False)) + if value: + result += item.get("if_true", []) + else: + result += item.get("if_false", []) + + elif kind == "int": + value = system.config.get_int(key) or 0 + if skip_if is not None and value == skip_if: + continue + if value: + value = overrides.get("value_map", {}).get(str(value), value) + if "max" in overrides: + value = min(value, overrides["max"]) + result += [s.replace("{value}", str(value)) for s in item.get("args", [])] + + else: # str + value = system.config.get_str(key) or item.get("default", "") + if skip_if is not None and value == skip_if: + continue + if value: + restriction = item.get("value_machine_restriction", {}) + if value in restriction and machine not in restriction[value]: + _logger.debug( + "MESS: skipping -%s %s: not supported on machine %s (allowed: %s)", + key, value, machine, restriction[value], + ) + continue + result += [s.replace("{value}", value) for s in item.get("args", [])] + + return result + + +def _build_rom_ext_args(rom_ext_args: list[dict], rom: Path) -> list[str]: + """Emit static args based on the ROM file extension.""" + ext = rom.suffix.lower() + result: list[str] = [] + for item in rom_ext_args: + if ext in item.get("extensions", []): + result += item.get("args", []) + return result + + +def _load_softlist_map() -> dict: + """Load the softlist→machine/media/autoboot mapping JSON.""" + if not MESS_SOFTLIST_MAP.exists(): + _logger.warning("MESS: messSoftlistMap.json not found at %s", MESS_SOFTLIST_MAP) + return {} + try: + with MESS_SOFTLIST_MAP.open() as f: + data = json.load(f) + data.pop("_comment", None) + return data + except (json.JSONDecodeError, OSError) as exc: + _logger.error("MESS: Failed to load messSoftlistMap.json: %s", exc) + return {} + + +def _compute_sha1(rom: Path) -> str: + """ + Compute the SHA1 of the ROM's raw data. + + For .zip archives, hashes the first file inside (as MAME stores sha1 of the + uncompressed content, not the archive itself). + """ + suffix = rom.suffix.casefold() + + if suffix == ".zip": + try: + with zipfile.ZipFile(rom, "r") as zf: + names = [n for n in zf.namelist() if not n.endswith("/")] + if names: + with zf.open(names[0]) as inner: + return _sha1_of_stream(inner) + except (zipfile.BadZipFile, KeyError, OSError) as exc: + _logger.warning("MESS: Could not read zip %s: %s – hashing archive directly", rom, exc) + + with rom.open("rb") as f: + return _sha1_of_stream(f) + + +def _sha1_of_stream(stream) -> str: + h = hashlib.sha1() + for chunk in iter(lambda: stream.read(1 << 20), b""): + h.update(chunk) + return h.hexdigest() + + +def _get_hash_dirs() -> list[Path]: + """Return candidate MAME hash directories, user-supplied first.""" + dirs = [] + if MAME_HASH_DIR.exists(): + dirs.append(MAME_HASH_DIR) + if _MAME_BUILTIN_HASH_DIR.exists(): + dirs.append(_MAME_BUILTIN_HASH_DIR) + return dirs + + +def _hash_dir_mtime() -> float: + """Return the most recent mtime across all hash directories.""" + mtime = 0.0 + for d in _get_hash_dirs(): + try: + mtime = max(mtime, d.stat().st_mtime) + except OSError: + pass + return mtime + + +def _load_or_build_hash_cache() -> dict[str, RomInfo]: + """Load the SHA1→RomInfo cache from disk, rebuilding if stale.""" + if MESS_HASH_CACHE.exists(): + try: + with MESS_HASH_CACHE.open() as f: + data = json.load(f) + if ( + data.get("_version") == _CACHE_VERSION + and data.get("_mtime", 0) >= _hash_dir_mtime() + ): + return data.get("entries", {}) + except (json.JSONDecodeError, OSError): + pass + + _logger.info("MESS: Building software-list hash cache (this may take a moment)…") + t0 = time.monotonic() + entries = _build_hash_index() + elapsed = time.monotonic() - t0 + _logger.info("MESS: Cache built with %d entries in %.1fs", len(entries), elapsed) + + payload = { + "_version": _CACHE_VERSION, + "_mtime": _hash_dir_mtime(), + "entries": entries, + } + try: + mkdir_if_not_exists(MESS_HASH_CACHE.parent) + tmp = MESS_HASH_CACHE.with_suffix(".tmp") + with tmp.open("w") as f: + json.dump(payload, f) + tmp.replace(MESS_HASH_CACHE) + except OSError as exc: + _logger.warning("MESS: Could not write hash cache: %s", exc) + + return entries + + +def _build_hash_index() -> dict[str, RomInfo]: + """Parse all MAME software-list XMLs and build a sha1→RomInfo mapping.""" + index: dict[str, RomInfo] = {} + + for hash_dir in _get_hash_dirs(): + for xml_file in sorted(hash_dir.glob("*.xml")): + softlist_name = xml_file.stem + try: + context = ET.iterparse(xml_file, events=("start",)) + current_software: str = "" + current_media: str = "" + for _, elem in context: + tag = elem.tag + if tag == "software": + current_software = elem.get("name", "") + elif tag == "part": + current_media = elem.get("name", "") + elif tag == "rom": + sha1 = (elem.get("sha1") or "").lower() + if sha1 and current_software and sha1 not in index: + index[sha1] = RomInfo( + softlist=softlist_name, + software=current_software, + media=current_media, + ) + except ET.ParseError as exc: + _logger.warning("MESS: Skipping malformed XML %s: %s", xml_file, exc) + + return index diff --git a/package/batocera/core/batocera-configgen/data/mame/atom_flop_autoload.csv b/package/batocera/core/batocera-configgen/data/mame/atom_flop_autoload.csv deleted file mode 100644 index af46538cb71..00000000000 --- a/package/batocera/core/batocera-configgen/data/mame/atom_flop_autoload.csv +++ /dev/null @@ -1,16 +0,0 @@ -chuckie;\n*DOS\n*DIR\n*RUN"runme" -egghead;\n*DOS\n*DIR\n*RUN"EHRUN" -f14;\n*DOS\n*DIR\n*RUN"F1 4RUN" -gala;\n*DOS\n*DIR\n*RUN"runme" -galxians;\n*DOS\n*DIR\n*RUN"GALAXI" -hardhath;\n*DOS\n*DIR\n*RUN"LOADER" -hypervpr;\n*DOS\n*DIR\n*RUN"LOADER" -jetsetmn;\n*DOS\n*DIR\n*RUN"LOADER" -jsw;\n*DOS\n*DIR\n*RUN"JSWRUN" -jsw2;\n*DOS\n*DIR\n*RUN"JSW2RUN" -joeblade;\n*DOS\n*DIR\n*RUN"JOE" -jungle;\n*DOS\n*DIR\n*RUN"runme" -manicmin;\n*DOS\n*DIR\n*RUN"MMRUN" -repton;\n*DOS\n*DIR\n*RUN"runme" -tetris;\n*DOS\n*DIR\n*RUN"SIDRUN" -flappy;\n*DOS\n*DIR\n*RUN"FLAPPY" \ No newline at end of file diff --git a/package/batocera/core/batocera-configgen/data/mame/autoboot_scripts/a800_flop.lua b/package/batocera/core/batocera-configgen/data/mame/autoboot_scripts/a800_flop.lua new file mode 100644 index 00000000000..0d64e4e64c2 --- /dev/null +++ b/package/batocera/core/batocera-configgen/data/mame/autoboot_scripts/a800_flop.lua @@ -0,0 +1,23 @@ +local button = {} +for key, ports in pairs(manager.machine.ioport.ports) do + for field_name, field in pairs(ports.fields) do + button[field_name] = field + end +end + + +local frame_num = 0 +local function process_frame() + frame_num = frame_num + 1 + + if frame_num == 1 then + button["CONS.2: Option"]:set_value(1) + end + + if frame_num == 300 then + button["CONS.2: Option"]:set_value(0) + end + +end + +subscription=emu.add_machine_frame_notifier(process_frame) diff --git a/package/batocera/core/batocera-configgen/data/mame/autoboot_scripts/adam_flop.lua b/package/batocera/core/batocera-configgen/data/mame/autoboot_scripts/adam_flop.lua new file mode 100644 index 00000000000..5b02f16e05b --- /dev/null +++ b/package/batocera/core/batocera-configgen/data/mame/autoboot_scripts/adam_flop.lua @@ -0,0 +1,179 @@ +-- adam_flop.lua +-- +-- Coleco ADAM floppy (SmartFDD) autoboot. +-- +-- Boot flow: +-- EOS (Electronic Operating System) loads the first program from the floppy. +-- Most game disks contain T-DOS 4.5 (a CP/M-compatible OS by Tony Morehen) +-- as that first program. T-DOS then gives an A> command prompt. +-- +-- T-DOS autorun: +-- T-DOS 4.5 looks for a file called PROFILE.SUB on drive A: at boot. +-- If PROFILE.SUB exists the game starts automatically (no script needed). +-- If it does not exist, the user sees A> and must type the executable name. +-- Some disks have a PROFILE.SUB with a non-functional command (e.g. the game +-- title text); in those cases we wait for T-DOS to settle and type the correct +-- CP/M executable name ourselves. +-- +-- Executable detection: +-- Scan the raw .dsk image for the first CP/M directory entry with a .COM +-- extension (user 0, extent 0, printable ASCII name). This works regardless +-- of whether the softlist name matches the executable name. +-- Falls back to an uppercase/truncated version of the softlist name if +-- no .COM entry is found in the first 64 KB of the image. +-- +-- Image slot order for the adam machine: +-- 1: cassette1 2: cassette2 3: floppydisk1 4: floppydisk2 5-8: cartridge1-4 + +local common_autoboot = dofile("/usr/share/batocera/configgen/data/mame/autoboot_scripts/autoboot_common.lua") +local zip_util = dofile("/usr/share/batocera/configgen/data/mame/autoboot_scripts/autoboot_zip.lua") + +local button = {} +local frame_num = 0 + +common_autoboot.populate_buttons(button) +common_autoboot.print_image_info() + +local BUTTON_PRESS_DURATION = common_autoboot.DEFAULT_BUTTON_PRESS_DURATION + +local FLOP_SLOT = 3 -- floppydisk1 is the third image slot + +-- T-DOS finishes booting in ~5-7 s on real hardware; MAME emulates at ~60 Hz. +-- 600 frames (~10 s) gives a safe margin for T-DOS to load + PROFILE.SUB to fail +-- before we send the executable name. +local TDOS_PROMPT_FRAME = 600 + +local DSK_SCAN_BYTES = 65536 -- 64 KB covers the directory on any ADAM disk layout + +-- ── CP/M directory scanner ──────────────────────────────────────────────────── +-- +-- CP/M directory entries are 32 bytes: +-- byte 0 : user number (0x00 = active user 0, 0xE5 = deleted) +-- bytes 1-8 : filename (8 chars, space-padded, high bit = attribute flag) +-- bytes 9-11: extension (3 chars, space-padded, high bit = attribute flag) +-- byte 12 : extent low (0 for first extent) +-- Entries are always 32-byte aligned within 512-byte sectors, so scanning at +-- 32-byte increments from offset 0 catches every directory entry. + +local function find_first_com_on_disk(data) + if not data then return nil end + local n = #data + local all_coms = {} + + for i = 1, n - 31, 32 do + local user = data:byte(i) + if user == 0x00 and data:byte(i+12) == 0x00 then -- active entry, first extent + -- Validate filename (bytes i+1..i+8): printable ASCII, mask off attribute bit + local chars = {} + local valid = true + for j = i+1, i+8 do + local c = data:byte(j) & 0x7F + if c == 0x20 then + break -- space = padding, end of name + elseif c < 0x21 or c > 0x7E then + valid = false; break + else + table.insert(chars, string.char(c)) + end + end + if valid and #chars > 0 then + -- Check extension (bytes i+9..i+11) is COM + local e1 = data:byte(i+9) & 0x7F + local e2 = data:byte(i+10) & 0x7F + local e3 = data:byte(i+11) & 0x7F + if e1 == 0x43 and e2 == 0x4F and e3 == 0x4D then -- 'C','O','M' + local fname = table.concat(chars) + -- deduplicate + local seen = false + for _, v in ipairs(all_coms) do if v == fname then seen = true; break end end + if not seen then + table.insert(all_coms, fname) + end + end + end + end + end + + if #all_coms == 0 then + print("[adam_flop] No .COM file found in directory scan") + return nil + end + + -- Prefer a COM whose name does not end with a column-mode suffix (-80, -40, 80, 40, _80, _40). + -- The Infocom Zork disks list the 80-column version first; the plain version + -- (ZORK1, ZORKII, ZORK-III) runs on the standard 40-column ADAM display. + for _, fname in ipairs(all_coms) do + if not fname:match("[-_]?[48]0$") then + print(string.format("[adam_flop] Chosen COM (no col-suffix): %s.COM (all: %s)", + fname, table.concat(all_coms, ", "))) + return fname + end + end + + -- All entries have a column-mode suffix — just use the first one. + print(string.format("[adam_flop] Chosen COM (first, all suffixed): %s.COM", all_coms[1])) + return all_coms[1] +end + +local function detect_exe_name(mame_filename, fallback_key) + print("[adam_flop] --- Scanning disk for first .COM file ---") + local data = zip_util.read_bytes(mame_filename, DSK_SCAN_BYTES) + local com_name = find_first_com_on_disk(data) + if com_name then + return com_name + end + local fallback = fallback_key:sub(1, 8):upper() + print("[adam_flop] Falling back to softlist-derived name: " .. fallback) + return fallback +end + +-- ── Startup ─────────────────────────────────────────────────────────────────── + +local current_software_name = manager.machine.images:at(FLOP_SLOT).filename + +-- software_key: base filename without path or extension, lowercased (for boot_sequences lookup) +local software_key = current_software_name:match("([^/\\]+)$") +software_key = software_key:gsub("%.%w+$", ""):lower() + +-- Detect the actual CP/M executable name by scanning the disk image directory. +local exe_name = detect_exe_name(current_software_name, software_key) + +-- ── Boot functions ──────────────────────────────────────────────────────────── + +local function boot_default() + common_autoboot.type_at_frame(frame_num, exe_name .. "\n", TDOS_PROMPT_FRAME, BUTTON_PRESS_DURATION) +end + +-- Per-game overrides — add entries here when disk scanning gives the wrong +-- result (e.g. the game has multiple .COM files and the second one is correct, +-- or a multi-step boot sequence is needed). +local boot_sequences = { + ['default'] = boot_default, +} + +-- ── Main ────────────────────────────────────────────────────────────────────── + +local function process_frame() + frame_num = frame_num + 1 + + if frame_num == 1 then + emu.print_info("System Driver: " .. emu.romname()) + emu.print_info("Loaded Software Name: " .. current_software_name) + emu.print_info("[adam_flop] Software key: " .. software_key .. " T-DOS executable: " .. exe_name) + + if boot_sequences[software_key] then + emu.print_info("--- Booting using game-specific profile: " .. software_key .. " ---") + else + emu.print_info("--- Booting using default profile (will type: " .. exe_name .. ") ---") + end + end + + common_autoboot.debug_frame_num(frame_num) + + local boot_function = boot_sequences[software_key] or boot_sequences['default'] + if boot_function then + boot_function() + end +end + +subscription = emu.add_machine_frame_notifier(process_frame) diff --git a/package/batocera/core/batocera-configgen/data/mame/autoboot_scripts/alice90.lua b/package/batocera/core/batocera-configgen/data/mame/autoboot_scripts/alice90.lua new file mode 100644 index 00000000000..acadf206d37 --- /dev/null +++ b/package/batocera/core/batocera-configgen/data/mame/autoboot_scripts/alice90.lua @@ -0,0 +1,75 @@ +-- alice90.lua + +-- Load common autoboot functions +local common_autoboot = dofile("/usr/share/batocera/configgen/data/mame/autoboot_scripts/autoboot_common.lua") + +-- Script-wide variables +local button = {} +local frame_num = 0 + +common_autoboot.populate_buttons(button) + +-- --- Constants for this specific script --- +local BUTTON_PRESS_DURATION = common_autoboot.DEFAULT_BUTTON_PRESS_DURATION +local CASSETTE_MOTOR_OFF_DELAY_FRAMES = common_autoboot.DEFAULT_CASSETTE_MOTOR_OFF_DELAY_FRAMES + +local function boot_default(cassette_handler) + common_autoboot.type_at_frame(frame_num, "CLOAD\n", 100, BUTTON_PRESS_DURATION) + common_autoboot.play_cassette_at_frame(frame_num, ":cassette", 200) + + local cassette_load_done = cassette_handler(frame_num, CASSETTE_MOTOR_OFF_DELAY_FRAMES) + + if cassette_load_done then + emu.keypost('RUN\n') + end +end + +-- --- Main Script Logic --- + +-- Determine the currently loaded software name +local current_software_name = manager.machine.images:at(3).filename + +-- Map software names to their respective boot functions +local boot_sequences = { + ["default"] = boot_default, +} + +-- --- Cassette Loading Handler Setup --- +local cassette_handler = common_autoboot.create_cassette_handler(":cassette") + +-- MAME machine frame notification callback +local function process_frame() + frame_num = frame_num + 1 + + -- Initial setup/info display at frame 1 + if frame_num == 1 then + emu.print_info("System Driver: " .. emu.romname()) + emu.print_info("Loaded Software Name (from image filename): " .. current_software_name) + + -- Determine which profile will be used + if boot_sequences[current_software_name] then + emu.print_info("--- Booting using game specific profile: " .. current_software_name .. " ---") + else + emu.print_info("--- Booting using default profile ---") + end + end + + --- Print current frame number every 100 frames --- + if frame_num % 100 == 0 then -- The modulo operator (%) gives the remainder of a division + emu.print_info("Current Frame: " .. frame_num) + end + + -- Execute the relevant boot sequence based on the loaded software + local boot_function = boot_sequences[current_software_name] + + if not boot_function then -- If specific function not found + boot_function = boot_sequences["default"] -- Assign the default function + end + + if boot_function then + boot_function(cassette_handler) + end +end + +-- Subscribe to machine frame notifications +subscription = emu.add_machine_frame_notifier(process_frame) \ No newline at end of file diff --git a/package/batocera/core/batocera-configgen/data/mame/autoboot_scripts/apogee.lua b/package/batocera/core/batocera-configgen/data/mame/autoboot_scripts/apogee.lua new file mode 100644 index 00000000000..59d1b624362 --- /dev/null +++ b/package/batocera/core/batocera-configgen/data/mame/autoboot_scripts/apogee.lua @@ -0,0 +1,78 @@ +-- apogee.lua + +-- Load common autoboot functions +local common_autoboot = dofile("/usr/share/batocera/configgen/data/mame/autoboot_scripts/autoboot_common.lua") + +-- Script-wide variables +local button = {} +local frame_num = 0 + +-- --- Print Info --- +common_autoboot.print_image_info() + +-- --- Cassette Loading Handler Setup --- +local CASSETTE_SLOT = 1 +local CASSETTE_DEVICE_TAG = manager.machine.images:at(CASSETTE_SLOT).device.tag +local cassette_handler = common_autoboot.create_cassette_handler(CASSETTE_DEVICE_TAG) + +common_autoboot.populate_buttons(button) + +-- --- Constants for this specific script --- +local BUTTON_PRESS_DURATION = common_autoboot.DEFAULT_BUTTON_PRESS_DURATION +local CASSETTE_MOTOR_OFF_DELAY_FRAMES = common_autoboot.DEFAULT_CASSETTE_MOTOR_OFF_DELAY_FRAMES + +local function boot_default(cassette_handler) + common_autoboot.type_at_frame(frame_num, "I\n", 100) + common_autoboot.play_cassette_at_frame(frame_num, CASSETTE_DEVICE_TAG, 200) + + local cassette_load_done = cassette_handler(frame_num, CASSETTE_MOTOR_OFF_DELAY_FRAMES) + + if cassette_load_done then + emu.keypost('G\n') + end +end + +-- --- Main Script Logic --- + +-- Determine the currently loaded software name +local current_software_name = manager.machine.images:at(CASSETTE_SLOT).filename + +-- Map software names to their respective boot functions +local boot_sequences = { + ['default'] = boot_default, +} + +-- MAME machine frame notification callback +local function process_frame() + frame_num = frame_num + 1 + + -- Initial setup/info display at frame 1 + if frame_num == 1 then + emu.print_info("System Driver: " .. emu.romname()) + emu.print_info("Loaded Software Name (from image filename): " .. current_software_name) + + -- Determine which profile will be used + if boot_sequences[current_software_name] then + emu.print_info("--- Booting using game specific profile: " .. current_software_name .. " ---") + else + emu.print_info("--- Booting using default profile ---") + end + end + + --- DEBUG: Print current frame number every 100 frames --- + common_autoboot.debug_frame_num(frame_num) + + -- Execute the relevant boot sequence based on the loaded software + local boot_function = boot_sequences[current_software_name] + + if not boot_function then -- If specific function not found + boot_function = boot_sequences["default"] -- Assign the default function + end + + if boot_function then + boot_function(cassette_handler) + end +end + +-- Subscribe to machine frame notifications +subscription = emu.add_machine_frame_notifier(process_frame) \ No newline at end of file diff --git a/package/batocera/core/batocera-configgen/data/mame/autoboot_scripts/aquarius_cass.lua b/package/batocera/core/batocera-configgen/data/mame/autoboot_scripts/aquarius_cass.lua new file mode 100644 index 00000000000..511fdc4578d --- /dev/null +++ b/package/batocera/core/batocera-configgen/data/mame/autoboot_scripts/aquarius_cass.lua @@ -0,0 +1,216 @@ +-- aquarius_cass.lua +-- +-- Mattel Aquarius cassette autoboot. +-- +-- Aquarius .caq tape format: +-- [0xFF leader bytes] [0x00 marker] [6-byte filename] [0xFF inter-block] [0x00 marker] [data] +-- +-- File type detection via filename in header: +-- ASCII letters → BASIC program → CLOAD, then RUN after tape stops +-- "######" (0x23 × 6) → raw machine code payload → CLOAD (auto-executes) +-- +-- Most commercial games are two-part softlist entries: +-- cass1: BASIC loader (_BAS.caq, ~145 bytes) — handled by this script +-- cass2: machine code payload (_A.caq) — loaded automatically by the BASIC program +-- Single-part games may be pure BASIC or a standalone machine code file. + +local common_autoboot = dofile("/usr/share/batocera/configgen/data/mame/autoboot_scripts/autoboot_common.lua") +local zip_util = dofile("/usr/share/batocera/configgen/data/mame/autoboot_scripts/autoboot_zip.lua") + +local CAS_SCAN_BYTES = 4096 + +local TAPE_TYPE_BASIC = 0 +local TAPE_TYPE_MACHINE = 1 + +local BUTTON_PRESS_DURATION = common_autoboot.DEFAULT_BUTTON_PRESS_DURATION +local CASSETTE_STOP_DELAY_FRAMES = common_autoboot.DEFAULT_CASSETTE_MOTOR_OFF_DELAY_FRAMES + +local button = {} +local frame_num = 0 + +common_autoboot.populate_buttons(button) + +local function find_cassette_image() + for _, cass in pairs(manager.machine.cassettes) do + if cass.exists and cass.filename and #cass.filename > 0 then + return cass.filename, cass.device and cass.device.tag or ":cassette" + end + end + for _, img in pairs(manager.machine.images) do + if img.exists and img.filename and #img.filename > 0 then + local fn = img.filename:lower() + if fn:find("%.caq$") or fn:find("%.cas$") then + return img.filename, img.device and img.device.tag or ":cassette" + end + end + end + return nil, ":cassette" +end + +-- ── .caq header parser ──────────────────────────────────────────────────────── +-- +-- Structure: [0xFF... leader] [0x00 marker] [6-byte filename] [0xFF... inter-block] [0x00 data marker] [data...] +-- Filename "######" (all 0x23) indicates a raw machine code payload. +-- Any other ASCII filename indicates a BASIC program. + +local function parse_caq_header(data) + if not data or #data < 20 then return nil end + + -- Skip 0xFF leader bytes + local i = 1 + while i <= #data and data:byte(i) == 0xFF do i = i + 1 end + + -- Expect 0x00 header marker + if i > #data or data:byte(i) ~= 0x00 then return nil end + i = i + 1 + + -- Read 6-byte filename + if i + 5 > #data then return nil end + local name_bytes = {} + for j = i, i+5 do table.insert(name_bytes, data:byte(j)) end + + -- Detect type: all 0x23 ('#') = machine code payload; otherwise BASIC + local all_hash = true + for _, b in ipairs(name_bytes) do + if b ~= 0x23 then all_hash = false; break end + end + + local filename = "" + for _, b in ipairs(name_bytes) do + if b >= 0x21 and b <= 0x7E then filename = filename .. string.char(b) end + end + + if all_hash then + print("[Aquarius Autoboot] Header: MACHINE CODE (name='######')") + return { file_type = TAPE_TYPE_MACHINE, filename = "######" } + else + print(string.format("[Aquarius Autoboot] Header: BASIC name='%s'", filename)) + return { file_type = TAPE_TYPE_BASIC, filename = filename } + end +end + +local function detect_tape_info() + print("[Aquarius Autoboot] --- Tape type detection ---") + local path = find_cassette_image() + if not path then + print("[Aquarius Autoboot] No cassette image found, defaulting to BASIC.") + return { file_type = TAPE_TYPE_BASIC, filename = "" } + end + print("[Aquarius Autoboot] Tape path: " .. path) + local data = zip_util.read_bytes(path, CAS_SCAN_BYTES) + if not data then + print("[Aquarius Autoboot] Could not read tape, defaulting to BASIC.") + return { file_type = TAPE_TYPE_BASIC, filename = "" } + end + local info = parse_caq_header(data) + if info then return info end + print("[Aquarius Autoboot] No valid header found, defaulting to BASIC.") + return { file_type = TAPE_TYPE_BASIC, filename = "" } +end + +-- ── Cassette handler (same stop-detection pattern as mc10_cass.lua) ─────────── + +local function create_cassette_handler(cassette_device_tag) + local stop_frame = 0 + local seen_playing = false + local load_done = false + + return function(current_frame_num, delay_frames_after_stop) + local dev = manager.machine.cassettes[cassette_device_tag] + if not dev then + print("[Aquarius Autoboot] Cassette device not found: " .. cassette_device_tag) + return false + end + + if dev.is_stopped == false then + manager.machine.video.throttled = false + manager.machine.video.frameskip = 12 + seen_playing = true + else + manager.machine.video.throttled = true + manager.machine.video.frameskip = 0 + if seen_playing and stop_frame == 0 then + stop_frame = current_frame_num + print("[Aquarius Autoboot] Cassette stop detected at frame " .. current_frame_num) + end + end + + if not load_done and stop_frame ~= 0 and + current_frame_num > stop_frame + delay_frames_after_stop then + load_done = true + print("[Aquarius Autoboot] Cassette load complete at frame " .. current_frame_num) + return true + end + return false + end +end + +local function play_cassette(cassette_tag, start_frame) + if frame_num == start_frame then + local dev = manager.machine.cassettes[cassette_tag] + if dev then + dev:play() + print("[Aquarius Autoboot] Playing cassette at frame " .. frame_num) + else + print("[Aquarius Autoboot] Cassette device not found for play: " .. cassette_tag) + end + end +end + +-- ── Boot functions ──────────────────────────────────────────────────────────── + +local tape_info = nil +local cassette_tag = ":cassette" +local cassette_handler = nil + +local function boot_basic() + -- Aquarius BASIC load sequence: + -- 1. Enter — dismiss any initial splash + -- 2. CLOAD"name" — BASIC sends motor-on to the cassette (MAME starts tape automatically) + -- 3. play_cassette — belt-and-suspenders: explicitly start tape in case motor signal is late + -- 4. RUN — after tape stops, run the loaded program + local cmd = string.format('CLOAD"%s"\n', tape_info.filename) + common_autoboot.type_at_frame(frame_num, "\n", 100, BUTTON_PRESS_DURATION) + common_autoboot.type_at_frame(frame_num, cmd, 150, BUTTON_PRESS_DURATION) + play_cassette(cassette_tag, 200) + + if cassette_handler and cassette_handler(frame_num, CASSETTE_STOP_DELAY_FRAMES) then + emu.keypost("RUN\n") + end +end + +local function boot_machine() + -- Machine code payload: same sequence, no RUN needed (auto-executes on load). + common_autoboot.type_at_frame(frame_num, "\n", 100, BUTTON_PRESS_DURATION) + common_autoboot.type_at_frame(frame_num, "CLOAD\n", 150, BUTTON_PRESS_DURATION) + play_cassette(cassette_tag, 200) + + if cassette_handler then + cassette_handler(frame_num, CASSETTE_STOP_DELAY_FRAMES) + end +end + +-- ── Main ────────────────────────────────────────────────────────────────────── + +local function process_frame() + frame_num = frame_num + 1 + + if frame_num == 1 then + emu.print_info("System Driver: " .. emu.romname()) + common_autoboot.print_image_info() + local _, detected_tag = find_cassette_image() + cassette_tag = detected_tag or ":cassette" + cassette_handler = create_cassette_handler(cassette_tag) + tape_info = detect_tape_info() + end + + common_autoboot.debug_frame_num(frame_num) + + if tape_info and tape_info.file_type == TAPE_TYPE_MACHINE then + boot_machine() + else + boot_basic() + end +end + +subscription = emu.add_machine_frame_notifier(process_frame) diff --git a/package/batocera/core/batocera-configgen/data/mame/autoboot_scripts/atom_flop.lua b/package/batocera/core/batocera-configgen/data/mame/autoboot_scripts/atom_flop.lua new file mode 100644 index 00000000000..4359dd6332e --- /dev/null +++ b/package/batocera/core/batocera-configgen/data/mame/autoboot_scripts/atom_flop.lua @@ -0,0 +1,407 @@ +-- atom_flop.lua +-- Autoboot Acorn Atom floppy disks by inspecting the Atom DOS directory. + +local zip_util = dofile("/usr/share/batocera/configgen/data/mame/autoboot_scripts/autoboot_zip.lua") + +local DISK_SCAN_BYTES = 512 +local DIR_ENTRY_SIZE = 8 +local DIR_ENTRY_COUNT = 32 +local TYPE_DELAY = 120 + +print("[Atom Flop] Loaded from: " .. debug.getinfo(1, 'S').source) + +local frame_num = 0 +local boot_program = nil +local boot_started = false +local boot_stage = 0 +local next_stage_frame = 0 +local f14_key_down = false +local digit_key_down = nil +local lowercase_started = false +local lowercase_text = nil +local lowercase_index = 1 +local lowercase_key = nil +local lowercase_done_frame = 0 +local lowercase_next_frame = 0 +local lowercase_release_frame = 0 +local lowercase_close_posted = false + +local DOS_SETTLE_FRAMES = 140 +local F14_PREFIX_DELAY = 40 +local F14_DIGIT_DELAY = 90 +local F14_SUFFIX_DELAY = 130 +local F14_KEY_HOLD_FRAMES = 20 +local JSW2_PREFIX_DELAY = 40 +local JSW2_DIGIT_DELAY = 500 +local JSW2_SUFFIX_DELAY = 130 +local DIGIT_KEY_HOLD_FRAMES = 200 +local LOWERCASE_START_DELAY = 90 +local LOWERCASE_KEY_HOLD_FRAMES = 16 +local LOWERCASE_KEY_GAP_FRAMES = 12 + +local LETTER_KEYS = { + A = { "Y6", 0x08 }, + B = { "Y5", 0x08 }, + C = { "Y4", 0x08 }, + D = { "Y3", 0x08 }, + E = { "Y2", 0x08 }, + F = { "Y1", 0x08 }, + G = { "Y0", 0x08 }, + H = { "Y9", 0x10 }, + I = { "Y8", 0x10 }, + J = { "Y7", 0x10 }, + K = { "Y6", 0x10 }, + L = { "Y5", 0x10 }, + M = { "Y4", 0x10 }, + N = { "Y3", 0x10 }, + O = { "Y2", 0x10 }, + P = { "Y1", 0x10 }, + Q = { "Y0", 0x10 }, + R = { "Y9", 0x20 }, + S = { "Y8", 0x20 }, + T = { "Y7", 0x20 }, + U = { "Y6", 0x20 }, + V = { "Y5", 0x20 }, + W = { "Y4", 0x20 }, + X = { "Y3", 0x20 }, + Y = { "Y2", 0x20 }, + Z = { "Y1", 0x20 }, +} + +local DIGIT_KEYS = { + ["1"] = { "Y2", 0x02 }, + ["2"] = { "Y1", 0x02 }, + ["3"] = { "Y0", 0x02 }, + ["4"] = { "Y9", 0x04 }, + ["5"] = { "Y8", 0x04 }, + ["6"] = { "Y7", 0x04 }, + ["7"] = { "Y6", 0x04 }, + ["8"] = { "Y5", 0x04 }, + ["9"] = { "Y4", 0x04 }, + ["0"] = { "Y3", 0x02 }, +} + +local PREFERRED_PROGRAMS = { + -- Prefer colour variants when both colour and mono are present. + "CCHUCK", + "CGALA", + + "LOADER", + "JOE", + "EHRUN", + "F14RUN", + "GALAXI", + "JSW2RUN", + "JSWRUN", + "JUNGLE", + "MMRUN", + "RUNME", + "REPLOAD", +} + +local function find_disk_in_zip(zippath) + return zip_util.find_zip_entry(zippath, function(n) + return n:lower():match("%.dsk$") or n:lower():match("%.40t$") + end) +end + +local function is_atom_floppy_name(path) + local lower = path:lower() + return lower:match("%.dsk$") or lower:match("%.40t$") or lower:match("%.mfi$") or + lower:match("%.dfi$") or lower:match("%.hfe$") or lower:match("%.mfm$") or + lower:match("%.td0$") or lower:match("%.imd$") or lower:match("%.d77$") or + lower:match("%.d88$") or lower:match("%.1dd$") or lower:match("%.cqm$") or + lower:match("%.cqi$") +end + +local function find_floppy_path() + for _, img in pairs(manager.machine.images) do + if img.exists and img.filename and #img.filename > 0 then + local fn = img.filename + local lower = fn:lower() + if is_atom_floppy_name(lower) or lower:find("%.zip/") then + print("[Atom Flop] Floppy image: " .. fn) + return fn + end + if lower:match("%.zip$") then + local entry = find_disk_in_zip(fn) + if entry then + local vpath = fn .. "/" .. entry + print("[Atom Flop] Floppy image (zip): " .. vpath) + return vpath + end + end + end + end + return nil +end + +local function clean_dir_name(raw) + local chars = {} + for i = 1, #raw do + local c = raw:byte(i) + if c == 0x00 then break end + if c < 0x20 or c > 0x7e then return nil end + table.insert(chars, string.char(c)) + end + + local name = table.concat(chars):match("^%s*(.-)%s*$") + if name == "" then return nil end + return name +end + +local function parse_atom_dir(data) + local names = {} + local present = {} + if not data or #data < DIR_ENTRY_SIZE then return names, present end + + for idx = 0, DIR_ENTRY_COUNT - 1 do + local off = idx * DIR_ENTRY_SIZE + 1 + if off + DIR_ENTRY_SIZE - 1 > #data then break end + local raw = data:sub(off, off + DIR_ENTRY_SIZE - 1) + local name = clean_dir_name(raw) + if name then + table.insert(names, name) + present[name:upper()] = name + print(string.format("[Atom Flop] Dir[%d]: %s", idx, name)) + end + end + + return names, present +end + +local function choose_from_directory(names, present) + for _, wanted in ipairs(PREFERRED_PROGRAMS) do + if present[wanted] then return present[wanted] end + end + + for _, name in ipairs(names) do + local upper = name:upper() + if upper:match("RUN$") and not upper:match("SCRRUN$") then + return name + end + end + + if present["RUNME"] then return present["RUNME"] end + return nil +end + +local function normalize_boot_program(program) + if program and program:upper() == "REPLOADED" then + return "REPLOAD" + end + return program +end + +local function detect_boot_program() + print("[Atom Flop] --- Floppy autoboot detection ---") + + local path = find_floppy_path() + if not path then + print("[Atom Flop] No floppy image found.") + return nil + end + + local data = zip_util.read_bytes(path, DISK_SCAN_BYTES) + if data then + local names, present = parse_atom_dir(data) + local from_dir = choose_from_directory(names, present) + if from_dir then + print("[Atom Flop] Selected from directory: " .. from_dir) + return normalize_boot_program(from_dir) + end + end + + print("[Atom Flop] No runnable program detected.") + return nil +end + +local function key_1_field() + local port = manager.machine.ioport.ports[":Y2"] + return port and port:field(0x02) or nil +end + +local function digit_field(digit) + local keydef = DIGIT_KEYS[digit] + if not keydef then return nil end + local port = manager.machine.ioport.ports[":" .. keydef[1]] + return port and port:field(keydef[2]) or nil +end + +local function shift_field() + local port = manager.machine.ioport.ports[":Y10"] + return port and port:field(0x80) or nil +end + +local function letter_field(letter) + local keydef = LETTER_KEYS[letter:upper()] + if not keydef then return nil end + local port = manager.machine.ioport.ports[":" .. keydef[1]] + return port and port:field(keydef[2]) or nil +end + +local function set_digit_key(digit, pressed) + local field = digit_field(digit) + if not field then + print("[Atom Flop] Missing key field for digit " .. digit .. ", falling back to keypost") + if pressed then + emu.keypost(digit) + end + return + end + if pressed then + field:set_value(1) + digit_key_down = digit + else + field:clear_value() + digit_key_down = nil + end +end + +local function set_key_1(pressed) + local field = key_1_field() + if not field then + print("[Atom Flop] Missing key field for 1") + return + end + if pressed then + field:set_value(1) + f14_key_down = true + else + field:clear_value() + f14_key_down = false + end +end + +local function has_lowercase(text) + return text:find("%l") ~= nil +end + +local function start_lowercase_text(text, target_frame) + if frame_num == target_frame and not lowercase_started then + print("[Atom Flop] Typing lowercase via matrix: " .. text) + lowercase_text = text + lowercase_index = 1 + lowercase_next_frame = frame_num + lowercase_started = true + end +end + +local function release_lowercase_key() + if lowercase_key then + local field = letter_field(lowercase_key) + if field then field:clear_value() end + lowercase_key = nil + end + local shift = shift_field() + if shift then shift:clear_value() end + lowercase_next_frame = frame_num + LOWERCASE_KEY_GAP_FRAMES +end + +local function process_lowercase_text() + if not lowercase_started or lowercase_done_frame ~= 0 then return end + + if lowercase_key and frame_num >= lowercase_release_frame then + release_lowercase_key() + lowercase_index = lowercase_index + 1 + if lowercase_index > #lowercase_text then + lowercase_done_frame = lowercase_next_frame + end + return + end + + if lowercase_key or frame_num < lowercase_next_frame then return end + + local ch = lowercase_text:sub(lowercase_index, lowercase_index) + local field = letter_field(ch) + local shift = shift_field() + if not field or not shift then + print("[Atom Flop] Missing lowercase key field for: " .. ch) + lowercase_done_frame = frame_num + return + end + + shift:set_value(1) + field:set_value(1) + lowercase_key = ch + lowercase_release_frame = frame_num + LOWERCASE_KEY_HOLD_FRAMES +end + +local function keypost_at(text, target_frame) + if frame_num == target_frame then + print("[Atom Flop] Typing: " .. text:gsub("\n", "\\n")) + emu.keypost(text) + end +end + +local function process_frame() + frame_num = frame_num + 1 + + if not boot_program then + boot_program = detect_boot_program() + if not boot_program then + subscription:unsubscribe() + return + end + end + + if not boot_started then + boot_started = true + print("[Atom Flop] Booting with *RUN\"" .. boot_program .. "\"") + end + + keypost_at("*DOS\n", TYPE_DELAY) + + if boot_stage == 0 and frame_num == TYPE_DELAY then + print("[Atom Flop] Queueing: *DOS") + next_stage_frame = frame_num + DOS_SETTLE_FRAMES + boot_stage = 1 + elseif boot_stage == 1 and frame_num >= next_stage_frame then + print("[Atom Flop] Entering stage 3 at frame " .. frame_num .. ", next_stage_frame=" .. next_stage_frame) + print("[Atom Flop] Typing run command for: " .. boot_program) + boot_stage = 3 + end + + if boot_stage == 3 and boot_program:upper() == "F14RUN" then + keypost_at("*RUN\"F", next_stage_frame + F14_PREFIX_DELAY) + if frame_num == next_stage_frame + F14_DIGIT_DELAY then + print("[Atom Flop] Pressing Atom key 1") + set_key_1(true) + elseif f14_key_down and frame_num == next_stage_frame + F14_DIGIT_DELAY + F14_KEY_HOLD_FRAMES then + set_key_1(false) + end + keypost_at("4RUN\"\n", next_stage_frame + F14_SUFFIX_DELAY) + + elseif boot_stage == 3 and boot_program:upper() == "JSW2RUN" then + keypost_at("*RUN\"JSW", next_stage_frame + JSW2_PREFIX_DELAY) + if frame_num == next_stage_frame + JSW2_DIGIT_DELAY then + print("[Atom Flop] Pressing Atom key 2") + set_digit_key("2", true) + elseif digit_key_down == "2" and frame_num == next_stage_frame + JSW2_DIGIT_DELAY + DIGIT_KEY_HOLD_FRAMES then + set_digit_key("2", false) + end + keypost_at("RUN\"\n", next_stage_frame + JSW2_SUFFIX_DELAY) + + elseif boot_stage == 3 and has_lowercase(boot_program) then + keypost_at("*RUN\"", next_stage_frame + F14_PREFIX_DELAY) + start_lowercase_text(boot_program, next_stage_frame + LOWERCASE_START_DELAY) + process_lowercase_text() + if lowercase_done_frame ~= 0 and not lowercase_close_posted and frame_num >= lowercase_done_frame + LOWERCASE_KEY_GAP_FRAMES then + print("[Atom Flop] Closing lowercase run command") + emu.keypost("\"\n") + lowercase_close_posted = true + end + elseif boot_stage == 3 then + keypost_at("*RUN\"" .. boot_program .. "\"\n", next_stage_frame + F14_PREFIX_DELAY) + end + + if boot_stage == 3 and has_lowercase(boot_program) and lowercase_close_posted and frame_num > lowercase_done_frame + 60 then + subscription:unsubscribe() + elseif boot_stage == 3 and not has_lowercase(boot_program) and frame_num > next_stage_frame + F14_SUFFIX_DELAY + 60 then + if f14_key_down then set_key_1(false) end + if digit_key_down then set_digit_key(digit_key_down, false) end + subscription:unsubscribe() + end +end + +subscription = emu.add_machine_frame_notifier(process_frame) diff --git a/package/batocera/core/batocera-configgen/data/mame/autoboot_scripts/autoboot_common.lua b/package/batocera/core/batocera-configgen/data/mame/autoboot_scripts/autoboot_common.lua new file mode 100644 index 00000000000..ea093491d09 --- /dev/null +++ b/package/batocera/core/batocera-configgen/data/mame/autoboot_scripts/autoboot_common.lua @@ -0,0 +1,226 @@ +-- autoboot_common.lua + +-- Define common helper functions that will be used by other scripts. +-- These functions assume 'button' and 'frame_num' are available in the calling scope, +-- or they will need to be passed as arguments, which makes them truly independent. +-- For MAME Lua scripts, it's common to expect 'button' and 'frame_num' to be managed +-- by the main calling script. + +-- --- Common Constants --- +local DEFAULT_BUTTON_PRESS_DURATION = 100 -- Default duration for button presses in frames +local DEFAULT_CASSETTE_MOTOR_OFF_DELAY_FRAMES = 60 -- Default duration to wait after cassette stopped (after throttle) + +-- Helper function to press and release a button at specific frame numbers +-- Assumes 'frame_num' is globally accessible (or passed in) and 'button' table is global +local function press_and_release_button(button_table, current_frame_num, button_name, press_frame, duration) + local release_frame = press_frame + duration + -- emu.print_info(string.format("Common: %s (Current Frame: %d, Target Press: %d, Target Release: %d)", + -- button_name, current_frame_num, press_frame, release_frame)) + + if current_frame_num == press_frame then + emu.print_info("!!!! PRESSING button: " .. button_name .. " at frame " .. current_frame_num .. " (Duration: " .. duration .. " frames) !!!!") + if button_table[button_name] then + button_table[button_name]:set_value(1) + else + emu.print_info("Warning: Button '" .. button_name .. "' not found for press.") + end + elseif current_frame_num == release_frame then + emu.print_info("!!!! RELEASING button: " .. button_name .. " at frame " .. current_frame_num .. " !!!!") + if button_table[button_name] then + button_table[button_name]:set_value(0) + else + emu.print_info("Warning: Button '" .. button_name .. "' not found for release.") + end + end +end + +-- Helper function to type a string at a specific frame number +-- Assumes 'frame_num' is globally accessible (or passed in) +local function type_string_at_frame(current_frame_num, text, start_frame) + if current_frame_num == start_frame then + emu.print_info("Typing: " .. text:gsub("\n", "\\n") .. " at frame " .. current_frame_num) + emu.keypost(text) + end +end + +-- Function to populate the button table +-- It takes an empty table and populates it. +local function populate_button_table(target_button_table) + for key, ports in pairs(manager.machine.ioport.ports) do + for field_name, field in pairs(ports.fields) do + target_button_table[field_name] = field + end + end +end + +-- Function to get a list of device tag, e.g. :cassette. Some system uses :cassette1 +-- local function print_device_tag() +-- emu.print_info("--- List of Device Tag ---") +-- print(manager.machine.images:at(3).device.tag) +-- emu.print_info("--- End of Device Tag ---") +-- end + +local function print_image_info() + emu.print_info("--- Loaded Image Information ---") + local num_images = manager.machine.images:size() + emu.print_info("Total loaded images: " .. num_images) + + if num_images == 0 then + emu.print_info("No images currently loaded.") + else + -- Iterate through each image slot + for i = 1, num_images do + local image = manager.machine.images:at(i) + if image then + emu.print_info(string.format(" Image %d:", i)) + emu.print_info(string.format(" Device Tag: %s", image.device.tag or "N/A")) + emu.print_info(string.format(" Filename: %s", image.filename or "N/A")) + emu.print_info(string.format(" Mounted: %s", tostring(image.mounted))) + emu.print_info(string.format(" Software Name: %s", image.software_name or "N/A")) -- From softlist, if applicable + emu.print_info(string.format(" Is Cassette: %s", tostring(image.is_cassette))) + emu.print_info(string.format(" Is Harddisk: %s", tostring(image.is_harddisk))) + -- Add more properties as needed from the MAME Lua image_device documentation + else + emu.print_info(string.format(" Image %d: (Error: Could not retrieve image object)", i)) + end + end + end + emu.print_info("--- End of Image Information ---") +end + +-- Function to print the contents of the button table +local function print_button_table(button_table_to_print) + emu.print_info("--- Contents of 'button' table ---") + for field_name, field_object in pairs(button_table_to_print) do + local current_value = field_object.value + if current_value == nil then current_value = -1 end + + local type_class_name = field_object.type_class + if type_class_name == nil then type_class_name = "unknown" end + + local info_string = string.format( + "Key: '%s', Field Name: '%s', Current Value: %d, Type Class: %s", + field_name, + field_object.name, + current_value, + type_class_name + ) + emu.print_info(info_string) + end + emu.print_info("--- End of 'button' table contents ---") +end + +local function play_cassette_at_frame(current_frame_num, cassette_device_tag, start_frame) + if current_frame_num == start_frame then + local cassette_device = manager.machine.cassettes[cassette_device_tag] + if cassette_device then + cassette_device:play() + emu.print_info("Playing cassette '" .. cassette_device_tag .. "' at frame " .. current_frame_num) + else + emu.print_info("WARNING: Cassette device '" .. cassette_device_tag .. "' not found for play at frame " .. current_frame_num) + end + end +end + +-- New function for handling cassette throttling AND detecting load completion +-- This function now takes current_frame_num and returns true ONCE when loading is done. +-- It uses its own internal state variables, so they are not polluting the calling script. +local function create_cassette_handler(cassette_device_tag) + local motor_state_false_frame_num = 0 + local load_done_flag = false -- This flag will be set to true ONCE when loading is complete + local was_playing_and_throttling_off = false -- Tracks if we were actively loading and throttling was off + + return function(current_frame_num, delay_frames_after_motor_off) + local cassette_device = manager.machine.cassettes[cassette_device_tag] + if not cassette_device then + emu.print_info("WARNING: Cassette device with tag '" .. cassette_device_tag .. "' not found!") + return false + end + + -- Handle throttling/frameskip + if cassette_device.is_stopped == false and cassette_device.motor_state == true then + manager.machine.video.throttled = false + manager.machine.video.frameskip = 12 + was_playing_and_throttling_off = true -- Mark that it was actively playing (throttle disabled) + else + manager.machine.video.throttled = true + manager.machine.video.frameskip = 0 + -- If it was just playing and throttling was off, and now it's stopped/motor off + if was_playing_and_throttling_off and motor_state_false_frame_num == 0 then + motor_state_false_frame_num = current_frame_num -- Mark the frame motor just turned off + emu.print_info("Cassette motor OFF detected (and was loading) at frame " .. current_frame_num) + end + was_playing_and_throttling_off = false -- Reset, as it's no longer actively playing + end + + + -- Detect load completion AFTER the motor turns off and the delay passes + if load_done_flag == false then -- Only process if load hasn't been marked done yet + if motor_state_false_frame_num ~= 0 then -- Meaning the motor has turned off at some point + -- This is the delay check: + if current_frame_num > motor_state_false_frame_num + delay_frames_after_motor_off then + load_done_flag = true -- Mark loading as complete + emu.print_info("Cassette loading completed at frame " .. current_frame_num) + emu.print_info("Load done!!! (after delay)") + return true -- Signal that loading is done + end + end + end + + return false -- Indicate loading is not yet done + end +end + +local function create_delay_logic() + local delay_start_frame = 0 + local delay_in_frames = 0 + local delay_active = false + + -- This is the function that will be called continuously to check the delay state + return function(current_frame_num, initiate_delay_frames) + -- If initiate_delay_frames is provided, it means we want to START a new delay + if initiate_delay_frames and not delay_active then + delay_start_frame = current_frame_num + delay_in_frames = initiate_delay_frames + delay_active = true + emu.print_info("Delay initiated at frame " .. current_frame_num .. " for " .. delay_in_frames .. " frames.") + return false -- Delay just started, not done yet + end + + -- If a delay is active, check if it's over + if delay_active then + if current_frame_num >= delay_start_frame + delay_in_frames then + delay_active = false -- Delay is complete + emu.print_info("Delay completed at frame " .. current_frame_num) + return true -- Signal delay is over + else + return false -- Delay is still active + end + end + + return true -- No delay was active or initiated, so it's "done" by default. + end +end + +local function debug_frame_num(frame_num) + if frame_num % 100 == 0 then -- The modulo operator (%) gives the remainder of a division + emu.print_info("Current Frame: " .. frame_num) + end +end + + +-- Return a table of functions +return { + press_and_release = press_and_release_button, + type_at_frame = type_string_at_frame, + populate_buttons = populate_button_table, + print_buttons = print_button_table, + play_cassette_at_frame = play_cassette_at_frame, + create_cassette_handler = create_cassette_handler, + create_delay_logic = create_delay_logic, + DEFAULT_BUTTON_PRESS_DURATION = DEFAULT_BUTTON_PRESS_DURATION, + DEFAULT_CASSETTE_MOTOR_OFF_DELAY_FRAMES = DEFAULT_CASSETTE_MOTOR_OFF_DELAY_FRAMES, + print_image_info = print_image_info, + print_device_tag = print_device_tag, + debug_frame_num = debug_frame_num, +} \ No newline at end of file diff --git a/package/batocera/core/batocera-configgen/data/mame/autoboot_scripts/autoboot_zip.lua b/package/batocera/core/batocera-configgen/data/mame/autoboot_scripts/autoboot_zip.lua new file mode 100644 index 00000000000..b0a7353c6e6 --- /dev/null +++ b/package/batocera/core/batocera-configgen/data/mame/autoboot_scripts/autoboot_zip.lua @@ -0,0 +1,120 @@ +-- autoboot_zip.lua +-- Shared ZIP and file-reading utilities for MAME autoboot scripts. +-- +-- Usage: +-- local zip_util = dofile("/usr/share/batocera/configgen/data/mame/autoboot_scripts/autoboot_zip.lua") +-- local data = zip_util.read_bytes(mame_filename, 4096) +-- local entry = zip_util.find_zip_entry(zippath, function(n) return n:lower():match("%.dsk$") end) + +local function u16le(s, pos) + return s:byte(pos) | (s:byte(pos+1) << 8) +end + +local function u32le(s, pos) + return s:byte(pos) | (s:byte(pos+1) << 8) | (s:byte(pos+2) << 16) | (s:byte(pos+3) << 24) +end + +-- Parse a MAME image path into (kind, zippath, entrypath). +-- Virtual zip path: /roms/game.zip/game.cas → "zip", "/roms/game.zip", "game.cas" +-- Plain file path: /roms/game.cas → "plain", nil, "/roms/game.cas" +local function parse_mame_path(fullpath) + local p = fullpath:gsub("\\", "/") + local zippath, entry = p:match("^(.*%.zip)/(.+)$") + if zippath and entry then return "zip", zippath, entry end + return "plain", nil, p +end + +-- Walk a ZIP file's local file headers and return the first entry name +-- for which match_fn(entryname) returns true, or nil. +local function find_zip_entry(zippath, match_fn) + local f = io.open(zippath, "rb") + if not f then return nil end + local zipdata = f:read("*a") + f:close() + if not zipdata or #zipdata < 30 then return nil end + + local pos = 1 + while pos <= #zipdata - 30 do + if zipdata:byte(pos) ~= 0x50 or zipdata:byte(pos+1) ~= 0x4B or + zipdata:byte(pos+2) ~= 0x03 or zipdata:byte(pos+3) ~= 0x04 then break end + local comp_size = u32le(zipdata, pos+18) + local fname_len = u16le(zipdata, pos+26) + local extra_len = u16le(zipdata, pos+28) + local ename = zipdata:sub(pos+30, pos+30+fname_len-1) + local data_start = pos + 30 + fname_len + extra_len + if match_fn(ename) then return ename end + pos = data_start + comp_size + end + return nil +end + +-- Read bytes of a named entry from a ZIP archive. +-- method=0 (stored) : pure Lua, no external process. +-- method=8 (deflated): Python3 fallback. +-- max_bytes: maximum bytes to read, or nil to read the whole entry. +-- Returns a Lua byte-string, or nil on failure. +local function read_entry_from_zip(zippath, entryname, max_bytes) + local f = io.open(zippath, "rb") + if not f then return nil end + local zipdata = f:read("*a") + f:close() + if not zipdata or #zipdata < 30 then return nil end + + local pos = 1 + while pos <= #zipdata - 30 do + if zipdata:byte(pos) ~= 0x50 or zipdata:byte(pos+1) ~= 0x4B or + zipdata:byte(pos+2) ~= 0x03 or zipdata:byte(pos+3) ~= 0x04 then break end + local method = u16le(zipdata, pos+8) + local comp_size = u32le(zipdata, pos+18) + local uncomp_size = u32le(zipdata, pos+22) + local fname_len = u16le(zipdata, pos+26) + local extra_len = u16le(zipdata, pos+28) + local entry_name = zipdata:sub(pos+30, pos+30+fname_len-1) + local data_start = pos + 30 + fname_len + extra_len + + if entry_name:lower() == entryname:lower() then + if method == 0 then + local nbytes = max_bytes and math.min(uncomp_size, max_bytes) or uncomp_size + return zipdata:sub(data_start, data_start + nbytes - 1) + elseif method == 8 then + local zq = zippath:gsub("'", "'\\''") + local nq = entryname:gsub("'", "'\\''") + local pipe = io.popen(string.format( + "python3 -c 'import zipfile,sys; zf=zipfile.ZipFile(sys.argv[1]); sys.stdout.buffer.write(zf.read(sys.argv[2]))' '%s' '%s' 2>/dev/null", + zq, nq), "r") + if not pipe then return nil end + local raw = max_bytes and pipe:read(max_bytes) or pipe:read("*a") + pipe:close() + return (raw and #raw > 0) and raw or nil + end + return nil + end + + pos = data_start + comp_size + end + return nil +end + +-- Read bytes from a file, handling both plain files and MAME virtual zip paths. +-- max_bytes: maximum bytes to read, or nil to read everything. +-- Returns a Lua byte-string, or nil on failure. +local function read_bytes(mame_filename, max_bytes) + local kind, zippath, entrypath = parse_mame_path(mame_filename) + if kind == "zip" then + return read_entry_from_zip(zippath, entrypath, max_bytes) + end + local f = io.open(entrypath, "rb") + if not f then return nil end + local data = max_bytes and f:read(max_bytes) or f:read("*a") + f:close() + return data +end + +return { + u16le = u16le, + u32le = u32le, + parse_mame_path = parse_mame_path, + find_zip_entry = find_zip_entry, + read_entry_from_zip = read_entry_from_zip, + read_bytes = read_bytes, +} diff --git a/package/batocera/core/batocera-configgen/data/mame/autoboot_scripts/bbc_cass.lua b/package/batocera/core/batocera-configgen/data/mame/autoboot_scripts/bbc_cass.lua new file mode 100644 index 00000000000..9dca8260ae1 --- /dev/null +++ b/package/batocera/core/batocera-configgen/data/mame/autoboot_scripts/bbc_cass.lua @@ -0,0 +1,81 @@ +-- bbc_cass.lua + +-- WIP: Some games require *RUN instead of CHAIN, WIP WIP!! + +-- Load common autoboot functions +local common_autoboot = dofile("/usr/share/batocera/configgen/data/mame/autoboot_scripts/autoboot_common.lua") + +-- Script-wide variables +local button = {} +local frame_num = 0 + +common_autoboot.populate_buttons(button) + +-- Print the button table contents for debugging +-- common_autoboot.print_buttons(button) + +-- --- Constants for this specific script --- +local BUTTON_PRESS_DURATION = common_autoboot.DEFAULT_BUTTON_PRESS_DURATION +local CASSETTE_MOTOR_OFF_DELAY_FRAMES = common_autoboot.DEFAULT_CASSETTE_MOTOR_OFF_DELAY_FRAMES + +local function boot_default(cassette_handler) + common_autoboot.type_at_frame(frame_num, "*TAPE\n", 50, BUTTON_PRESS_DURATION) + common_autoboot.type_at_frame(frame_num, "CHAIN\"\"\n", 100, BUTTON_PRESS_DURATION) + common_autoboot.play_cassette_at_frame(frame_num, ":cassette", 300) + + local cassette_load_done = cassette_handler(frame_num, CASSETTE_MOTOR_OFF_DELAY_FRAMES) + + if cassette_load_done then + -- emu.keypost('RUN\n') + end +end + +-- --- Main Script Logic --- + +-- Determine the currently loaded software name +local current_software_name = manager.machine.images:at(1).filename + +-- Map software names to their respective boot functions +local boot_sequences = { + ["default"] = boot_default, +} + +-- --- Cassette Loading Handler Setup --- +local cassette_handler = common_autoboot.create_cassette_handler(":cassette") + +-- MAME machine frame notification callback +local function process_frame() + frame_num = frame_num + 1 + + -- Initial setup/info display at frame 1 + if frame_num == 1 then + emu.print_info("System Driver: " .. emu.romname()) + emu.print_info("Loaded Software Name (from image filename): " .. current_software_name) + + -- Determine which profile will be used + if boot_sequences[current_software_name] then + emu.print_info("--- Booting using game specific profile: " .. current_software_name .. " ---") + else + emu.print_info("--- Booting using default profile ---") + end + end + + -- --- Print current frame number every 100 frames --- + if frame_num % 100 == 0 then -- The modulo operator (%) gives the remainder of a division + emu.print_info("Current Frame: " .. frame_num) + end + + -- Execute the relevant boot sequence based on the loaded software + local boot_function = boot_sequences[current_software_name] + + if not boot_function then -- If specific function not found + boot_function = boot_sequences["default"] -- Assign the default function + end + + if boot_function then + boot_function(cassette_handler) + end +end + +-- Subscribe to machine frame notifications +subscription = emu.add_machine_frame_notifier(process_frame) \ No newline at end of file diff --git a/package/batocera/core/batocera-configgen/data/mame/autoboot_scripts/bbcb_flop.lua b/package/batocera/core/batocera-configgen/data/mame/autoboot_scripts/bbcb_flop.lua new file mode 100644 index 00000000000..b4bcf835ca4 --- /dev/null +++ b/package/batocera/core/batocera-configgen/data/mame/autoboot_scripts/bbcb_flop.lua @@ -0,0 +1,79 @@ +-- bbcb_flop.lua + +-- Load common autoboot functions +local common_autoboot = dofile("/usr/share/batocera/configgen/data/mame/autoboot_scripts/autoboot_common.lua") + +-- Script-wide variables +local button = {} +local frame_num = 0 + +common_autoboot.populate_buttons(button) + +-- Print the button table contents for debugging +-- common_autoboot.print_buttons(button) + +-- --- Constants for this specific script --- +local BUTTON_PRESS_DURATION = common_autoboot.DEFAULT_BUTTON_PRESS_DURATION +local CASSETTE_MOTOR_OFF_DELAY_FRAMES = common_autoboot.DEFAULT_CASSETTE_MOTOR_OFF_DELAY_FRAMES + +local function boot_default(cassette_handler) + common_autoboot.type_at_frame(frame_num, "*CAT\n", 50, BUTTON_PRESS_DURATION) + common_autoboot.type_at_frame(frame_num, "*EXEC !BOOT\n", 250, BUTTON_PRESS_DURATION) + common_autoboot.play_cassette_at_frame(frame_num, ":cassette", 300) + + local cassette_load_done = cassette_handler(frame_num, CASSETTE_MOTOR_OFF_DELAY_FRAMES) + + if cassette_load_done then + -- emu.keypost('RUN\n') + end +end + +-- --- Main Script Logic --- + +-- Determine the currently loaded software name +local current_software_name = manager.machine.images:at(1).filename + +-- Map software names to their respective boot functions +local boot_sequences = { + ["default"] = boot_default, +} + +-- --- Cassette Loading Handler Setup --- +local cassette_handler = common_autoboot.create_cassette_handler(":cassette") + +-- MAME machine frame notification callback +local function process_frame() + frame_num = frame_num + 1 + + -- Initial setup/info display at frame 1 + if frame_num == 1 then + emu.print_info("System Driver: " .. emu.romname()) + emu.print_info("Loaded Software Name (from image filename): " .. current_software_name) + + -- Determine which profile will be used + if boot_sequences[current_software_name] then + emu.print_info("--- Booting using game specific profile: " .. current_software_name .. " ---") + else + emu.print_info("--- Booting using default profile ---") + end + end + + -- --- Print current frame number every 100 frames --- + if frame_num % 100 == 0 then -- The modulo operator (%) gives the remainder of a division + emu.print_info("Current Frame: " .. frame_num) + end + + -- Execute the relevant boot sequence based on the loaded software + local boot_function = boot_sequences[current_software_name] + + if not boot_function then -- If specific function not found + boot_function = boot_sequences["default"] -- Assign the default function + end + + if boot_function then + boot_function(cassette_handler) + end +end + +-- Subscribe to machine frame notifications +subscription = emu.add_machine_frame_notifier(process_frame) \ No newline at end of file diff --git a/package/batocera/core/batocera-configgen/data/mame/autoboot_scripts/bbcb_flop_orig.lua b/package/batocera/core/batocera-configgen/data/mame/autoboot_scripts/bbcb_flop_orig.lua new file mode 100644 index 00000000000..3d5e54fd980 --- /dev/null +++ b/package/batocera/core/batocera-configgen/data/mame/autoboot_scripts/bbcb_flop_orig.lua @@ -0,0 +1,79 @@ +-- bbcb_flop_orig.lua + +-- Load common autoboot functions +local common_autoboot = dofile("/usr/share/batocera/configgen/data/mame/autoboot_scripts/autoboot_common.lua") + +-- Script-wide variables +local button = {} +local frame_num = 0 + +common_autoboot.populate_buttons(button) + +-- Print the button table contents for debugging +-- common_autoboot.print_buttons(button) + +-- --- Constants for this specific script --- +local BUTTON_PRESS_DURATION = common_autoboot.DEFAULT_BUTTON_PRESS_DURATION +local CASSETTE_MOTOR_OFF_DELAY_FRAMES = common_autoboot.DEFAULT_CASSETTE_MOTOR_OFF_DELAY_FRAMES + +local function boot_default(cassette_handler) + common_autoboot.type_at_frame(frame_num, "*CAT\n", 50, BUTTON_PRESS_DURATION) + common_autoboot.type_at_frame(frame_num, "*EXEC !BOOT\n", 250, BUTTON_PRESS_DURATION) + common_autoboot.play_cassette_at_frame(frame_num, ":cassette", 300) + + local cassette_load_done = cassette_handler(frame_num, CASSETTE_MOTOR_OFF_DELAY_FRAMES) + + if cassette_load_done then + -- emu.keypost('RUN\n') + end +end + +-- --- Main Script Logic --- + +-- Determine the currently loaded software name +local current_software_name = manager.machine.images:at(1).filename + +-- Map software names to their respective boot functions +local boot_sequences = { + ["default"] = boot_default, +} + +-- --- Cassette Loading Handler Setup --- +local cassette_handler = common_autoboot.create_cassette_handler(":cassette") + +-- MAME machine frame notification callback +local function process_frame() + frame_num = frame_num + 1 + + -- Initial setup/info display at frame 1 + if frame_num == 1 then + emu.print_info("System Driver: " .. emu.romname()) + emu.print_info("Loaded Software Name (from image filename): " .. current_software_name) + + -- Determine which profile will be used + if boot_sequences[current_software_name] then + emu.print_info("--- Booting using game specific profile: " .. current_software_name .. " ---") + else + emu.print_info("--- Booting using default profile ---") + end + end + + -- --- Print current frame number every 100 frames --- + if frame_num % 100 == 0 then -- The modulo operator (%) gives the remainder of a division + emu.print_info("Current Frame: " .. frame_num) + end + + -- Execute the relevant boot sequence based on the loaded software + local boot_function = boot_sequences[current_software_name] + + if not boot_function then -- If specific function not found + boot_function = boot_sequences["default"] -- Assign the default function + end + + if boot_function then + boot_function(cassette_handler) + end +end + +-- Subscribe to machine frame notifications +subscription = emu.add_machine_frame_notifier(process_frame) \ No newline at end of file diff --git a/package/batocera/core/batocera-configgen/data/mame/autoboot_scripts/bk0010.lua b/package/batocera/core/batocera-configgen/data/mame/autoboot_scripts/bk0010.lua new file mode 100644 index 00000000000..fd707ce0c9c --- /dev/null +++ b/package/batocera/core/batocera-configgen/data/mame/autoboot_scripts/bk0010.lua @@ -0,0 +1,261 @@ +-- bk0010.lua + +-- Load common autoboot functions +local common_autoboot = dofile("/usr/share/batocera/configgen/data/mame/autoboot_scripts/autoboot_common.lua") + +-- Script-wide variables +local button = {} +local frame_num = 0 + +-- --- Load butons --- +common_autoboot.populate_buttons(button) + +-- --- Print Info --- +common_autoboot.print_image_info() +-- common_autoboot.print_buttons(button) + +-- --- Cassette Loading Handler Setup --- +local CASSETTE_SLOT = 1 +local CASSETTE_DEVICE_TAG = manager.machine.images:at(CASSETTE_SLOT).device.tag +local cassette_handler = common_autoboot.create_cassette_handler(CASSETTE_DEVICE_TAG) + + +-- --- Constants for this specific script --- +local BUTTON_PRESS_DURATION = common_autoboot.DEFAULT_BUTTON_PRESS_DURATION +local CASSETTE_MOTOR_OFF_DELAY_FRAMES = 100 -- common_autoboot.DEFAULT_CASSETTE_MOTOR_OFF_DELAY_FRAMES + +local function create_boot_default_sequence(frame_num, cassette_monitor_func, title_to_type) -- 'cassette_monitor_func' is the per-frame callable handler + local sequence_state = 0 + -- Define states for clarity + local STATES = { + INIT_PRE_LOAD_ACTIONS = 0, -- Initial actions before CLOAD/PLAY + PLAY_CASS_INITIATED = 1, -- CLOAD/PLAY commands sent, waiting for motor to spin up + LOADING_CASS = 2, -- Cassette motor is ON, monitoring for completion + CASS_LOAD_DONE = 3, -- Cassette has finished loading, waiting for post-load actions + POST_LOAD_ACTIONS = 4, -- Executing actions after cassette load + COMPLETE = 5 -- Sequence finished + } + + local post_load_start_frame = 0 -- Frame at which post-load actions began + + return function(current_frame_num) -- This inner function runs every frame + -- Always call cassette_monitor_func once we are in a state where loading should be active. + -- This ensures continuous monitoring, throttling, and detection of load completion. + if sequence_state >= STATES.LOADING_CASS and sequence_state < STATES.CASS_LOAD_DONE then + local cassette_load_done = cassette_monitor_func(current_frame_num, CASSETTE_MOTOR_OFF_DELAY_FRAMES) + if cassette_load_done then + sequence_state = STATES.CASS_LOAD_DONE -- Transition directly to CASS_LOAD_DONE + post_load_start_frame = current_frame_num -- Mark start for relative timing of post-actions + emu.print_info("Boot Default: Cassette loading is complete (triggered by handler). Transitioning to post-load actions.") + return -- Exit early for this frame after state change, to prevent double-execution + end + end + + -- --- State Machine Logic --- + if sequence_state == STATES.INIT_PRE_LOAD_ACTIONS then + -- Schedule initial pre-load actions + common_autoboot.type_at_frame(current_frame_num, "MO\n", 100) + common_autoboot.type_at_frame(current_frame_num, "M\n", 200) + common_autoboot.type_at_frame(current_frame_num, title_to_type .. "\n", 300) + common_autoboot.play_cassette_at_frame(current_frame_num, CASSETTE_DEVICE_TAG, 400) + + -- Transition to PLAY_CASS_INITIATED after these actions are scheduled (at the last target frame or beyond) + if current_frame_num >= 350 then + sequence_state = STATES.PLAY_CASS_INITIATED + emu.print_info("Boot Default: Pre-load actions scheduled. Waiting for cassette motor to engage.") + end + + elseif sequence_state == STATES.PLAY_CASS_INITIATED then + -- Wait for the cassette motor to actually spin up + local cassette_device = manager.machine.cassettes[CASSETTE_DEVICE_TAG] + if cassette_device and cassette_device.motor_state == true then + sequence_state = STATES.LOADING_CASS + emu.print_info("Boot Default: Cassette motor confirmed ON. Beginning continuous monitoring.") + end + + -- If sequence_state becomes CASS_LOAD_DONE (from the monitor above), this block is skipped, + -- and the logic will fall through to the next `elseif` for POST_LOAD_ACTIONS in the subsequent frame. + + elseif sequence_state == STATES.CASS_LOAD_DONE then + -- This state is primarily a transition point. + -- The 'RUN' command is NOT issued here. It's issued by the cassette_monitor_func internally. + -- We transition immediately to POST_LOAD_ACTIONS after load is detected. + sequence_state = STATES.POST_LOAD_ACTIONS + emu.print_info("Boot Default: Transitioning to post-load actions.") + + elseif sequence_state == STATES.POST_LOAD_ACTIONS then + -- --- Actions to perform AFTER cassette load is done --- + -- Use `post_load_start_frame` for relative timing + common_autoboot.type_at_frame(current_frame_num, "S\n", post_load_start_frame + 50, 1) + + -- Check if all post-load actions are completed + if current_frame_num >= post_load_start_frame + 100 + BUTTON_PRESS_DURATION then + sequence_state = STATES.COMPLETE + emu.print_info("Boot Default: All post-load actions completed. Sequence finished.") + end + + elseif sequence_state == STATES.COMPLETE then + -- Do nothing, sequence is finished. + end + end +end + +-- --- Main Script Logic --- + +-- Determine the currently loaded software name +local current_software_name = manager.machine.images:at(CASSETTE_SLOT).filename + +-- Map software names to their respective boot functions +local boot_sequences = { + aljur = create_boot_default_sequence(frame_num, cassette_handler, "ALJUR .COD"), + antix = create_boot_default_sequence(frame_num, cassette_handler, "Antix"), + apple = create_boot_default_sequence(frame_num, cassette_handler, "Apple"), + arkano = create_boot_default_sequence(frame_num, cassette_handler, "Arkano"), + arkanoid = create_boot_default_sequence(frame_num, cassette_handler, "Arkanoid"), + asprekl = create_boot_default_sequence(frame_num, cassette_handler, "Asp_rekl"), + ass = create_boot_default_sequence(frame_num, cassette_handler, "Ass"), + ass2 = create_boot_default_sequence(frame_num, cassette_handler, "Ass2"), + astrdrom = create_boot_default_sequence(frame_num, cassette_handler, "Astrdrom"), + baby = create_boot_default_sequence(frame_num, cassette_handler, "Baby"), + bally = create_boot_default_sequence(frame_num, cassette_handler, "Bally"), + bapple = create_boot_default_sequence(frame_num, cassette_handler, "Bapple"), + barb = create_boot_default_sequence(frame_num, cassette_handler, "Barb"), + barbar = create_boot_default_sequence(frame_num, cassette_handler, "Barbar"), + barman = create_boot_default_sequence(frame_num, cassette_handler, "Barman"), + batiskaf = create_boot_default_sequence(frame_num, cassette_handler, "Batiskaf"), + bigchess = create_boot_default_sequence(frame_num, cassette_handler, "Bigchess"), + bil = create_boot_default_sequence(frame_num, cassette_handler, "Bil"), + billiard = create_boot_default_sequence(frame_num, cassette_handler, "BILLIARD"), + biowar = create_boot_default_sequence(frame_num, cassette_handler, "Biowar"), + bkdemo = create_boot_default_sequence(frame_num, cassette_handler, "BKDEMO"), + bkpack = create_boot_default_sequence(frame_num, cassette_handler, "Bkpack"), + blcout = create_boot_default_sequence(frame_num, cassette_handler, "Blcout"), + blockout = create_boot_default_sequence(frame_num, cassette_handler, "BLOCKOUT"), + bolde2 = create_boot_default_sequence(frame_num, cassette_handler, "Bolde2"), + bolder2 = create_boot_default_sequence(frame_num, cassette_handler, "Bolder2"), + bolder3 = create_boot_default_sequence(frame_num, cassette_handler, "Bolder3"), + bomb4 = create_boot_default_sequence(frame_num, cassette_handler, "Bomb4"), + breach = create_boot_default_sequence(frame_num, cassette_handler, "breach"), + ['break'] = create_boot_default_sequence(frame_num, cassette_handler, "Break"), + breaking = create_boot_default_sequence(frame_num, cassette_handler, "Breaking"), + brhouse = create_boot_default_sequence(frame_num, cassette_handler, "Brhouse"), + bubbler = create_boot_default_sequence(frame_num, cassette_handler, "Bubbler"), + bubler = create_boot_default_sequence(frame_num, cassette_handler, "Bubler"), + cache = create_boot_default_sequence(frame_num, cassette_handler, "Cache"), + cave = create_boot_default_sequence(frame_num, cassette_handler, "Cave"), + cavedoc = create_boot_default_sequence(frame_num, cassette_handler, "Cave_doc"), + caveman = create_boot_default_sequence(frame_num, cassette_handler, "Cave_man"), + caveres = create_boot_default_sequence(frame_num, cassette_handler, "Cave_res"), + chaser = create_boot_default_sequence(frame_num, cassette_handler, "Chaser"), + checkers = create_boot_default_sequence(frame_num, cassette_handler, "Checkers"), + chess07 = create_boot_default_sequence(frame_num, cassette_handler, "Chess07"), + chess1 = create_boot_default_sequence(frame_num, cassette_handler, "Chess1"), + chess2 = create_boot_default_sequence(frame_num, cassette_handler, "Chess2"), + chess3 = create_boot_default_sequence(frame_num, cassette_handler, "Chess3"), + chess4 = create_boot_default_sequence(frame_num, cassette_handler, "Chess4"), + circler = create_boot_default_sequence(frame_num, cassette_handler, "CIRCLER"), + columns = create_boot_default_sequence(frame_num, cassette_handler, "Columns"), + columns2 = create_boot_default_sequence(frame_num, cassette_handler, "Columns2"), + comic = create_boot_default_sequence(frame_num, cassette_handler, "Comic"), + cosmic = create_boot_default_sequence(frame_num, cassette_handler, "COSMIC"), + cosmicb = create_boot_default_sequence(frame_num, cassette_handler, "Cosmic_b"), + courier = create_boot_default_sequence(frame_num, cassette_handler, "Courier"), + covox = create_boot_default_sequence(frame_num, cassette_handler, "covox"), + crack = create_boot_default_sequence(frame_num, cassette_handler, "Crack"), + dama = create_boot_default_sequence(frame_num, cassette_handler, "Dama"), + demsl = create_boot_default_sequence(frame_num, cassette_handler, "Demsl"), + demsl1 = create_boot_default_sequence(frame_num, cassette_handler, "Demsl1"), + demsl2 = create_boot_default_sequence(frame_num, cassette_handler, "Demsl2"), + demsl3 = create_boot_default_sequence(frame_num, cassette_handler, "Demsl3"), + desan = create_boot_default_sequence(frame_num, cassette_handler, "Desan"), + desandoc = create_boot_default_sequence(frame_num, cassette_handler, "Desandoc"), + desant = create_boot_default_sequence(frame_num, cassette_handler, "Desant"), + diamond = create_boot_default_sequence(frame_num, cassette_handler, "Diamond"), + digger = create_boot_default_sequence(frame_num, cassette_handler, "Digger"), + divers3 = create_boot_default_sequence(frame_num, cassette_handler, "Divers3"), + dragon = create_boot_default_sequence(frame_num, cassette_handler, "DRAGON.BIN"), + flier = create_boot_default_sequence(frame_num, cassette_handler, "FLIER"), + font = create_boot_default_sequence(frame_num, cassette_handler, "FONT"), + hobbitovl = create_boot_default_sequence(frame_num, cassette_handler, "HOBBIT.OVL"), + hobbit = create_boot_default_sequence(frame_num, cassette_handler, "HOBBIT"), + horror = create_boot_default_sequence(frame_num, cassette_handler, "Horror"), + horrors = create_boot_default_sequence(frame_num, cassette_handler, "Horror_s"), + house1 = create_boot_default_sequence(frame_num, cassette_handler, "House1"), + house2 = create_boot_default_sequence(frame_num, cassette_handler, "House2"), + indjon = create_boot_default_sequence(frame_num, cassette_handler, "Indjon"), + jetman = create_boot_default_sequence(frame_num, cassette_handler, "Jetman"), + joebla = create_boot_default_sequence(frame_num, cassette_handler, "Joebla"), + john = create_boot_default_sequence(frame_num, cassette_handler, "John"), + johnny = create_boot_default_sequence(frame_num, cassette_handler, "Johnny"), + jokey = create_boot_default_sequence(frame_num, cassette_handler, "Jokey"), + jones = create_boot_default_sequence(frame_num, cassette_handler, "Jones"), + jones1 = create_boot_default_sequence(frame_num, cassette_handler, "Jones1"), + jones2 = create_boot_default_sequence(frame_num, cassette_handler, "Jones2"), + jones3 = create_boot_default_sequence(frame_num, cassette_handler, "Jones3"), + jungle = create_boot_default_sequence(frame_num, cassette_handler, "Jungle"), + karado = create_boot_default_sequence(frame_num, cassette_handler, "Karado"), + karate = create_boot_default_sequence(frame_num, cassette_handler, "Karate"), + klad = create_boot_default_sequence(frame_num, cassette_handler, "Klad"), + kurier = create_boot_default_sequence(frame_num, cassette_handler, "Kurier"), + ladderovl = create_boot_default_sequence(frame_num, cassette_handler, "ladder.ovl"), + ladder = create_boot_default_sequence(frame_num, cassette_handler, "ladder"), + lrunnergms = create_boot_default_sequence(frame_num, cassette_handler, "LRUNNER.GMS"), + lrunner = create_boot_default_sequence(frame_num, cassette_handler, "LRUNNER"), + othello = create_boot_default_sequence(frame_num, cassette_handler, "OTHELLO"), + otl9 = create_boot_default_sequence(frame_num, cassette_handler, "otl9"), + p10k = create_boot_default_sequence(frame_num, cassette_handler, "p10k"), + pacman = create_boot_default_sequence(frame_num, cassette_handler, "PACMAN"), + perevoro = create_boot_default_sequence(frame_num, cassette_handler, "PEREVOROT"), + popo = create_boot_default_sequence(frame_num, cassette_handler, "POPO"), + raceovl = create_boot_default_sequence(frame_num, cassette_handler, "RACE.OVL"), + race = create_boot_default_sequence(frame_num, cassette_handler, "RACE"), + rever1 = create_boot_default_sequence(frame_num, cassette_handler, "Rever1"), + rever2 = create_boot_default_sequence(frame_num, cassette_handler, "Rever2"), + rotate = create_boot_default_sequence(frame_num, cassette_handler, "ROTATE"), + rtype = create_boot_default_sequence(frame_num, cassette_handler, "RTYPE"), + rtype1 = create_boot_default_sequence(frame_num, cassette_handler, "RTYPE1.LAB"), + rtype2 = create_boot_default_sequence(frame_num, cassette_handler, "RTYPE2.LAB"), + sokoban = create_boot_default_sequence(frame_num, cassette_handler, "SOKOBAN"), + stalker = create_boot_default_sequence(frame_num, cassette_handler, "Stalker"), + tetr3 = create_boot_default_sequence(frame_num, cassette_handler, "TETR3"), + tetris = create_boot_default_sequence(frame_num, cassette_handler, "TETRIS"), + tetro = create_boot_default_sequence(frame_num, cassette_handler, "Tetro"), + xonix1 = create_boot_default_sequence(frame_num, cassette_handler, "XONIX1"), + xonix5 = create_boot_default_sequence(frame_num, cassette_handler, "XONIX5"), + zoom = create_boot_default_sequence(frame_num, cassette_handler, "ZOOM"), +} + +-- MAME machine frame notification callback +local function process_frame() + frame_num = frame_num + 1 + + -- Initial setup/info display at frame 1 + if frame_num == 1 then + emu.print_info("System Driver: " .. emu.romname()) + emu.print_info("Loaded Software Name (from image filename): " .. current_software_name) + + -- Determine which profile will be used + if boot_sequences[current_software_name] then + emu.print_info("--- Booting using game specific profile: " .. current_software_name .. " ---") + else + emu.print_info("--- Booting using default profile ---") + end + end + + --- DEBUG: Print current frame number every 100 frames --- + common_autoboot.debug_frame_num(frame_num) + + -- Execute the relevant boot sequence based on the loaded software + local boot_function = boot_sequences[current_software_name] + + if not boot_function then -- If specific function not found + boot_function = boot_sequences["default"] -- Assign the default function + end + + if boot_function then + boot_function(frame_num, cassette_handler) + end +end + +-- Subscribe to machine frame notifications +subscription = emu.add_machine_frame_notifier(process_frame) \ No newline at end of file diff --git a/package/batocera/core/batocera-configgen/data/mame/autoboot_scripts/c64_cass.lua b/package/batocera/core/batocera-configgen/data/mame/autoboot_scripts/c64_cass.lua new file mode 100644 index 00000000000..edb070f3c11 --- /dev/null +++ b/package/batocera/core/batocera-configgen/data/mame/autoboot_scripts/c64_cass.lua @@ -0,0 +1,52 @@ +-- to get cassette device name: +-- [MAME]> for k,v in pairs(manager.machine.cassettes) do +-- [MAME]>> print(k) +-- [MAME]>> end + + +local frame_num = 0 +local load_done_frame_num = 0 +local motor_state_false_frame_num = 0 +local function process_frame() + frame_num = frame_num + 1 + + if frame_num == 1 then + emu.keypost('LOAD\n') + manager.machine.cassettes[":tape:c1530:cassette"]:play() + end + + -- Workaround, sometimes the first "load" is misspelled + if frame_num == 150 then + emu.keypost('LOAD\n') + manager.machine.cassettes[":tape:c1530:cassette"]:play() + end + + if frame_num > 300 then + if motor_state_false_frame_num == 0 then + if manager.machine.cassettes[":tape:c1530:cassette"].motor_state == false then + motor_state_false_frame_num = frame_num + end + else + if frame_num > motor_state_false_frame_num + 60 then + if load_done_frame_num == 0 then + load_done_frame_num = frame_num + emu.keypost('RUN\n') + end + end + end + end + + if manager.machine.cassettes[":tape:c1530:cassette"].motor_state == true then + motor_state_false_frame_num = 0 + end + + if manager.machine.cassettes[":tape:c1530:cassette"].is_stopped == false and manager.machine.cassettes[":tape:c1530:cassette"].motor_state == true then + manager.machine.video.throttled = false + manager.machine.video.frameskip = 12 + else + manager.machine.video.throttled = true + manager.machine.video.frameskip = 0 + end +end + +subscription=emu.add_machine_frame_notifier(process_frame) diff --git a/package/batocera/core/batocera-configgen/data/mame/autoboot_scripts/c64_flop.lua b/package/batocera/core/batocera-configgen/data/mame/autoboot_scripts/c64_flop.lua new file mode 100644 index 00000000000..09106307586 --- /dev/null +++ b/package/batocera/core/batocera-configgen/data/mame/autoboot_scripts/c64_flop.lua @@ -0,0 +1 @@ +emu.keypost('LOAD "*" ,8,1\n') diff --git a/package/batocera/core/batocera-configgen/data/mame/autoboot_scripts/camplynx_cass.lua b/package/batocera/core/batocera-configgen/data/mame/autoboot_scripts/camplynx_cass.lua new file mode 100644 index 00000000000..2aff44f2e94 --- /dev/null +++ b/package/batocera/core/batocera-configgen/data/mame/autoboot_scripts/camplynx_cass.lua @@ -0,0 +1,142 @@ +-- camplynx_cass.lua +-- +-- Camputers Lynx cassette autoboot with header-based type detection. +-- +-- CampLynx .tap byte format: +-- 0x22 opening quote +-- ASCII string (variable length) +-- 0x22 closing quote +-- 0x42 ('B') BASIC program → LOAD "name"\n + RUN\n after motor off +-- 0x4D ('M') machine code → MLOAD "name"\n (auto-runs after load) +-- + +local common_autoboot = dofile("/usr/share/batocera/configgen/data/mame/autoboot_scripts/autoboot_common.lua") +local zip_util = dofile("/usr/share/batocera/configgen/data/mame/autoboot_scripts/autoboot_zip.lua") + +local TAP_SCAN_BYTES = 256 + +local TAPE_TYPE_BASIC = 0 +local TAPE_TYPE_MACHINE = 1 + +local BUTTON_PRESS_DURATION = common_autoboot.DEFAULT_BUTTON_PRESS_DURATION +local CASSETTE_MOTOR_OFF_DELAY_FRAMES = common_autoboot.DEFAULT_CASSETTE_MOTOR_OFF_DELAY_FRAMES + +local CASSETTE_SLOT = 1 +local CASSETTE_DEVICE_TAG = manager.machine.images:at(CASSETTE_SLOT).device.tag + +local button = {} +local frame_num = 0 +common_autoboot.populate_buttons(button) + +local function find_cassette_path() + for _, cass in pairs(manager.machine.cassettes) do + if cass.exists and cass.filename and #cass.filename > 0 then + print("[CampLynx Autoboot] Cassette device: " .. cass.filename) + return cass.filename + end + end + for _, img in pairs(manager.machine.images) do + if img.exists and img.filename and #img.filename > 0 then + local fn = img.filename:lower() + if fn:find("%.tap$") or fn:find("%.cas$") or fn:find("%.wav$") then + print("[CampLynx Autoboot] Image device cassette: " .. img.filename) + return img.filename + end + end + end + return nil +end + +-- ── CampLynx header parser ──────────────────────────────────────────────────── +-- +-- Format: "filename"... +-- 0x22 ... 0x22 quoted filename +-- 0x42 ('B') BASIC +-- 0x4D ('M') machine code + +local function parse_tap_header(data) + if not data or #data < 4 then return nil end + if data:byte(1) ~= 0x22 then return nil end + + local end_pos = nil + for i = 2, #data do + if data:byte(i) == 0x22 then + end_pos = i + break + end + end + if not end_pos or end_pos + 1 > #data then return nil end + + local filename = data:sub(2, end_pos - 1) + local type_byte = data:byte(end_pos + 1) + + if type_byte == 0x4D then + print(string.format("[CampLynx Autoboot] Header: MACHINE name='%s'", filename)) + return { file_type = TAPE_TYPE_MACHINE, filename = filename } + else + print(string.format("[CampLynx Autoboot] Header: BASIC name='%s'", filename)) + return { file_type = TAPE_TYPE_BASIC, filename = filename } + end +end + +local function detect_tape_info() + print("[CampLynx Autoboot] --- Tape type detection ---") + local path = find_cassette_path() + if not path then + print("[CampLynx Autoboot] No cassette found, defaulting to BASIC.") + return { file_type = TAPE_TYPE_BASIC, filename = "" } + end + local data = zip_util.read_bytes(path, TAP_SCAN_BYTES) + if not data then + print("[CampLynx Autoboot] Could not read cassette, defaulting to BASIC.") + return { file_type = TAPE_TYPE_BASIC, filename = "" } + end + local info = parse_tap_header(data) + if info then return info end + print("[CampLynx Autoboot] No valid header found, defaulting to BASIC.") + return { file_type = TAPE_TYPE_BASIC, filename = "" } +end + +-- ── Boot functions ──────────────────────────────────────────────────────────── + +local cassette_handler = common_autoboot.create_cassette_handler(CASSETTE_DEVICE_TAG) +local tape_info = nil + +local function boot_basic() + common_autoboot.type_at_frame(frame_num, 'LOAD "' .. tape_info.filename .. '"\n', 100, BUTTON_PRESS_DURATION) + common_autoboot.play_cassette_at_frame(frame_num, CASSETTE_DEVICE_TAG, 200) + + if cassette_handler(frame_num, CASSETTE_MOTOR_OFF_DELAY_FRAMES) then + emu.keypost("RUN\n") + end +end + +local function boot_machine() + -- MLOAD auto-runs after loading; no post-load command needed. + common_autoboot.type_at_frame(frame_num, 'MLOAD "' .. tape_info.filename .. '"\n', 100, BUTTON_PRESS_DURATION) + common_autoboot.play_cassette_at_frame(frame_num, CASSETTE_DEVICE_TAG, 200) + + cassette_handler(frame_num, CASSETTE_MOTOR_OFF_DELAY_FRAMES) +end + +-- ── Main ────────────────────────────────────────────────────────────────────── + +local function process_frame() + frame_num = frame_num + 1 + + if frame_num == 1 then + emu.print_info("System Driver: " .. emu.romname()) + common_autoboot.print_image_info() + tape_info = detect_tape_info() + end + + common_autoboot.debug_frame_num(frame_num) + + if tape_info and tape_info.file_type == TAPE_TYPE_MACHINE then + boot_machine() + else + boot_basic() + end +end + +subscription = emu.add_machine_frame_notifier(process_frame) diff --git a/package/batocera/core/batocera-configgen/data/mame/autoboot_scripts/cdi.lua b/package/batocera/core/batocera-configgen/data/mame/autoboot_scripts/cdi.lua new file mode 100644 index 00000000000..9d22c1a5450 --- /dev/null +++ b/package/batocera/core/batocera-configgen/data/mame/autoboot_scripts/cdi.lua @@ -0,0 +1,38 @@ +local button = {} + +for i, j in pairs(manager.machine.ioport.ports) do + for field_name, field in pairs(j.fields) do + id = field.port.tag .. ',' .. field.mask .. ',' .. field.type + print(field_name,": ", id) + button[id] = field + end +end + +local boot_end_frame = 700 +local frame_num = 0 +local previous_throttled = manager.machine.video.throttled +local previous_frameskip = manager.machine.video.frameskip + +local function process_frame() + frame_num = frame_num + 1 + + if frame_num == 1 then + manager.machine.video.throttled = false + manager.machine.video.frameskip = 12 + end + + if frame_num == boot_end_frame then + manager.machine.video.throttled = previous_throttled + manager.machine.video.frameskip = previous_frameskip + + button[":MOUSEBTN,1,64"]:set_value(1) + end + + if frame_num == boot_end_frame + 20 then + button[":MOUSEBTN,1,64"]:set_value(0) + end +end + +subscription=emu.add_machine_frame_notifier(process_frame) + + diff --git a/package/batocera/core/batocera-configgen/data/mame/autoboot_scripts/cgenie_cass.lua b/package/batocera/core/batocera-configgen/data/mame/autoboot_scripts/cgenie_cass.lua new file mode 100644 index 00000000000..37bb37aae33 --- /dev/null +++ b/package/batocera/core/batocera-configgen/data/mame/autoboot_scripts/cgenie_cass.lua @@ -0,0 +1,163 @@ +-- cgenie_cass.lua +-- +-- Colour Genie cassette autoboot with header-based type detection. +-- +-- CGenie .cas byte format (after optional 36-byte ASCII text header): +-- 0x66 sync byte +-- 0x55 header/name block marker → machine-code program +-- [0..5] filename (6 bytes ASCII, 0x20-padded) +-- 0x3C data block follows +-- ... +-- +-- 0x66 sync byte +-- BASIC tokenised data follows → BASIC program +-- +-- Boot sequences: +-- BASIC → \n · CLOAD\n · play · RUN\n after motor off +-- machine code→ \n · SYSTEM\n · \n · play · /\n after motor off + +local common_autoboot = dofile("/usr/share/batocera/configgen/data/mame/autoboot_scripts/autoboot_common.lua") +local zip_util = dofile("/usr/share/batocera/configgen/data/mame/autoboot_scripts/autoboot_zip.lua") + +local CAS_SCAN_BYTES = 4096 + +local TAPE_TYPE_BASIC = 0 +local TAPE_TYPE_DATA = 1 +local TAPE_TYPE_MACHINE = 2 + +local BUTTON_PRESS_DURATION = common_autoboot.DEFAULT_BUTTON_PRESS_DURATION +local CASSETTE_MOTOR_OFF_DELAY_FRAMES = common_autoboot.DEFAULT_CASSETTE_MOTOR_OFF_DELAY_FRAMES + +local button = {} +local frame_num = 0 +common_autoboot.populate_buttons(button) + +local function find_cassette_path() + for _, cass in pairs(manager.machine.cassettes) do + if cass.exists and cass.filename and #cass.filename > 0 then + print("[CGenie Autoboot] Cassette device: " .. cass.filename) + return cass.filename + end + end + for _, img in pairs(manager.machine.images) do + if img.exists and img.filename and #img.filename > 0 then + local fn = img.filename:lower() + if fn:find("%.cas$") or fn:find("%.wav$") then + print("[CGenie Autoboot] Image device cassette: " .. img.filename) + return img.filename + end + end + end + return nil +end + +-- ── CGenie header parser ────────────────────────────────────────────────────── +-- +-- Scan for the 0x66 sync byte. The byte immediately following it determines +-- the file type: +-- 0x55 → machine-code (SYSTEM) program; next 6 bytes are the filename. +-- other → BASIC (CLOAD) program; no filename needed. + +local function parse_cgenie_header(data) + if not data or #data < 4 then return nil end + + for i = 1, #data - 2 do + if data:byte(i) == 0x66 then + local next_byte = data:byte(i + 1) + + if next_byte == 0x55 then + -- Machine-code header: read 6-byte filename + if i + 7 > #data then return nil end + local raw = {} + for j = 0, 5 do + raw[j + 1] = data:byte(i + 2 + j) + end + -- Strip trailing spaces and NULs to get the name SYSTEM expects + local name_end = 6 + while name_end >= 1 and (raw[name_end] == 0x20 or raw[name_end] == 0x00) do + name_end = name_end - 1 + end + local filename = "" + for j = 1, name_end do + filename = filename .. string.char(raw[j]) + end + print(string.format("[CGenie Autoboot] Header: MACHINE name='%s'", filename)) + return { file_type = TAPE_TYPE_MACHINE, filename = filename } + else + -- No name block → BASIC program + print("[CGenie Autoboot] Header: BASIC") + return { file_type = TAPE_TYPE_BASIC, filename = "" } + end + end + end + + return nil +end + +local function detect_tape_info() + print("[CGenie Autoboot] --- Tape type detection ---") + local path = find_cassette_path() + if not path then + print("[CGenie Autoboot] No cassette found, defaulting to BASIC.") + return { file_type = TAPE_TYPE_BASIC, filename = "" } + end + local data = zip_util.read_bytes(path, CAS_SCAN_BYTES) + if not data then + print("[CGenie Autoboot] Could not read cassette, defaulting to BASIC.") + return { file_type = TAPE_TYPE_BASIC, filename = "" } + end + local info = parse_cgenie_header(data) + if info then return info end + print("[CGenie Autoboot] No valid header found, defaulting to BASIC.") + return { file_type = TAPE_TYPE_BASIC, filename = "" } +end + +-- ── Boot functions ──────────────────────────────────────────────────────────── + +local cassette_handler = common_autoboot.create_cassette_handler(":cassette") +local tape_info = nil + +local function boot_basic() + common_autoboot.type_at_frame(frame_num, "\n", 50, BUTTON_PRESS_DURATION) + common_autoboot.type_at_frame(frame_num, "CLOAD\n", 200, BUTTON_PRESS_DURATION) + common_autoboot.play_cassette_at_frame(frame_num, ":cassette", 300) + + if cassette_handler(frame_num, CASSETTE_MOTOR_OFF_DELAY_FRAMES) then + emu.keypost("RUN\n") + end +end + +local function boot_machine() + -- SYSTEM prompts "*? " for the tape name, then loads from tape, + -- then prompts "*/ " for the start address; "/" runs from default. + common_autoboot.type_at_frame(frame_num, "\n", 50, BUTTON_PRESS_DURATION) + common_autoboot.type_at_frame(frame_num, "SYSTEM\n", 150, BUTTON_PRESS_DURATION) + common_autoboot.type_at_frame(frame_num, tape_info.filename .. "\n", 200, BUTTON_PRESS_DURATION) + common_autoboot.play_cassette_at_frame(frame_num, ":cassette", 300) + + if cassette_handler(frame_num, CASSETTE_MOTOR_OFF_DELAY_FRAMES) then + emu.keypost("/\n") + end +end + +-- ── Main ────────────────────────────────────────────────────────────────────── + +local function process_frame() + frame_num = frame_num + 1 + + if frame_num == 1 then + emu.print_info("System Driver: " .. emu.romname()) + common_autoboot.print_image_info() + tape_info = detect_tape_info() + end + + common_autoboot.debug_frame_num(frame_num) + + if tape_info and tape_info.file_type == TAPE_TYPE_MACHINE then + boot_machine() + else + boot_basic() + end +end + +subscription = emu.add_machine_frame_notifier(process_frame) diff --git a/package/batocera/core/batocera-configgen/data/mame/autoboot_scripts/coco_cass.lua b/package/batocera/core/batocera-configgen/data/mame/autoboot_scripts/coco_cass.lua new file mode 100644 index 00000000000..bdb63a815c6 --- /dev/null +++ b/package/batocera/core/batocera-configgen/data/mame/autoboot_scripts/coco_cass.lua @@ -0,0 +1,81 @@ +-- vtech2_cass.lua + +-- Load common autoboot functions +local common_autoboot = dofile("/usr/share/batocera/configgen/data/mame/autoboot_scripts/autoboot_common.lua") + +-- Script-wide variables +local button = {} +local frame_num = 0 + +common_autoboot.populate_buttons(button) + +-- Print the button table contents for debugging +-- common_autoboot.print_buttons(button) + +-- --- Constants for this specific script --- +local BUTTON_PRESS_DURATION = common_autoboot.DEFAULT_BUTTON_PRESS_DURATION +local CASSETTE_MOTOR_OFF_DELAY_FRAMES = common_autoboot.DEFAULT_CASSETTE_MOTOR_OFF_DELAY_FRAMES + +local function boot_default(cassette_handler) + -- common_autoboot.type_at_frame(frame_num, "CLOAD:RUN\n", 100, BUTTON_PRESS_DURATION) + + common_autoboot.type_at_frame(frame_num, "CLOADM:EXEC\n", 100, BUTTON_PRESS_DURATION) + + common_autoboot.play_cassette_at_frame(frame_num, ":cassette", 400) -- need to wait at least 300 frames after CLOAD cmd, otherwise will have LOADING ERROR! + + local cassette_load_done = cassette_handler(frame_num, CASSETTE_MOTOR_OFF_DELAY_FRAMES) + + if cassette_load_done then + emu.keypost('RUN\n') + end +end + +-- --- Main Script Logic --- + +-- Determine the currently loaded software name +local current_software_name = manager.machine.images:at(1).filename + +-- Map software names to their respective boot functions +local boot_sequences = { + ["default"] = boot_default, +} + +-- --- Cassette Loading Handler Setup --- +local cassette_handler = common_autoboot.create_cassette_handler(":cassette") + +-- MAME machine frame notification callback +local function process_frame() + frame_num = frame_num + 1 + + -- Initial setup/info display at frame 1 + if frame_num == 1 then + emu.print_info("System Driver: " .. emu.romname()) + emu.print_info("Loaded Software Name (from image filename): " .. current_software_name) + + -- Determine which profile will be used + if boot_sequences[current_software_name] then + emu.print_info("--- Booting using game specific profile: " .. current_software_name .. " ---") + else + emu.print_info("--- Booting using default profile ---") + end + end + + -- --- Print current frame number every 100 frames --- + if frame_num % 100 == 0 then -- The modulo operator (%) gives the remainder of a division + emu.print_info("Current Frame: " .. frame_num) + end + + -- Execute the relevant boot sequence based on the loaded software + local boot_function = boot_sequences[current_software_name] + + if not boot_function then -- If specific function not found + boot_function = boot_sequences["default"] -- Assign the default function + end + + if boot_function then + boot_function(cassette_handler) + end +end + +-- Subscribe to machine frame notifications +subscription = emu.add_machine_frame_notifier(process_frame) diff --git a/package/batocera/core/batocera-configgen/data/mame/autoboot_scripts/coco_flop.lua b/package/batocera/core/batocera-configgen/data/mame/autoboot_scripts/coco_flop.lua new file mode 100644 index 00000000000..ee4f869a375 --- /dev/null +++ b/package/batocera/core/batocera-configgen/data/mame/autoboot_scripts/coco_flop.lua @@ -0,0 +1,130 @@ +cpu = manager.machine.devices[":maincpu"] +mem = cpu.spaces["program"] +base_addr = 0x0400 +base_file_name_size = 9 +ext_file_name_size = 3 +next_file = 32 +file_index = 0 +dir_frame = 0 +try_run = 0 +type_buffer = {} +type_qty = 0 +memory_clean_frame = 0 +character_mask = 0x3F + +local function filter_char(char) + ret_char = char + if char >= 0x60 then + ret_char = char & character_mask + end + + return ret_char +end + +local function stack_char(char) + table.insert(type_buffer, 1, char) + type_qty = type_qty + 1 +end + +local function dir(frame) + stack_char("d") + stack_char("i") + stack_char("r") + stack_char(" ") + stack_char("0") + stack_char("\n") + dir_frame = frame +end + +local function is_extension_allowed() + addr = base_addr + file_index * next_file + base_file_name_size + + ext = {} + for c = 0, (ext_file_name_size-1) do + ext[c+1] = mem:read_u8(addr + c) + end + + allowed_ext = { string.byte('B'), string.byte('A'), string.byte('S')} + + allowed = 0 + + for c = 1, 3 do + if allowed_ext[c] == ext[c] then + allowed = allowed + 1 + end + end + + return (allowed == 3) +end + +local function run_file() + print("file index", file_index) + if is_extension_allowed() == false then + file_index = file_index + 1 + return + end + + stack_char("r") + stack_char("u") + stack_char("n") + stack_char("\"") + for c = 0, base_file_name_size-1 do + addr = base_addr + file_index * next_file + c + char = filter_char(mem:read_u8(addr)) + if char ~= 0x20 then + stack_char(string.char(char)) + end + end + + stack_char("\"\n") + try_run = 1 +end + +local function wait_for_clean_memory(frame) + if memory_clean_frame ~= 0 then + if frame > memory_clean_frame + 25 then + try_run = 0 + dir_frame = 0 + memory_clean_frame = 0 + file_index = file_index + 1 + end + else + start_file = mem:read_u8(base_addr + file_index * next_file) + if start_file == 0 then + memory_clean_frame = frame + end + end +end + +local function unstack_char() + if type_qty > 0 then + char = table.remove(type_buffer) + type_qty = type_qty - 1 + emu.keypost(char) + end +end + +frame_num = 0 +local function process_frame() + frame_num = frame_num + 1 + + if dir_frame == 0 then + if frame_num > 90 then + dir(frame_num) + end + else + if try_run == 0 then + if frame_num > dir_frame + 350 then + run_file() + end +-- else +-- wait_for_clean_memory(frame_num) + end + end + + if frame_num % 5 == 0 then + unstack_char() + end +end + +subscription=emu.add_machine_frame_notifier(process_frame) diff --git a/package/batocera/core/batocera-configgen/data/mame/autoboot_scripts/coco_sha1_map.json b/package/batocera/core/batocera-configgen/data/mame/autoboot_scripts/coco_sha1_map.json new file mode 100644 index 00000000000..ff61421180b --- /dev/null +++ b/package/batocera/core/batocera-configgen/data/mame/autoboot_scripts/coco_sha1_map.json @@ -0,0 +1,10570 @@ +{ + "72a3531037168ae46d805ef62a02c71c4fd70570": { + "filename": "3D Tic Tac Toe (Oelrich Publications).zip", + "disk0": "3DTTT.DSK", + "basic": "LOADM\"3DTTT\":EXEC\n" + }, + "53f7ad1015ee0f3eca0bbfb94a1e7d93d6a0abb9": { + "filename": "3D Brickaway (Avalon Hill).zip", + "disk0": "3DBRICK.DSK", + "joy-right": "mjoy0", + "basic": "LOADM\"3DBRICK\":EXEC\n" + }, + "0667c50f611bac17e30026a4a10c2fa461970f67": { + "filename": "3D Ghostmania (Randy S. Johnson).zip", + "disk0": "3DGHOST.DSK", + "basic": "LOADM\"3D GHOST\":EXEC\n" + }, + "c19bf3d6ee2527c06ff5b5f4566f928dc2d54ed3": { + "filename": "10 Meter Diving (Tammy Jones).zip", + "disk0": "PFRMDIVE.DSK", + "basic": "RUN\"PFRMDIVE\"\n" + }, + "e04701223cf599e898059e074c0010953b0d6df3": { + "filename": "3D Space Wars (Newson Consultants).zip", + "disk0": "3DWAR.DSK", + "joy-right": "kjoy0", + "basic": "LOADM\"3DWAR\":EXEC\n" + }, + "f911ec9658dfc52a325245ec2e295a22fb4f5610": { + "filename": "5000 (Gary James) (Coco 3).zip", + "machine": "coco3", + "disk0": "5000.DSK", + "basic": "RUN\"5000\"\n" + }, + "92be86e96cb4cf75a0151cb611e0c465f82997f2": { + "filename": "5-Line Fruit Slot Machine (Tom Mix Software) (Coco 3).zip", + "machine": "coco3", + "disk0": "5LINE.DSK", + "basic": "LOADM\"5LINE\":EXEC\n" + }, + "05d63de44dce98928091044496fcc25628fb58ae": { + "filename": "4 Mile Island Adventure (Owls Nest Software).zip", + "disk0": "4MILE.DSK", + "basic": "RUN\"4MILE\"\n" + }, + "4a5e1d4d0d8062d413a4729da2060e3309ae8f67": { + "filename": "2048 (George Janssen) (Coco 3).zip", + "machine": "coco3", + "disk0": "2048.DSK", + "basic": "LOADM\"2048\":EXEC\n" + }, + "d391b85bdea188ea096289fb6339095e5bcb6c49": { + "filename": "666 The Haunted House (C. Hofsetz & G. Castro).zip", + "disk0": "666thh.dsk", + "basic": "RUN\"666\"\n" + }, + "9f37e05606037db6b9bbb6080d815d848c94b3a5": { + "filename": "5 Player Poker (T&D Software).zip", + "disk0": "POKER5.DSK", + "basic": "RUN\"POKER5\"\n" + }, + "f92a4c2868a8617bf011fc4d683dc3d9e153a59c": { + "filename": "Able Builders (T&D Software).zip", + "disk0": "ABLE.DSK", + "joy-right": "mjoy0", + "basic": "LOADM\"ABLE\":EXEC\n" + }, + "0e53b3ad87b29bf9125d48282cf762e2feb18de2": { + "filename": "7 Card Stud (Tandy).zip", + "disk0": "7CARD.DSK", + "basic": "LOADM\"7CARD\":EXEC\n" + }, + "9375ed622eb483b42c95c474e3062e90cb01fe16": { + "filename": "Across the Rubicon (Ark Royal Games).zip", + "disk0": "rubicon.dsk", + "basic": "RUN\"RUBICON\"\n" + }, + "16ee3a269b22636159633e93c758b10ce125acc2": { + "filename": "8-Ball (Anteco Software).zip", + "disk0": "8BALL.DSK", + "joy-left": "mjoy0", + "basic": "LOADM\"8BALL\":EXEC\n" + }, + "a456fdc2e0129a7f7a8642c9204dd6c219970f5d": { + "filename": "Aandark II (Fred D. Provoncha).zip", + "disk0": "AANDARK2.DSK", + "basic": "RUN\"INTRO\"\n" + }, + "b0ff710a6db6b36630ad0770252814c530714636": { + "filename": "A BASIC Adventure (Kevin Davidson).zip", + "disk0": "BASICADV.DSK", + "basic": "RUN\"BASICADV\"\n" + }, + "f78a204938fef27604198c70c977480f0fc62c35": { + "filename": "A day at the Races (George Bodiroga).zip", + "disk0": "RACES.DSK", + "basic": "RUN\"RACES\"\n" + }, + "bd7bb528b478944e7276ced11546882cef503597": { + "filename": "Advanced Star Trench Warfare (Fred B. Scerbo).zip", + "disk0": "TRENCH.DSK", + "joy-right": "mjoy0", + "basic": "RUN\"TRENCH\"\n" + }, + "7a95fc695ffeb1011a343df2ac4336072c61e33c": { + "filename": "Adventure in Mythology (Saguaro Software) (SSC).zip", + "disk0": "MYTHOLGY.DSK", + "basic": "LOADM\"MYTHOLGY\":EXEC\n" + }, + "11cce148d19dcba2997520fab189c0834ae07037": { + "filename": "Adventure in Wonder Land (Prickly Pear Software).zip", + "disk0": "ALICE.DSK", + "basic": "LOADM\"ALICE\":EXEC\n" + }, + "5acf6f014a81abca627fd08966d74de8501fe135": { + "filename": "Adventure One - Land That Time Forgot (Mike Lustig).zip", + "disk0": "landadv.dsk", + "basic": "RUN\"LAND-ADV\"\n" + }, + "8b80cc96a74a3301f69602ee8a5b7ad0534bca70": { + "filename": "Adventures of Johnny Zero, The (Mark and Mike Anderson).zip", + "disk0": "JOHNNY0.DSK", + "basic": "RUN\"JOHNNY 0\"\n" + }, + "61bb3bf2d9caa01436287b5042a28963ee3399f9": { + "filename": "Adventure (Stuart Rayner).zip", + "disk0": "ADVENT.DSK", + "basic": "RUN\"ADV 32K\"\n" + }, + "737e09c08a0055dca9d1122d30fb0d0490288e9e": { + "filename": "Aerial Attack (Challenge Software) (SG-24 intro).zip", + "disk0": "AERIAL.DSK", + "joy-right": "kjoy0", + "basic": "LOADM\"AERIAL\":EXEC\n" + }, + "ae616d3bf8b3f6a76315743be5f5249498f7cd4f": { + "filename": "Adventure Trilogy (Softlaw Corp.).zip", + "disk0": "Trlgyadv.dsk", + "joy-right": "kjoy0", + "basic": "LOADM\"TRLGYADV\":EXEC\n" + }, + "9368ee1fa61d276de323be67884e43e5736faeae": { + "filename": "Air Attack (T&D Software).zip", + "disk0": "AIR.DSK", + "basic": "LOADM\"AIR\":EXEC\n" + }, + "c0a1d297c9179dc1c1aa92dd0410513a2144d303": { + "filename": "Airline (Adventure International).zip", + "disk0": "AIRLINE.DSK", + "basic": "LOADM\"AIRLINE\":EXEC\n" + }, + "2cb75936d1167ec937fb08add8cb249d8a80f10a": { + "filename": "Aftermath of the Asimovian Disaster, The (Tim Hartnell) (Coco 3).zip", + "machine": "coco3", + "disk0": "AFTERMAT.DSK", + "basic": "RUN\"AFTERMAT\"\n" + }, + "37167f0be6310906cbabf016205783a93bbef922": { + "filename": "Airball (Microdeal).zip", + "disk0": "AIRBALL.DSK", + "joy-right": "kjoy0", + "basic": "LOADM\"AIRBALL\":EXEC\n" + }, + "162031777ecccc9a3cb34891b12adcba361b8d4f": { + "filename": "Alchemist's Laboratory, The (Real Software Company).zip", + "disk0": "ALCHEM.DSK", + "basic": "RUN\"ALCHEM\"\n" + }, + "cca762ff97399849054f2f5f6ff9697b6a344903": { + "filename": "Air Traffic (Betasoft Systems).zip", + "disk0": "AIRTRAF.DSK", + "basic": "LOADM\"AIRTRAF\":EXEC\n" + }, + "ede875a6f2ad368a63182c34d072b3104db61d21": { + "filename": "Alcatraz II (Spectral Associates).zip", + "disk0": "ALCATRAZ.DSK", + "joy-right": "kjoy0", + "basic": "RUN\"ALCATRAZ\"\n" + }, + "8abe25e595c94d60388b5db1c383d876cb5d96e6": { + "filename": "Amazing (Rad Hewko).zip", + "disk0": "AMAZING.DSK", + "basic": "RUN\"AMAZING\"\n" + }, + "f244f0d0ffe871d63d926408c580f3ff342a5aaa": { + "filename": "Amphibia (The Rainbow).zip", + "disk0": "AMPHIBIA.DSK", + "joy-right": "kjoy0", + "basic": "RUN\"BOOT\"\n" + }, + "9fd86667d26cb377f8dcf8a20d3b8d0ef190395d": { + "filename": "Aldaron (Jade Products).zip", + "disk0": "ALDARON.DSK", + "basic": "LOADM\"ALDARON\":EXEC\n" + }, + "a106c65e63d4d2a5b725081ec39f10a49d4b56c6": { + "filename": "A Mazing World of Malcom Mortar (Tandy) (Coco 3).zip", + "machine": "coco3", + "disk0": "MALCOM.DSK", + "joy-right": "kjoy0", + "basic": "LOADM\"MALCOM\":EXEC\n" + }, + "ceef8436a404dacd1c5ab49038decdf037704fe0": { + "filename": "Andrea Coco, The (Saguard Software).zip", + "disk0": "ANDREA.DSK", + "basic": "RUN\"ANDREA\"\n" + }, + "977f3ed2274d585510c7357c4f042a89e5be96e6": { + "filename": "Android Attack (Spectral Associates).zip", + "disk0": "ANDATCK.DSK", + "joy-right": "kjoy0", + "basic": "LOADM\"ANDATCK\":EXEC\n" + }, + "ea9b12583101890a7db2fe6aa52fd5e685f9e46a": { + "filename": "Annihilator (Chromasette).zip", + "disk0": "ANNIHILA.DSK", + "basic": "LOADM\"ANNIHILA\":EXEC\n" + }, + "046d8de78bddba12dad659c38ee6c3e60fcfbf21": { + "filename": "Androne (Tandy).zip", + "disk0": "ANDRONE.DSK", + "joy-left": "kjoy0", + "basic": "LOADM\"ANDRONE\":EXEC\n" + }, + "1e9cdf6ec5c2e7d2f8914cd606c33afc4478913a": { + "filename": "Anti-Ballistic Missile Command (CLOAD).zip", + "disk0": "ABM.DSK", + "joy-right": "mjoy0", + "basic": "RUN\"ABM\"\n" + }, + "eb651ca25f681e826ca743809d4d75c980c23ec7": { + "filename": "Arcadia (Flipper Action).zip", + "disk0": "ARCADIA.DSK", + "joy-right": "kjoy0", + "basic": "RUN\"ARCADIA\"\n" + }, + "28ba51f5c2960953c0cd00b3c010302402dc1cfa": { + "filename": "Alternate Reality - The City (Datasoft) (OS-9) (Coco 3).zip", + "machine": "coco3", + "disk0": "CITYBOOT.DSK", + "joy-right": "kjoy0", + "basic": "DOS\n" + }, + "ac4fbd2cb7ca2680ce3129f26847d0b691b03cbe": { + "filename": "Alternate Reality - The City (Datasoft) (OS-9) (Coco 3).zip", + "machine": "coco3", + "disk0": "CITYBOOT.DSK", + "joy-right": "kjoy0", + "basic": "DOS\n" + }, + "525daa84553d665d75a1bff115433926fcdecaec": { + "filename": "Alternate Reality - The City (Datasoft) (OS-9) (Coco 3).zip", + "machine": "coco3", + "disk0": "CITYBOOT.DSK", + "joy-right": "kjoy0", + "basic": "DOS\n" + }, + "e79c218a9ccdc8c0a62f76afd2b3eb2a23112edb": { + "filename": "Arcade Factory, The (B&B Software).zip", + "disk0": "ARCADFCT.DSK", + "joy-right": "kjoy0", + "basic": "LOADM\"ARCADE\":EXEC\n" + }, + "ec005a315070f4c04a26917bb0bed9e17a9000f2": { + "filename": "Apples (Computerware).zip", + "disk0": "APPLES.DSK", + "joy-right": "kjoy0", + "basic": "LOADM\"APPLES\":EXEC\n" + }, + "4e66e8135391df23027fb2db59281c1b4f2cd276": { + "filename": "Arconiax Assignment, The (The Rainbow).zip", + "disk0": "ARCON32K.DSK", + "basic": "RUN\"ARCON32K\"\n" + }, + "50198639edfc1238e27d3eee794850e317971195": { + "filename": "Arctic Adventure (Harry McCracken) (Coco port).zip", + "disk0": "ARCTIC.DSK", + "basic": "RUN\"ARCTIC\"\n" + }, + "c857448d76cea56673f451c7ece7f6866132a6d7": { + "filename": "Arena of Skill (Chuck Nivision) (Coco 3).zip", + "machine": "coco3", + "disk0": "arena.dsk", + "joy-right": "kjoy0", + "basic": "RUN\"ARENA\"\n" + }, + "2299aa882cc25d600dca31c33800d03147b97642": { + "filename": "Arex (Adventure International).zip", + "disk0": "AREX.DSK", + "joy-right": "kjoy0", + "basic": "LOADM\"AREX\":EXEC\n" + }, + "71eaf55ca268d2b556fca15401aa287d2f8c801d": { + "filename": "Arthur (Yves & Marc).zip", + "disk0": "ARTHUR.DSK", + "joy-right": "kjoy0", + "basic": "LOADM\"ARTHUR\":EXEC\n" + }, + "68976c3148d97f503fa7f0cb39e882019ae6c5dc": { + "filename": "Armchair Admiral (Eversoft Games) (Coco 3).zip", + "machine": "coco3", + "disk0": "ARMCHAIR.DSK", + "basic": "RUN\"ADMIRAL\"\n" + }, + "fd3f51429c7b61c14c0acb68cb430d0e02d48084": { + "filename": "Artillery v2.01 (Jordan Ellenberg).zip", + "disk0": "artil201.dsk", + "basic": "RUN\"ARTIL\"\n" + }, + "132f88817e247549a422a6235d447cf5b1a28968": { + "filename": "Arkanoid (Tandy) (Coco 1-2) (Coco 3).zip", + "machine": "coco3", + "disk0": "ARKANOID.DSK", + "joy-right": "mjoy0", + "basic": "RUN\"ARKANOID\"\n" + }, + "aa25f3fd2a3258beeeea9f8c4357f3af46d33e1b": { + "filename": "Asimov Awards 2006.zip", + "machine": "coco3", + "disk0": "AA2006.DSK", + "basic": "RUN\"RUN ME\"\n" + }, + "5f39e0648be8385dde7b59193ae8a69c6c26416a": { + "filename": "Asimov Awards 2008.zip", + "machine": "coco3", + "disk0": "AA2008.DSK", + "basic": "RUN\"RUN ME\"\n" + }, + "c05d6a00649345787dd18eb9281ac674c3945c6f": { + "filename": "Assasin (Michael Freeman).zip", + "disk0": "ASSASIN.DSK", + "joy-right": "kjoy0", + "basic": "LOADM\"ASSASIN\":EXEC\n" + }, + "a7d37476610908ee64350c6dd1bc83372667671e": { + "filename": "Asimov Awards 2007.zip", + "machine": "coco3", + "disk0": "AA2007.DSK", + "basic": "RUN\"RUN ME\"\n" + }, + "e85f0aab16d6c51fbc7fb186d312d11dc0966067": { + "filename": "Assignment 45 (80 Micro).zip", + "disk0": "ASS-45.DSK", + "basic": "RUN\"ASS-45\"\n" + }, + "6eb8f1c066020c80dcffd65b8330cd17cb0c6287": { + "filename": "Asteroids RX (Allan Turvey and Pere Serrat).zip", + "disk0": "ASTRX.DSK", + "basic": "LOADM\"ASTRX\":EXEC\n" + }, + "ce73a7c00a21343fb215464c98f4c5adc0f87ca9": { + "filename": "Atom (Tandy).zip", + "disk0": "ATOM.DSK", + "joy-right": "kjoy0", + "basic": "LOADM\"ATOM\":EXEC\n" + }, + "56c3b93be31975570b9aac14ac3ac9af4cbedfbd": { + "filename": "Athletyx (Microdeal Cornwall).zip", + "disk0": "ATHLETYX.DSK", + "joy-right": "kjoy0", + "basic": "LOADM\"ATHLETYX\":EXEC\n" + }, + "16106a7dc9d5c8c1897f56fcb2f90d267e44dc44": { + "filename": "ATC Simulator (Tom Mix Software).zip", + "disk0": "ATCSIM.DSK", + "basic": "RUN\"ATCSIM\"\n" + }, + "b73ff206aa3f681324a7b5b48961a6ad605b9e18": { + "filename": "Astro Blast (Chromasette).zip", + "disk0": "ASTBLAST.DSK", + "basic": "RUN\"ASTBLAST\"\n" + }, + "5922f247531d50d4a89c61279eb638c54785b838": { + "filename": "Astro Blast (Mark Data Products) (SG-8 intro).zip", + "disk0": "ASTRO.DSK", + "joy-right": "kjoy0", + "basic": "LOADM\"ASTRO\":EXEC\n" + }, + "4159ab26097f1091da0aea5d8083d02361d047c3": { + "filename": "At Risk (Rick Ulland) (Coco 3).zip", + "machine": "coco3", + "disk0": "ATRISK.DSK", + "joy-right": "kjoy0", + "basic": "RUN\"RISK\"\n" + }, + "7ae2108cda00dd01bd808cf493e37da5c284861e": { + "filename": "Avenger (The Cornsoft Group).zip", + "disk0": "AVENGER.DSK", + "basic": "LOADM\"AVENGER\":EXEC\n" + }, + "dc549e68dd221f29420d345c0afa722212b8d205": { + "filename": "Autobahn (Sigma Software) (SG-8) (Coco 1-2).zip", + "disk0": "AUTOBAHN.DSK", + "joy-right": "kjoy0", + "basic": "LOADM\"AUTOBAHN\":EXEC\n" + }, + "e75a2437de28f5e51c68bcd758c66e08292758c7": { + "filename": "Awari (HLO).zip", + "machine": "coco2bus", + "disk0": "AWARIv2.DSK", + "basic": "RUN\"AWARI2\"\n" + }, + "62a3589a9293be39d1ba51d51a3508cebd9076fd": { + "filename": "B-Zone (Jim Jewett).zip", + "disk0": "B-Zone.dsk", + "joy-right": "kjoy0", + "basic": "RUN\"B-ZONE\"\n" + }, + "7e8db90e4a7cc449e97b5debaf5d27e4c1a2a7ba": { + "filename": "Baccarat (T&D Software).zip", + "disk0": "BACCARAT.DSK", + "joy-right": "kjoy0", + "basic": "RUN\"BACCARAT\"\n" + }, + "305d6fdffc0f52b060f44c91f31395d1595b0afc": { + "filename": "Backgammon (Tandy).zip", + "disk0": "BACKGAMN.DSK", + "basic": "LOADM\"BACKGAMN\":EXEC\n" + }, + "6532531276d655ec6fafa7f2fddf107adee6d14e": { + "filename": "B-1 Nuclear Bomber (Avalon Hill) (Coco port).zip", + "disk0": "B1BOMBER.DSK", + "basic": "RUN\"B1BOMBER\"\n" + }, + "0b199481b5bf2431a269e273b9e26a05ff4870bb": { + "filename": "A Warlock's Revenge (Snailsoft Software) (Coco 3).zip", + "machine": "coco3", + "disk0": "WARLOCK.DSK", + "joy-right": "kjoy0", + "basic": "RUN\"AUTOEXEC\"\n" + }, + "2e9072011b8efd3c64c08ecf5f5ce1c5312e7589": { + "filename": "Balloon (Jamie Cho) (Coco 3).zip", + "machine": "coco3", + "disk0": "Balloon.dsk", + "joy-right": "kjoy0", + "basic": "RUN\"BALLOON\"\n" + }, + "d9e6d1d385856da322db9ca1f8f1f90072a90060": { + "filename": "Balloon Fire (T&D Software).zip", + "disk0": "BALLFIRE.DSK", + "joy-right": "kjoy0", + "basic": "LOADM\"BALLFIRE\":EXEC\n" + }, + "a494b101158e09bd9c60b396fa69f906b4e0c01c": { + "filename": "Back Track (Incentive Software) (Coco port).zip", + "disk0": "BTRACK.DSK", + "basic": "LOADM\"BTRACK\":EXEC\n" + }, + "b717022d2d22c6fe68b3cf44929844c2cccacbe1": { + "filename": "Balldozer (Kouga Software) (Coco port).zip", + "disk0": "BALLDOZE.DSK", + "basic": "LOADM\"BALLDOZE\":EXEC\n" + }, + "4c4bff6356ea0091f194240ea43fc605fdf5c7ad": { + "filename": "Balloon Attack (T&D Software).zip", + "disk0": "BALLATCK.DSK", + "joy-right": "kjoy0", + "basic": "LOADM\"BALLATCK\":EXEC\n" + }, + "2e303ee4ae2dcf739d021dccc6f60d38ab03d36e": { + "filename": "Bagitman (Aardvark).zip", + "disk0": "BAGITMAN.DSK", + "joy-right": "kjoy0", + "basic": "LOADM\"BAGITMAN\":EXEC\n" + }, + "2121d488895ea6ff4b615fc65a11425447ef2dd7": { + "filename": "Balm (Joab C. Jackson).zip", + "disk0": "BALM.DSK", + "basic": "RUN\"BALM\"\n" + }, + "d6245a964dea312da305986efb7a2ac63c043b0a": { + "filename": "Barrier to Entry 1.0 - A Quiz About CoCoTalk In-Jokes (Jason Reighard).zip", + "disk0": "QUIZ.DSK", + "basic": "RUN\"QUIZ\"\n" + }, + "d7c4d411822944a57df506ff48ad1ef1e1e26b35": { + "filename": "Baseball (Tandy).zip", + "disk0": "BASEBALL.DSK", + "joy-right": "kjoy0", + "basic": "LOADM\"BASEBALL\":EXEC\n" + }, + "51b6193c3ddcdbc7fa016fa45da61ed35f911d47": { + "filename": "Barbarossa (Ark Royal Games).zip", + "disk0": "barbarosa.dsk", + "basic": "RUN\"GO\"\n" + }, + "574fa15b628c164fe38b56b4616122c5de8fb41a": { + "filename": "Bar 5 Video Slot Machine (Tom Mix Software).zip", + "disk0": "BAR5.DSK", + "joy-right": "kjoy0", + "basic": "LOADM\"BAR5\":EXEC\n" + }, + "c885b63896d4e3abcdbadd495846701b280e9cbb": { + "filename": "Bash 1.00.00 (SRB Software) (Coco 3).zip", + "machine": "coco3", + "disk0": "BASH.DSK", + "joy-right": "mjoy0", + "basic": "LOADM\"BASH\":EXEC\n" + }, + "227708614daa06d2110d5185182f25cddb70087b": { + "filename": "Bash 3.00.00 (SRB Software) (Coco 1-2) (Coco 3).zip", + "machine": "coco3", + "disk0": "bash3.dsk", + "joy-right": "mjoy0", + "basic": "DOS\n" + }, + "5798c5c72dfa67b54bad6d2ea1484f2ff1409a0a": { + "filename": "Basic Dungeons & Dragons (Snailsoft) (Coco 3).zip", + "machine": "coco3", + "disk0": "BASICD&D.DSK", + "basic": "RUN\"AUTOEXEC\"\n" + }, + "41a18568295189209736095b343312d32408f367": { + "filename": "Basic Pong v1.11 (Richard Kelly).zip", + "disk0": "PONG111.DSK", + "basic": "RUN\"PONG-111\"\n" + }, + "39379d427c184a69f8858736d1510d934db95c01": { + "filename": "Bats and Bugs (Bill Franks).zip", + "disk0": "BATS.DSK", + "joy-right": "kjoy0", + "basic": "RUN\"BATS\"\n" + }, + "2179bc2cf5c0e81a7cc998040a06ecb3966771c0": { + "filename": "Basic Pac-Man 2.0 (CoCo-Synthesis) (Coco 3).zip", + "machine": "coco3", + "disk0": "PACMAN.DSK", + "joy-right": "kjoy0", + "basic": "RUN\"PAC-MAN\"\n" + }, + "870555581b6955f25565d2a3f024e7ec755f4d80": { + "filename": "Battle of Gettysburg (Softwride).zip", + "disk0": "GETTYSBG.DSK", + "basic": "RUN\"GETTYSBG\"\n" + }, + "d9971832bc49e158fab6384bed106a0a39f17ad9": { + "filename": "Battle for Tunis (Ark Royal Games).zip", + "disk0": "tunis.dsk", + "basic": "RUN\"TUNIS\"\n" + }, + "8fe1133ccb540dbbfb27b40403d76a03d7a72311": { + "filename": "Battle Hymn - The Battle of Gettysburg (Ark Royal Software).zip", + "disk0": "hymn.dsk", + "basic": "LOADM\"G\":EXEC\n" + }, + "db923900a1cd76a408267837aceb3ac0a4996c37": { + "filename": "Battle Of The Bulge (Ark Royal Games).zip", + "disk0": "BULGE.DSK", + "basic": "RUN\"BULGE\"\n" + }, + "d088ca564e5386d9a54ade35945d62a5d0d42295": { + "filename": "Battle of Midway, The (Ark Royal Games).zip", + "disk0": "MIDWAY.DSK", + "basic": "RUN\"MIDWAY\"\n" + }, + "473e5a86ea7e30ddc3bee7ed8cdb4cfdbc69a0da": { + "filename": "Battle Stations (Tom Mix Software).zip", + "disk0": "bstations.dsk", + "joy-right": "kjoy0", + "basic": "RUN\"BATTLE\"\n" + }, + "af30753f8dd9cc97cc14b169503a6f1ec029b44f": { + "filename": "BC Bill (Imagine Software).zip", + "disk0": "BCBILL.DSK", + "joy-right": "kjoy0", + "basic": "LOADM\"BCBILL\":EXEC\n" + }, + "0c2754a536e5477fdc1a6f2c03dbed9eb14da60e": { + "filename": "Battle Tank Demo (Kenneth Del Signore).zip", + "disk0": "BATLTANK.DSK", + "joy-left": "kjoy0", + "basic": "LOADM\"BATLTANK\":EXEC\n" + }, + "b75b5668c2fb6c24eb908d1506ed64f637d6ce3f": { + "filename": "Beam Rider (Spectral Associates).zip", + "disk0": "BEAM.DSK", + "joy-right": "kjoy0", + "basic": "LOADM\"BEAM\":EXEC\n" + }, + "d004f908e8498f8dd38a3ed84d3c8baec74df19a": { + "filename": "Beanstalker (Microvision) (Coco port).zip", + "disk0": "BEANSTLK.DSK", + "basic": "LOADM\"BEANSTLK\":EXEC\n" + }, + "754ead3391c947bdd34ef484dfd6603700a582bb": { + "filename": "Bedlam (Tandy).zip", + "disk0": "BEDLAM.DSK", + "basic": "LOADM\"BEDLAM\":EXEC\n" + }, + "1ebf725a8a4367dc86a82a3de3d60b5cbbb4e1b4": { + "filename": "Berserk (Mark Data Products) (SG-8 intro).zip", + "disk0": "BERSERK.DSK", + "joy-right": "kjoy0", + "basic": "LOADM\"BERSERK\":EXEC\n" + }, + "0053a1aebf79a8cd9e7119320c9aa90cb3819b92": { + "filename": "Beyond the Cimeeon Moon (Nelson Software Systems).zip", + "disk0": "CIMEEON.DSK", + "basic": "LOADM\"CIMEEON\":EXEC\n" + }, + "f388984b1854d3ef363e1203bbea694086cef375": { + "filename": "Binary Adventure (Retro Rick).zip", + "disk0": "BINARY.DSK", + "basic": "RUN\"BINARY\"\n" + }, + "ffe185d069ab56077104d98907548a2a303f651d": { + "filename": "Beware the KBB (Retro Rick).zip", + "disk0": "KBBV110.DSK", + "joy-right": "kjoy0", + "basic": "DOS\n" + }, + "19845fd55284179d2306e35ca1cb21edc25049e8": { + "filename": "Big Business (Guilbert Murray).zip", + "disk0": "BUSINESS.DSK", + "basic": "RUN\"BUSINESS\"\n" + }, + "a8dabfe71a2ba35a4ba829b6e5fc630893294cd1": { + "filename": "Biosphere (Tandy).zip", + "disk0": "Biosphere.dsk", + "basic": "DOS\n" + }, + "2c786f3d5b8ea83b8ece8677ddc78e292e53c80c": { + "filename": "Birds (Tom Mix Software).zip", + "disk0": "BIRDS.DSK", + "joy-right": "kjoy0", + "basic": "LOADM\"BIRDS\":EXEC\n" + }, + "1f88e17c785a25623ecddc645e54201050646639": { + "filename": "Black Grid (SPORTware) (Coco 3).zip", + "machine": "coco3", + "disk0": "BLKGRID.DSK", + "joy-right": "mjoy0", + "basic": "RUN\"BLK GRID\"\n" + }, + "0f1f59367280db03739ac117e7d4278f041b9e35": { + "filename": "Blackbeard's Island (Novasoft).zip", + "disk0": "blckbrd.dsk", + "basic": "LOADM\"BLACKB\":EXEC\n", + "tv-input": "redblue" + }, + "78ebec6b610423412751e4872488243686ef4a4f": { + "filename": "Black Hole (T&D Software).zip", + "disk0": "BLAKHOLE.DSK", + "basic": "RUN\"BLAKHOLE\"\n" + }, + "3a4305b772e6cd4a0eef794d59f00328b2f8918b": { + "filename": "Black Jack Royale (Second City Software).zip", + "disk0": "BJR.DSK", + "basic": "RUN\"BJR\"\n" + }, + "156e91b3219633bc8177e9f283517efb8a884daa": { + "filename": "BLACKJACKPRO (Skillware Corporation).zip", + "disk0": "BJPRO.DSK", + "joy-right": "mjoy0", + "basic": "RUN\"COUNTING/L1\"\n" + }, + "452a9639744d82c8e833291c48227ea4f9cfe099": { + "filename": "Blackjack (Steve Kincade).zip", + "disk0": "BLACKJAK.DSK", + "joy-right": "kjoy0", + "basic": "RUN\"BLACKJAK\"\n" + }, + "e081f7823448a17ee4c874a396b19ee17a6d1037": { + "filename": "Blackjack (Tandy).zip", + "disk0": "BLACKJAK.DSK", + "basic": "RUN\"BLACKJAK\"\n" + }, + "b4d3b22e45f432b6deeb1b39173783b926f5971c": { + "filename": "Black Sanctum, The (Mark Data Products).zip", + "disk0": "SANCTUM.DSK", + "basic": "LOADM\"SANCTUM\":EXEC\n", + "tv-input": "redblue" + }, + "e06bc3689eda75f78d59e5e97578e656ec2797b9": { + "filename": "Blackjack (Tom Mix Software).zip", + "disk0": "BLAKJAK.DSK", + "basic": "LOADM\"BLAKJAK\":EXEC\n" + }, + "b06d1716470df50030e8c474f0b14115b05d3c81": { + "filename": "Black Sanctum, The (Text) (Mark Data Products).zip", + "disk0": "SANCTUM.DSK", + "basic": "LOADM\"SANCTUM\":EXEC\n" + }, + "7a04ff673b90b5771c14751c32d57bea1e630f0d": { + "filename": "Blak Jak (Cload).zip", + "disk0": "BLAKJAK.DSK", + "basic": "RUN\"BLAKJAK\"\n" + }, + "c56c89c593ea7514d448e5ecae836b137921091a": { + "filename": "Blitz (George Phillips).zip", + "disk0": "BLITZ.DSK", + "basic": "LOADM\"BLITZ\":EXEC\n" + }, + "beb8d0b62b3f142d43a812dc33ca732a5af787bf": { + "filename": "Bleep (CLOAD).zip", + "disk0": "BLEEP.DSK", + "basic": "RUN\"BLEEP\"\n" + }, + "ba704ad28443f0c962a4e63457a54c32fd494f40": { + "filename": "Block Buster (Chromasette).zip", + "disk0": "BLCKBST.DSK", + "joy-right": "mjoy0", + "basic": "RUN\"BLCKBST\"\n" + }, + "930949f4ac27489cf3e44aa38630d4fe984d9ddd": { + "filename": "Blockade (Chromasette).zip", + "disk0": "BLOCKADE.DSK", + "joy-left": "kjoy0", + "basic": "RUN\"BLOCKADE\"\n" + }, + "34442f82ce56fbf0b2566f8bbf38f9e380e91843": { + "filename": "Blockade (J.R. Applegate).zip", + "disk0": "BLOCKADE.DSK", + "joy-right": "kjoy0", + "basic": "LOADM\"BLOCKADE\":EXEC\n" + }, + "404e8895e3a7de0a2800af2a5670798c5c831c9b": { + "filename": "Bloc Head (Computerware).zip", + "disk0": "BLOCHEAD.DSK", + "joy-right": "kjoy0", + "basic": "LOADM\"BLOCHEAD\":EXEC\n" + }, + "23542a4919dc0efcbf82b825718304d958d830e0": { + "filename": "Blox (Hyper Tech Software) (Coco 3).zip", + "machine": "coco3", + "disk0": "BLOX.DSK", + "basic": "LOADM\"BLOX\":EXEC\n" + }, + "798cdd4fc45741ef2ff108464696b90ae88b1b66": { + "filename": "Bob and the Alien Fireflies (8bitsinthebasement).zip", + "disk0": "CocoBob.dsk", + "joy-left": "kjoy0", + "basic": "LOADM\"COCOBOB\":EXEC\n" + }, + "e74a3298178384807a0c4d5edc8fd55e0f01e0b9": { + "filename": "Body Parts (Elite Software).zip", + "disk0": "BODYPTS.DSK", + "basic": "LOADM\"BODYPTS\":EXEC\n" + }, + "6011f8c0950ec7204f9f00d0ba7510b471aa0465": { + "filename": "Bomber Command (Ark Royal Games).zip", + "disk0": "BOMBER.DSK", + "basic": "RUN\"BOMBER\"\n" + }, + "ef8990d2a68f854ec77fed7dec4b8f3f4414849c": { + "filename": "Bonka (J. Morrison Micros) (Coco Port) (SG-8 intro).zip", + "disk0": "BONKA.DSK", + "basic": "LOADM\"BONKA\":EXEC\n" + }, + "fdf06212c89ff2fa4faef4e7afc1aa3c9c5057c3": { + "filename": "Boris the Bold (Blaby) (Coco port).zip", + "disk0": "BORIS.DSK", + "joy-right": "kjoy0", + "basic": "LOADM\"BORIS\":EXEC\n" + }, + "966085882948c57c5e2624b446d910e2c6e42986": { + "filename": "Bouncing Boulders (Diecom Products).zip", + "disk0": "BOULDERS.DSK", + "joy-right": "kjoy0", + "basic": "LOADM\"BOULDERS\":EXEC\n", + "tv-input": "redblue" + }, + "522c7be62af43d18b265cd16db2a42c2cbfd9546": { + "filename": "Bowling (A. J. Pomerantz).zip", + "disk0": "BOWL.DSK", + "joy-right": "kjoy0", + "basic": "RUN\"BOWL\"\n" + }, + "3e2220e4ac7b10ae37d16b127180fb2b3d065e36": { + "filename": "Bonk! (Color Software Systems) (OS-9) (Coco 3).zip", + "machine": "coco3", + "disk0": "bonk.dsk", + "joy-right": "mjoy0", + "basic": "DOS\n" + }, + "b9b0f93a0e885d1099cb6c580cd219c6ec07efc4": { + "filename": "Box Shoot (Chromasette).zip", + "disk0": "BOXSHOOT.DSK", + "joy-left": "mjoy0", + "joy-right": "kjoy0", + "basic": "LOADM\"BOXSHOOT\":EXEC\n" + }, + "d48b3df5719c6e0600d57019f6b461c1d530c304": { + "filename": "Boxing (Tim Jones).zip", + "disk0": "BOXING.DSK", + "joy-right": "kjoy0", + "basic": "RUN\"BOXING\"\n" + }, + "1da6e454e73146684b80e39391ad8fc00c689872": { + "filename": "Breakthru (Avalon Hill).zip", + "disk0": "BREAKTHR.DSK", + "joy-right": "mjoy0", + "basic": "LOADM\"BREAKTHR\":EXEC\n" + }, + "344c9eb5d3f4e10118a822608cce8a63f903cfa0": { + "filename": "Brew Master (Novasoft).zip", + "disk0": "BREW.DSK", + "joy-right": "kjoy0", + "basic": "LOADM\"BREW\":EXEC\n" + }, + "e3b987ff0a2fe8f433508775e25fc9270d6c5764": { + "filename": "Brick Pong (T&D Software).zip", + "disk0": "BRIKPONG.DSK", + "joy-right": "mjoy0", + "basic": "LOADM\"BRIKPONG\":EXEC\n" + }, + "3915624b547c6ed68fe435511ef13764eabc67ba": { + "filename": "Bridge Tutor (Tandy).zip", + "disk0": "BRIDGE.DSK", + "basic": "LOADM\"BRIDGE\":EXEC\n" + }, + "55a7a0307e89318e4ef2eb4f5114f2e28520814c": { + "filename": "Bubble Buster (T&D Software).zip", + "disk0": "BUBBLE.DSK", + "joy-right": "mjoy0", + "basic": "LOADM\"BUBBLE\":EXEC\n" + }, + "b7db692333f8d648fe80d0ca3e78cd2beb68adda": { + "filename": "Buggle (Mystery Glass House Software).zip", + "disk0": "BUGGLE.DSK", + "basic": "RUN\"BUGGLE\"\n" + }, + "c7e51db58afd16c0cd9404fe4968a8c8b9a20d65": { + "filename": "Bugs-II (Four Star Software).zip", + "disk0": "BUGS-II.DSK", + "basic": "LOADM\"BUGS-II\":EXEC\n" + }, + "756e63015ada0a3d5ad5a53f1a3c6d80072ab01c": { + "filename": "Bugs (Color Computer Magazine).zip", + "disk0": "BUGS.DSK", + "basic": "RUN\"BUGS\"\n" + }, + "95ee7fee673eb1e078972cb5b99a52830790a764": { + "filename": "Bumpers (Mark Data Products).zip", + "disk0": "BUMPERS.DSK", + "joy-left": "mjoy0", + "basic": "LOADM\"BUMPERS\":EXEC\n" + }, + "9a5cbeedb8f5c61648906f729297aab10a6a1b64": { + "filename": "Butterfly Patrol (Spectral Associates).zip", + "disk0": "BFPATROL.DSK", + "joy-right": "kjoy0", + "basic": "LOADM\"BFPATROL\":EXEC\n" + }, + "4aac210099b77ef2da00c0658cc05b8460c2e132": { + "filename": "Buried Buxx (Jeff Robinson).zip", + "disk0": "BIGBUCKS.DSK", + "joy-right": "kjoy0", + "basic": "RUN\"BIGBUCKS\"\n" + }, + "4691a9ee01983f9d17ff89640a401c525af99d11": { + "filename": "Bustout (Tandy).zip", + "disk0": "BUSTOUT.DSK", + "joy-left": "mjoy0", + "basic": "LOADM\"BUSTOUT\":EXEC\n" + }, + "0607055c0efa0a0323fa0e3dcd61952fb8cc6a8d": { + "filename": "Buzzard Bait (Tom Mix Software).zip", + "disk0": "BUZZARD.DSK", + "joy-left": "kjoy0", + "basic": "LOADM\"BUZZARD\":EXEC\n" + }, + "8bb4b6e2424a7bcb4fe063d3dbcc59dcda84b801": { + "filename": "Buzzworm (Tom Mix Software).zip", + "disk0": "BUZZWORM.DSK", + "joy-left": "kjoy0", + "basic": "LOADM\"BUZZWORM\":EXEC\n", + "tv-input": "redblue" + }, + "d846c3a1823b93c72d801945aadca8e5fcf3f528": { + "filename": "Caladuril (Diecom Products).zip", + "disk0": "caladuri.dsk", + "basic": "LOADM\"CAL\":EXEC\n" + }, + "fb14ddd3df1bd56114be70265269ede7de4d25f5": { + "filename": "Calixto Island (Text) (Mark Data Products).zip", + "disk0": "CALIXTO.DSK", + "basic": "LOADM\"CALIXTO\":EXEC\n" + }, + "e870598c7eebac95130143f0d12d6f7067bc6bc3": { + "filename": "Camel (Paul Shoemaker).zip", + "disk0": "CAMEL.DSK", + "basic": "LOADM\"CAMEL\":EXEC\n" + }, + "67bf479749edb3e9789df9638aa827b93ff220dc": { + "filename": "Calixto Island (Mark Data Products).zip", + "disk0": "CALIXTO.DSK", + "basic": "LOADM\"CALIXTO\":EXEC\n", + "tv-input": "redblue" + }, + "a8e74f25b9a06bf987486a8113be06c1974a91e8": { + "filename": "Candy Co (Intracolor).zip", + "disk0": "CANDYCO.DSK", + "joy-left": "kjoy0", + "basic": "LOADM\"CANDYCO\":EXEC\n", + "tv-input": "redblue" + }, + "fc786aed43d957778c80f02aaf68132940a4e23a": { + "filename": "Canfield (Compute!) (Coco 3).zip", + "machine": "coco3", + "disk0": "CANFIELD.DSK", + "basic": "RUN\"CANFIELD\"\n" + }, + "1504abd07ec5aa59e9cf2017f1defa75dbde8445": { + "filename": "Canyon Climber (Tandy).zip", + "disk0": "CANYON.DSK", + "joy-right": "kjoy0", + "basic": "LOADM\"CANYON\":EXEC\n" + }, + "3a94c57a6e8569d67b37451fb07b916c2c35a347": { + "filename": "Card King (David A. Mills) (SSC).zip", + "disk0": "CARDKING.DSK", + "basic": "RUN\"CARDKING\"\n" + }, + "b37577c4af549cda98c87c82e6a86373c62e21eb": { + "filename": "Card Games (Tandy).zip", + "disk0": "cards.dsk", + "basic": "DIR\n" + }, + "7873dcc80076ebdd24dac64d104a30f38e125e56": { + "filename": "Cartel (T&D Software).zip", + "disk0": "CARTEL.DSK", + "basic": "RUN\"CARTEL\"\n" + }, + "75774f10736a1e7cf0f2fcafc36cc781b4e49bf4": { + "filename": "Cashman (Computer Shack).zip", + "disk0": "CASHMAN.DSK", + "joy-left": "kjoy0", + "basic": "LOADM\"CASHMAN\":EXEC\n", + "tv-input": "redblue" + }, + "9776d4431279476efc18ee9f44487ed4a75d25e4": { + "filename": "Castle Guard (Tandy).zip", + "disk0": "CASTLE.DSK", + "joy-left": "mjoy0", + "basic": "LOADM\"CASTLE\":EXEC\n" + }, + "543cf7565d8eb4faa1dbf353d696a176f72d38a7": { + "filename": "Castle of Tharoggad (Tandy) (Coco 3).zip", + "machine": "coco3", + "disk0": "THAROGAD.DSK", + "joy-right": "mjoy0", + "basic": "LOADM\"THAROGAD\":EXEC\n" + }, + "774669e4f30601afed9848c004c475109b420272": { + "filename": "Castle Ragoona (Family Computers).zip", + "disk0": "ragoona.dsk", + "basic": "RUN\"RAGOONA\"\n" + }, + "00ff6ea6b877c2cb3913567876a0bbf6bc024ce2": { + "filename": "Castle Zhagwhar (Keith Schuler).zip", + "disk0": "CASTLE1.DSK", + "joy-right": "kjoy0", + "basic": "RUN\"CASTLE1\"\n" + }, + "750ae97bf51be80e7e7846f6e28ae96a4874a52e": { + "filename": "Castle Thuudo (The Rainbow).zip", + "disk0": "thuudo.dsk", + "basic": "RUN\"THUUDO\"\n", + "tv-input": "redblue" + }, + "eefd25e2f64c592837e3a9f88c19b83989da2282": { + "filename": "Catacombs (L. Miller) (Coco 3).zip", + "machine": "coco3", + "disk0": "CATACOMB.DSK", + "joy-right": "kjoy0", + "basic": "LOADM\"CATACOMB\":EXEC\n" + }, + "27553a7056ea7018476a8d566814c3c64f353a62": { + "filename": "Catacomb (Oregon Color Computer Systems).zip", + "disk0": "CATACOMB.DSK", + "joy-right": "kjoy0", + "basic": "LOADM\"CATACOMB\":EXEC\n" + }, + "b995031621a9a310ac545f22518445ceb773d00a": { + "filename": "Catalyst (Computer Shack).zip", + "disk0": "CATALYST.DSK", + "joy-right": "kjoy0", + "basic": "LOADM\"CATALYST\":EXEC\n" + }, + "42f18561081ea775b93b5f039cd16b7b3ce9708e": { + "filename": "Catch 'em (Aadvark-80).zip", + "disk0": "CATCHEM.DSK", + "joy-right": "mjoy0", + "basic": "LOADM\"CATCHEM\":EXEC\n" + }, + "af159c18717c92b5342487963f6944b1d3b3c4e2": { + "filename": "Caterpillar (Aadvark-80).zip", + "disk0": "CATRPILR.DSK", + "joy-right": "mjoy0", + "basic": "LOADM\"CATRPILR\":EXEC\n" + }, + "e5a11c83773d8d88bb294916f59f5402d36ad410": { + "filename": "Caterpillar Cave (T&D Software).zip", + "disk0": "CATCAVE.DSK", + "basic": "LOADM\"CATCAVE\":EXEC\n" + }, + "acc1bb1f460dd3b2103ebf159e899df635a47a1b": { + "filename": "Caterpillar (David Crandall).zip", + "disk0": "CATERDAV.DSK", + "joy-right": "kjoy0", + "basic": "LOADM\"CATERDAV\":EXEC\n" + }, + "91212fc1996ed01b64a24cb1dfc6dbc4f85d46cd": { + "filename": "Cave Fighter (Cable Software) (Coco port).zip", + "disk0": "CAVEFGHT.DSK", + "joy-right": "kjoy0", + "basic": "LOADM\"CAVEFGHT\":EXEC\n" + }, + "ef2ab3091c8b5edbf52221795541cd74bebc0a8c": { + "filename": "Cave Hunter (Mark Data Products) (SG-24) (Coco 1-2).zip", + "disk0": "HUNTER.DSK", + "joy-right": "kjoy0", + "basic": "LOADM\"HUNTER\":EXEC\n" + }, + "33f49b7412d801b466a1ecac56c8aabb59072947": { + "filename": "Caverns of Narzod, The (Max Bettridge).zip", + "disk0": "NARZOD.DSK", + "joy-right": "kjoy0", + "basic": "RUN\"NARZOD\"\n" + }, + "999959f61c12cd6bd2ccb22f50f19778d8e9fba5": { + "filename": "Cavehunt (Hot Coco).zip", + "disk0": "CAVEHUNT.DSK", + "joy-right": "kjoy0", + "basic": "RUN\"CAVEHUNT\"\n" + }, + "9b1b540ca0c523c8d11649fc3ac8e787b4af2be1": { + "filename": "Caviator (T&D Software).zip", + "disk0": "caviator.dsk", + "joy-right": "kjoy0", + "basic": "RUN\"CAVIATOR\"\n" + }, + "b62d08eb49f1325e0ca03042e9b1c2e0a184cf4d": { + "filename": "Cave Walker (OS-9) (Tandy).zip", + "disk0": "CAVEWALK.DSK", + "joy-right": "kjoy0", + "basic": "DOS\n" + }, + "8e0b3ff1b423d954e2986be94698234dd4801fdd": { + "filename": "Caves of the Unwashed Heathen (Bug fixed) (Aaron Oliver).zip", + "machine": "coco3", + "disk0": "Caves (coco 3).dsk", + "basic": "RUN\"HEATHEN\"\n" + }, + "267655a6b6d85f286c04c57ef36de69d2bc106ea": { + "filename": "Caves of the Unwashed Heathen (Bug fixed) (Aaron Oliver).zip", + "machine": "coco3", + "disk0": "Caves (coco 3).dsk", + "basic": "RUN\"HEATHEN\"\n" + }, + "81525ab6d036c32ad0fd764b0e5241bf2b530deb": { + "filename": "Caves of the Unwashed Heathen (Paul Shoemaker).zip", + "disk0": "caves2.dsk", + "basic": "RUN\"HEATHEN\"\n" + }, + "aa9243db907145427b33d383e7ab3b7692c593a9": { + "filename": "Caves of the Unwashed Heathen (Paul Shoemaker).zip", + "disk0": "caves2.dsk", + "basic": "RUN\"HEATHEN\"\n" + }, + "5c76505600a9c2cd37029ccfa0b72a9183ee1c64": { + "filename": "CC Space Trek (Color Computer Magazine).zip", + "disk0": "STREK2.DSK", + "basic": "RUN\"STREK2\"\n" + }, + "0f4dd6687560857ebd1b086e70f95551a42458db": { + "filename": "CC-Thello (Spectral Associates).zip", + "disk0": "CCTHELLO.DSK", + "joy-left": "mjoy0", + "basic": "LOADM\"CCTHELLO\":EXEC\n" + }, + "63ef14df567829caeb28d670d403c011e3df4337": { + "filename": "Chambers (Tom Mix Software).zip", + "disk0": "CHAMBERS.DSK", + "joy-right": "kjoy0", + "basic": "LOADM\"CHAMBERS\":EXEC\n" + }, + "524c3461649a850aabccce42f03f60ead67328c2": { + "filename": "Chambers (1984) (Tom Mix Software).zip", + "disk0": "CHAMB84.DSK", + "joy-right": "kjoy0", + "basic": "LOADM\"CHAMBERS\":EXEC\n" + }, + "e9115c1041af0460adabb9d8dcb73c4f32b0571c": { + "filename": "Chatwin Manor (Goldsoft).zip", + "disk0": "Chatwin Manor.dsk", + "basic": "RUN\"START\"\n" + }, + "057fa375c48986eb0fc4756c88b9b35c4647067a": { + "filename": "Champion (Mark Data Products).zip", + "disk0": "champion.dsk", + "joy-right": "mjoy0", + "basic": "LOADM\"BOOT\":EXEC\n" + }, + "b5c833c82a072861776e92504a576cad4ee01528": { + "filename": "C-Hawk Ball (Stephen Macri) (Coco 3).zip", + "machine": "coco3", + "disk0": "CHAWK3.DSK", + "basic": "RUN\"CHAWK3\"\n" + }, + "2b8dfe8561752bea4901bab440a5e6c3f46531a8": { + "filename": "Checker King (PMODE 4) (Tandy).zip", + "disk0": "CHEKRSBW.DSK", + "basic": "LOADM\"CHEKRSBW\":EXEC\n" + }, + "3cb825e87529f629b87e3ebc02bf20de2b1a0392": { + "filename": "Checker King (Tandy).zip", + "disk0": "CHECKERS.DSK", + "basic": "LOADM\"CHECKERS\":EXEC\n" + }, + "7ec16df5096f65454d2b339f4613b371b716473e": { + "filename": "Checkers (Tood Burns).zip", + "disk0": "CHECKERS.DSK", + "basic": "RUN\"CHECKERS\"\n" + }, + "9287876f83d29296b3625e2a5d20b9fb43bbf37c": { + "filename": "Checkers (R. Young).zip", + "disk0": "CHECKR.DSK", + "basic": "RUN\"D\"\n" + }, + "118ab34fb989a1a1d2393afc318bb7eaec4d4166": { + "filename": "ChesSD (Software Dynamics).zip", + "disk0": "CHESSD.DSK", + "basic": "RUN\"CHESS\"\n" + }, + "66362c0b10f2b3905dd31d2c10c602c5e90bd26f": { + "filename": "Chess (R. Young).zip", + "disk0": "CHESSR.DSK", + "basic": "RUN\"CHESS\"\n" + }, + "8ea9e06d3000b45f348491e5c51607fbfc8bc588": { + "filename": "Chess (R. Young).zip", + "disk0": "CHESSR.DSK", + "basic": "RUN\"CHESS\"\n" + }, + "22790f1cd45b9edf792f856dc02ae7a9caf638fb": { + "filename": "Chess Set (Microsoft).zip", + "disk0": "CHESS.DSK", + "joy-right": "mjoy0", + "basic": "RUN\"CHESS\"\n" + }, + "6f6fba697a95e89d04eded5dad3d291fa8d47f17": { + "filename": "Chopper Rescue (Prism Software).zip", + "disk0": "chopper.dsk", + "joy-right": "kjoy0", + "basic": "RUN\"CHOPPER\"\n" + }, + "29d21519d28e5d973731ef8ab81d1a9aabd6f21d": { + "filename": "Chopper Strike (Computer Shack-Michtron).zip", + "disk0": "CHOPPER.DSK", + "joy-right": "kjoy0", + "basic": "LOADM\"CHOPPER\":EXEC\n" + }, + "54bda01fd5c21e4cbb4548780e64c53d52ccd44b": { + "filename": "Christmas Match (Paul Shoemaker).zip", + "machine": "coco3", + "disk0": "XMASMTCH.DSK", + "joy-right": "mjoy0", + "basic": "LOADM\"MATCHCC3\":EXEC\n" + }, + "d30afec941d86c7108468287e0c3bcfab8737ab3": { + "filename": "Chuckie Egg (A&F Software) (Coco port).zip", + "disk0": "CHUCKIE.DSK", + "basic": "LOADM\"CHUCKIE\":EXEC\n" + }, + "7992c66adc3a007ba80126d2a64b1ac14713333b": { + "filename": "Citadel of Pershu, The (Tim Hartnell) (Coco 3).zip", + "machine": "coco3", + "disk0": "CITADEL.DSK", + "basic": "RUN\"CITADEL\"\n" + }, + "6e9c506ae57a6ffb47b8de0b7bf16b013b6f3949": { + "filename": "City (T&D Software).zip", + "disk0": "CITY.DSK", + "basic": "LOADM\"CITY\":EXEC\n" + }, + "f8bf822d60e324c3c50150a192538a5d79d9e420": { + "filename": "City War (Prickly-Pear Software).zip", + "disk0": "CITYWAR.DSK", + "basic": "RUN\"CITY WAR\"\n" + }, + "7ea20684579f79f99e75c046213867870243c430": { + "filename": "City Bomber (The Saint John Gallery).zip", + "disk0": "CITYBMBR.dsk", + "joy-right": "kjoy0", + "basic": "RUN\"BOOT\"\n" + }, + "02543cf611c676b300c2f153f3f5f58c19739fe3": { + "filename": "Classic Solitaire (Eversoft Games) (fixed for 1 and 2MB RAM) (Coco 3).zip", + "machine": "coco3", + "disk0": "KLONDIKE.DSK", + "joy-right": "mjoy0", + "basic": "RUN\"BOOT\"\n" + }, + "1d1757a5e629348c0661aec12a851aec3bdbf3f4": { + "filename": "Classic Solitaire (Eversoft Games) (Coco 3).zip", + "machine": "coco3", + "disk0": "KLONDIKE.DSK", + "joy-right": "mjoy0", + "basic": "RUN\"BOOT\"\n" + }, + "7fb8bfc412e3645449ba9a22595672bf6d05db85": { + "filename": "Clickomania! v1.0 (Todd Wallace) (Coco 3).zip", + "machine": "coco3", + "disk0": "click.dsk", + "joy-right": "mjoy0", + "basic": "LOADM\"CLICK\":EXEC\n" + }, + "4a157c64d337c2b431555c5f1310ece47b75afd6": { + "filename": "Cliffhanger (INPUT).zip", + "disk0": "cliffhgr.dsk", + "joy-right": "kjoy0", + "basic": "RUN\"CLIFFHGR\"\n" + }, + "8d55d7b6f7e1e97af07d192ca909fb6f104eb147": { + "filename": "Climb (CLOAD).zip", + "disk0": "climb.dsk", + "joy-right": "kjoy0", + "basic": "LOADM\"CLIMB\":EXEC\n" + }, + "b27936cc9a73883db9b84aa48651e40d8fd0ea0d": { + "filename": "Clowns & Balloons (Tandy).zip", + "disk0": "CLOWNS.DSK", + "joy-right": "mjoy0", + "basic": "LOADM\"CLOWNS\":EXEC\n" + }, + "2f4fbb0e2e5b567645dcd8bcb0bc06e2e74e471d": { + "filename": "Coco 3 Solitaire (Paul Shoemaker) (Coco 3).zip", + "machine": "coco3", + "disk0": "KLONDKE3.DSK", + "basic": "RUN\"KLONDKE3\"\n" + }, + "c9563213e544681bfcf5191dd800214947eabde8": { + "filename": "CoCo 3-D Rat Maze (Retro Rick).zip", + "disk0": "RATMAZE.DSK", + "basic": "RUN\"RATMAZE\"\n" + }, + "15e24e362f7ecbdd33f1b975a82d954f44c36d23": { + "filename": "Coco 3 Cribbage (Bob van der Poel) (Coco 3).zip", + "machine": "coco3", + "disk0": "CC3CRIB.DSK", + "basic": "RUN\"CRIBBAGE\"\n" + }, + "385bc7fd130eab0ecb35392fa5388b9d57cbcd73": { + "filename": "Coco 3 Video Poker (CoCo Brothers) (Coco 3).zip", + "machine": "coco3", + "disk0": "VIDEOPKR.DSK", + "basic": "RUN\"BOOT\"\n" + }, + "ec7e8c9da2f7c1f45749b3fdae27f98a5a4c22f3": { + "filename": "Coco 3 Yahtzee Game (Keith Morabeto) (Coco 3).zip", + "machine": "coco3", + "disk0": "YAT3.DSK", + "basic": "RUN\"YAT3\"\n" + }, + "10bc7e20a1d8e203029c9cade7c80c2c98dc88d0": { + "filename": "Coco Championship Golf (The Rainbow).zip", + "disk0": "COCOGOLF.DSK", + "basic": "RUN\"COCOGOLF\"\n" + }, + "2d93237bc18a359b42d4a23ffa9a3d38b285a42e": { + "filename": "Coco Craps (Anthony Morris) (Coco 3).zip", + "machine": "coco3", + "disk0": "CRAPS3.DSK", + "basic": "RUN\"CRAPS3\"\n" + }, + "32533789ba4245f14ab374963fc080bf13fdd6f9": { + "filename": "CocoGROW (Jay Searle) (Coco 3).zip", + "machine": "coco3", + "disk0": "COCOGROW.DSK", + "basic": "LOADM\"COCOGROW\":EXEC\n" + }, + "adeb6d3cf5fa104831ea62f879880258af3bb5dc": { + "filename": "COCOdle (Rick Adams) (Coco 3).zip", + "machine": "coco3", + "disk0": "cocole.dsk", + "basic": "RUN\"COCOLE\"\n" + }, + "96155aaa83db3dc43d9d461d4b05a28cde667dc7": { + "filename": "Cocolife (Paul Ripke).zip", + "disk0": "cocolife.dsk", + "basic": "LOADM\"LIFEG1R\":EXEC\n" + }, + "c513d7ed2c9bf83ccb63f03b5264fb208d8be832": { + "filename": "Coco-III Solitaire (The Rainbow) (Coco 3).zip", + "machine": "coco3", + "disk0": "cc3sol.dsk", + "basic": "RUN\"SOLITAIR\"\n" + }, + "3c42873c6e263c8008bf9de301fd4fad02edb121": { + "filename": "Coco-Pong (Chris Pedersen).zip", + "disk0": "PONG.DSK", + "basic": "RUN\"PONG\"\n" + }, + "493110b8be67e8a00a188b677ab3ad7e25c1b079": { + "filename": "Coco-Q (The Rainbow) (Coco 3).zip", + "machine": "coco3", + "disk0": "COCO-Q.DSK", + "basic": "RUN\"COCO-Q\"\n" + }, + "42fe89fe2016d145df52ac0bed9eceabad702fe3": { + "filename": "Coco Miniature Golf (The Rainbow).zip", + "disk0": "MINIGOLF.DSK", + "joy-right": "kjoy0", + "basic": "RUN\"MINIGOLF\"\n" + }, + "248ac9f69593710e47af6ef23e832753fb353112": { + "filename": "CoCo's Bowling Alley (Floyd Keirnan).zip", + "disk0": "BOWLING.DSK", + "joy-right": "kjoy0", + "basic": "RUN\"BOWLING\"\n" + }, + "60f7013dbd6959ab54e759fb35a40cf82002eef0": { + "filename": "Coco Solitaire (Paul Shoemaker).zip", + "disk0": "SOLITARE.DSK", + "basic": "RUN\"SOLITARE\"\n" + }, + "fa588f95896705ff0fbef78af90a5824d3536824": { + "filename": "CoCoStud (CoCo Brothers Software) (Coco 3).zip", + "machine": "coco3", + "disk0": "COCOSTUD.DSK", + "basic": "RUN\"BOOT\"\n" + }, + "c8d87ff447989c867ce781de350f9abb3e07a29c": { + "filename": "Coco-Terrestrial (ET) (T&D Software).zip", + "disk0": "ET.DSK", + "basic": "RUN\"ET\"\n" + }, + "763dabc2c28af742da51b00f56cd0f6d78ab699a": { + "filename": "CoCothello (ColorSystems) (OS-9) (Coco 3).zip", + "machine": "coco3", + "disk0": "othello.dsk", + "joy-right": "mjoy0", + "basic": "DOS\n" + }, + "7499d11006f9f056e9876210fde5eeb538b01562": { + "filename": "Coco Yahtzee (Patrick Leabo) (Coco 3).zip", + "machine": "coco3", + "disk0": "YAHTZEE.DSK", + "basic": "RUN\"YAHTZEE\"\n" + }, + "6e9aa0c072327ea9906abfe43ad46d58f147927e": { + "filename": "CoCoYahtzee (ColorSystems) (OS-9) (Coco 3).zip", + "machine": "coco3", + "disk0": "yahtzee.dsk", + "joy-right": "mjoy0", + "basic": "DOS\n" + }, + "b5aeb5b1b261e589cef1008275073e2537dafca7": { + "filename": "Color Blackjack (The Rainbow).zip", + "disk0": "BLACKJAC.DSK", + "basic": "RUN\"BLACKJAC\"\n" + }, + "d953e3f13da1d52000e5adad1364230824e0602b": { + "filename": "Coco Zone (The Rainbow).zip", + "disk0": "cocozone.dsk", + "basic": "RUN\"COCOBOOT\"\n" + }, + "26db3afd91fec18f507dfb476c5be27f33658390": { + "filename": "Color Bowl Football (Computerware).zip", + "disk0": "COLRBOWL.DSK", + "joy-left": "kjoy0", + "basic": "LOADM\"COLRBOWL\":EXEC\n" + }, + "401a779ebbba18c182bbc33d47bc845b68ada5df": { + "filename": "Color Blast (Challenge Software) (SG-24 intro).zip", + "disk0": "COBLAST.DSK", + "joy-right": "kjoy0", + "basic": "LOADM\"COBLAST\":EXEC\n" + }, + "d77609a0ed2011764c3ff32d7ffa498921ec6578": { + "filename": "Color Car Action (Novasoft) (6809 optimized).zip", + "disk0": "CLRCAR68.DSK", + "joy-left": "kjoy0", + "basic": "LOADM\"COLORCAR\":EXEC\n" + }, + "1493e8a05431510a2702dc2a49787669821e2d98": { + "filename": "Color Caterpillar (Soft Sector Marketing Inc.).zip", + "disk0": "CATERPIL.DSK", + "joy-left": "kjoy0", + "basic": "LOADM\"CATERPIL\":EXEC\n" + }, + "3c31efa3fd1424e1a6881c015454fe47d7ad7640": { + "filename": "Color Computer 3 Karate (Nigel Fredericks) (Coco 3).zip", + "machine": "coco3", + "disk0": "3KARATE.DSK", + "basic": "LOADM\"3KARATE\":EXEC\n" + }, + "670c8f41cdfb4378c0492b462eb3d3fd9f815b1e": { + "filename": "Color Car Action (Novasoft).zip", + "disk0": "COLORCAR.DSK", + "joy-left": "kjoy0", + "basic": "LOADM\"COLORCAR\":EXEC\n" + }, + "d03aa2ba90d1524efc946a099a1505b451410e2a": { + "filename": "Color Computer Clue (2018) (LDG Free Software).zip", + "disk0": "CLUE.DSK", + "basic": "RUN\"CLUE\"\n" + }, + "87b530c2da2412826f42a55acaedab4134e47b7f": { + "filename": "Color Computer Clue (LDG Free Software) (Coco 3).zip", + "machine": "coco3", + "disk0": "cc3clue.dsk", + "basic": "RUN\"CC3CLUE\"\n" + }, + "3e0589daec6606aec9a71c4fa0cb86a3fa7fd583": { + "filename": "Color Cubes (Tandy).zip", + "disk0": "CUBES.DSK", + "basic": "LOADM\"CUBES\":EXEC\n" + }, + "19f5cff7e2fb6298ab24db3af7d1ef70d9262d7d": { + "filename": "Color Computer Clue (LDG Free Software).zip", + "disk0": "CLUE.DSK", + "basic": "PCLEAR1:RUN\"CLUE\"\n" + }, + "f9fa7c37699aefb454e803a4a890446b86fda162": { + "filename": "Color Dot (CLOAD Publications Inc.).zip", + "disk0": "COLORDOT.DSK", + "joy-left": "mjoy0", + "joy-right": "mjoy0", + "basic": "RUN\"COLORDOT\"\n" + }, + "675bfbb55eb774bb2ee65a4443d21c4282c0458d": { + "filename": "Color Eights (Pierre Sarrazin).zip", + "disk0": "color8-0.1.12.dsk", + "basic": "RUN\"COLOR8\"\n" + }, + "046a9ea82b334b23c10bafebf66c544a80f06858": { + "filename": "Colordle (Paul Ripke).zip", + "disk0": "colordle.dsk", + "basic": "RUN\"COLORDLE\"\n" + }, + "e3edeb2b85bbde8fbc125daf3be51365ce352a40": { + "filename": "Color Invaders (Computerware).zip", + "disk0": "COLORINV.DSK", + "joy-right": "mjoy0", + "basic": "LOADM\"COLORINV\":EXEC\n" + }, + "b18cd57dda5ce787259e0da1a72bc684729f1362": { + "filename": "Color Golf III (Tom Mix Software).zip", + "disk0": "GOLF.DSK", + "basic": "RUN\"GOLF\"\n" + }, + "f65c64dc1ac4daa446a92599fd79863afdaf9b47": { + "filename": "Color Meteroids (Spectral Associates).zip", + "disk0": "METEORS.DSK", + "joy-left": "mjoy0", + "joy-right": "kjoy0", + "basic": "LOADM\"METEORS\":EXEC\n" + }, + "aab545c5160cd66188f0b3ad337ba6044dbd47da": { + "filename": "Color Modem Chess (Double Density Software).zip", + "disk0": "MODCHESS.DSK", + "basic": "RUN\"MODCHESS\"\n" + }, + "e5daf3a86799a8a62217206930c22d1e0a100b67": { + "filename": "Colorout (Spectral Associates).zip", + "disk0": "COLOROUT.DSK", + "basic": "LOADM\"COLOROUT\":EXEC\n" + }, + "1489131a49699ab90393f0e95e06db1ae97a48cf": { + "filename": "Color Panic (Spectral Associate).zip", + "disk0": "CLRPANIC.DSK", + "joy-right": "kjoy0", + "basic": "LOADM\"CLRPANIC\":EXEC\n" + }, + "546f9e7884ec99db57e4cbc7b8168795b2383423": { + "filename": "Color Planet Invasion 1.1 (Spectral Associates).zip", + "disk0": "INVASION.DSK", + "joy-right": "kjoy0", + "basic": "LOADM\"INVASION\":EXEC\n" + }, + "2270b2c7332f524fd4c89ab5d1d38f6438755c9f": { + "filename": "Color Planet Invasion (Spectral Associates).zip", + "disk0": "INVASION.DSK", + "joy-right": "kjoy0", + "basic": "LOADM\"INVASION\":EXEC\n" + }, + "ae4fcafc06e0cd72cfbe7727e106fb41286870d0": { + "filename": "Colorpede (Intracolor).zip", + "disk0": "CPEDE.DSK", + "joy-left": "mjoy0", + "basic": "LOADM\"CPEDE\":EXEC\n" + }, + "0372bafeaa0ab93f8445f9a022868fe4e71c0c6f": { + "filename": "Color Poker (The Rainbow).zip", + "disk0": "POKER.DSK", + "basic": "RUN\"POKER\"\n" + }, + "1bb67f6603d74d9dfdf3061d2fca66defc7370a2": { + "filename": "Color Robot Battle (Tandy).zip", + "disk0": "ROBOT.DSK", + "basic": "LOADM\"ROBOT\":EXEC\n" + }, + "c5c3403fb50393e71c4cccecf314761495e20277": { + "filename": "Color Space Invaders (PMODE 4) (Spectral Associates).zip", + "disk0": "NEW-INV.DSK", + "basic": "LOADM\"NEW-INV\":EXEC\n" + }, + "ddf2d3534fb8d88495a7b4988f4229ccb1d0d58b": { + "filename": "Color Space Invaders (Spectral Associates).zip", + "disk0": "INVADERS.DSK", + "basic": "LOADM\"INVADERS\":EXEC\n" + }, + "613ab27851e44276390e0547b6501ad7722e369c": { + "filename": "Color Space Traders (Spectral Associates).zip", + "disk0": "TRADERS.DSK", + "basic": "RUN\"TRADERS\"\n" + }, + "9f96db912be81145de1431f2756385a7f726d2a1": { + "filename": "Color-Trek (Spectral Associates).zip", + "disk0": "TREK.DSK", + "basic": "LOADM\"TREK\":EXEC\n" + }, + "3183d3e3a48c2eae7d6d272acde2ef04829ca95e": { + "filename": "Colorzap (Spectral Associates).zip", + "disk0": "COLORZAP.DSK", + "joy-right": "kjoy0", + "basic": "LOADM\"COLORZAP\":EXEC\n" + }, + "19ed5144a6d470b9af7141308635e94ca1676776": { + "filename": "Colossal Cave (CRL) (OS-9) (Coco 3).zip", + "machine": "coco3", + "disk0": "colossal.dsk", + "basic": "DOS\n" + }, + "d6eb0dbd327a9cb5fa5b2fa02e07ad3bc75e6ea4": { + "filename": "Company Commander 1.1 (Ark Royal Games).zip", + "disk0": "COMPANY.DSK", + "basic": "RUN\"COMPANY\"\n" + }, + "09830a14fee61fe67514ab58ad357ac4bce15514": { + "filename": "Company Commander 2.0 (Ark Royal Games).zip", + "disk0": "COMPANY2.DSK", + "basic": "RUN\"COMPANY\"\n" + }, + "d78ba6e96326b8cb6c321c1f14bfe49da523dfe0": { + "filename": "Copta Snatch (Blaby Computer Games) (Coco port) (SG-8).zip", + "disk0": "copta.dsk", + "basic": "LOADM\"COPTA\"\n" + }, + "8e1f6403a3ba9c634594e938bd8b94d95f2deaf4": { + "filename": "Concentrate! (Eric A. Wolf) (Coco 3).zip", + "machine": "coco3", + "disk0": "CCTRATE.DSK", + "joy-right": "mjoy0", + "basic": "RUN\"CCTRATE\"\n" + }, + "6944fe58735598cdec5b73083ed6416a05ad01de": { + "filename": "Conflict (Soft Sector Marketing).zip", + "disk0": "conflict.dsk", + "basic": "RUN\"CONFLICT\"\n" + }, + "8d64e6456c3e1215a24eebf01646cd9a01b65320": { + "filename": "Conquering Armies (Mitchell Software).zip", + "disk0": "ARMIES.DSK", + "basic": "RUN\"ARMIES\"\n" + }, + "cd897573a114d99be90dd15d8f4c640eaa49b8d9": { + "filename": "Controllers, The (The Rainbow) (Coco 3).zip", + "machine": "coco3", + "disk0": "CONTROL.DSK", + "joy-right": "kjoy0", + "basic": "RUN\"CONTROL\"\n" + }, + "47bb42937e34a0f4522a2bf6ab8f304beacb8a75": { + "filename": "Cosmic Ambush (Nick Marentes) (Coco 3).zip", + "machine": "coco3", + "disk0": "cambush.dsk", + "joy-right": "kjoy0", + "basic": "RUN\"CAMBUSH\"\n" + }, + "4d36d5cd3fd4103e43021c37d4a4925ecb7aa290": { + "filename": "Cosmic Clones (Mark Data Products).zip", + "disk0": "CLONES.DSK", + "basic": "LOADM\"CLONES\":EXEC\n" + }, + "4f7936e852e3130cef5d549631d1008199bd956f": { + "filename": "Cosmic Cruiser (Imagine) (Coco port).zip", + "disk0": "CRUISER.DSK", + "basic": "LOADM\"CRUISER\":EXEC\n" + }, + "c24a4932878c0c33aa62535a40aa7f2599e9c261": { + "filename": "Cosmic Crusader (Blaby) (Coco port).zip", + "disk0": "CRUSADER.DSK", + "joy-right": "kjoy0", + "basic": "LOADM\"CRUSADER\":EXEC\n" + }, + "e604fbe33d580f83b5540ce00f0a5b86fb43fcfd": { + "filename": "Cosmic Sweeper (T&D Software).zip", + "disk0": "COSMIC.DSK", + "joy-right": "kjoy0", + "basic": "LOADM\"COSMIC\":EXEC\n" + }, + "844f44dfd48bf44dedcf8515b9d7e998a91bd287": { + "filename": "Cosmic Superbowl (Spectral Associates).zip", + "disk0": "SUPRBOWL.DSK", + "joy-right": "kjoy0", + "basic": "LOADM\"SUPRBOWL\":EXEC\n" + }, + "89fe30673be225ef6b8df39314482b875f085745": { + "filename": "Crash (Tom Mix Software).zip", + "disk0": "CRASH.DSK", + "joy-right": "kjoy0", + "basic": "LOADM\"CRASH\":EXEC\n" + }, + "22a94e8079baf6773faabe98ba694e06b4d13b30": { + "filename": "Crawler (T&D Software).zip", + "disk0": "CRAWLER.DSK", + "basic": "LOADM\"CRAWLER\":EXEC\n" + }, + "b9c003f369775af61f7acda69cbba2bbf26c6cc1": { + "filename": "Crazy 8's (Eversoft Games) (Coco 3).zip", + "machine": "coco3", + "disk0": "CRAZY8S.DSK", + "joy-right": "mjoy0", + "basic": "RUN\"CRAZY8\"\n" + }, + "7f0cdce10bb8f90fd3849f6baa1feb1888682acf": { + "filename": "Creature Feature (Color Software) (SG-24 intro).zip", + "disk0": "CREATURE.DSK", + "joy-right": "kjoy0", + "basic": "LOADM\"CREATURE\":EXEC\n" + }, + "a2a70382b43fc35a730f2411aa0f24716b812c0f": { + "filename": "Crazy Painter (The Cornsoft Group).zip", + "disk0": "CRAZPNTR.DSK", + "basic": "LOADM\"CRAZPNTR\":EXEC\n" + }, + "2b4711aa9e25a959659cc7daeda61d07131f5a2e": { + "filename": "Cribbage (John Griggs).zip", + "disk0": "CRIBBAGE.DSK", + "basic": "RUN\"CRIBBAGE\"\n" + }, + "eca44ccd9d4bf90c62cddfb99640344fb9f0c2e4": { + "filename": "Croaker (Color Horizon Software).zip", + "disk0": "CROAKER.DSK", + "joy-right": "kjoy0", + "basic": "LOADM\"CROAKER\":EXEC\n" + }, + "adda1266818d1bac5a222c4491b6f6e7287089df": { + "filename": "Croid 2.0 - Eliza (Spectral Associates).zip", + "disk0": "CROID.DSK", + "basic": "LOADM\"CROID\":EXEC\n" + }, + "8140972bc99da4954fddb7e3c500bccb5d03f921": { + "filename": "Crosswords (Tandy).zip", + "disk0": "CROSWORD.DSK", + "basic": "LOADM\"CROSWORD\":EXEC\n" + }, + "3bb04d28769366e778c89aa2735f5a51471219ba": { + "filename": "Croid (Andrew Simpson) (Coco 3).zip", + "machine": "coco3", + "disk0": "croid.dsk", + "basic": "RUN\"CROID\"\n" + }, + "932f4f076e6d53cbe50250ce73ec35074f7f6099": { + "filename": "Crown of Merro, The (Jeff Craig).zip", + "disk0": "MERRO.DSK", + "basic": "RUN\"MERRO\"\n" + }, + "dda92a511cbc13e17a6ee4ebf7d5e3991afd4153": { + "filename": "Crystal Revenge (Owlware).zip", + "disk0": "Crystal Revenge.dsk", + "joy-right": "kjoy0", + "basic": "RUN\"CRYSTAL\"\n" + }, + "30c57acd03e2418a20989143beb2d0d99658053d": { + "filename": "Cuber (PMODE 4) (Tom Mix Software).zip", + "disk0": "CUBER4.DSK", + "joy-right": "kjoy0", + "basic": "LOADM\"CUBER4\":EXEC\n" + }, + "221c3343ba24f371d1daa9f3479222f979486534": { + "filename": "Crystle Castles (Thundervision).zip", + "disk0": "CRYSTLE.DSK", + "joy-right": "kjoy0", + "basic": "LOADM\"CRYSTAL\":EXEC\n" + }, + "ec5d12e7047b972908acff161e6b3f78b3cf0794": { + "filename": "Cubix (Spectral Associates).zip", + "disk0": "CUBIX.DSK", + "joy-right": "kjoy0", + "basic": "LOADM\"CUBIX\":EXEC\n" + }, + "122f731fd9a7d3e09cce798e0567b24de987de72": { + "filename": "Cuber (Tom Mix Software).zip", + "disk0": "CUBER.DSK", + "joy-right": "kjoy0", + "basic": "LOADM\"CUBER\":EXEC\n" + }, + "69452b01e025da7e0cde64eff63ad42706431c58": { + "filename": "Currillian Cruiser (Glenn Calafati).zip", + "disk0": "CRUCRZR.dsk", + "joy-right": "kjoy0", + "basic": "RUN\"START\"\n" + }, + "b56d19f43b525bf69196edac96c3e025663616f8": { + "filename": "Cuthbert and the Golden Chalice (Microdeal).zip", + "disk0": "CHALICE.DSK", + "joy-right": "kjoy0", + "basic": "LOADM\"CUTHBERT\":EXEC\n" + }, + "1bba7c19f5cbf78e1d615ef6c4a6a8dc74493f9c": { + "filename": "Curse of Crowley Manor, The (Adventure International).zip", + "disk0": "crowley.dsk", + "basic": "LOADM\"CROWLEY\":EXEC\n" + }, + "5d90f2708bc07a34b5a99ee276505711be8ed965": { + "filename": "Cuthbert Goes Digging (Microdeal Cornwall).zip", + "disk0": "CUTHDIG.DSK", + "joy-right": "kjoy0", + "basic": "LOADM\"CUTHDIG\":EXEC\n" + }, + "aa2a7526f1b97de465604b12be0d41d243ddbf66": { + "filename": "Cuthbert in Space (Tandy).zip", + "disk0": "CUTHSPAC.DSK", + "basic": "LOADM\"CUTHSPAC\":EXEC\n" + }, + "ee89584415ecae2f2d53e6ba3640e5263acc6273": { + "filename": "Cuthbert Goes Walkabout (Tom Mix Software).zip", + "disk0": "CUTHWALK.DSK", + "joy-right": "kjoy0", + "basic": "LOADM\"CUTHWALK\":EXEC\n" + }, + "94015eb4b11db04c7b6ed6ded413c5f18eeca71b": { + "filename": "Cuthbert in the Cooler (Microdeal Cornwall).zip", + "disk0": "COOLER.DSK", + "joy-right": "kjoy0", + "basic": "LOADM\"COOLER\":EXEC\n" + }, + "0c158bb6bc83d5c27f2ae44407ddb16b984e1346": { + "filename": "Cuthbert in the Mines (Microdeal Cornwall).zip", + "disk0": "CUTHMINE.DSK", + "basic": "LOADM\"CUTHMINE\":EXEC\n" + }, + "35a9c81ad1a24dbfe9753f6fa22844f27081a206": { + "filename": "Cyber Tank (Mark Data Products).zip", + "disk0": "CYBRTANK.DSK", + "basic": "LOADM\"CYBRTANK\":EXEC\n" + }, + "de33d485f7107e3a43680b1ce15943e4a2e99892": { + "filename": "Cyberwar (Bumblebee Software).zip", + "disk0": "CYBERWAR.DSK", + "joy-right": "kjoy0", + "basic": "LOADM\"CYBERWAR\":EXEC\n" + }, + "f33636788aff14e365364045db03fb7ebdeccdef": { + "filename": "Cyclops (Romik) (Coco port) (SG-24) (Coco 1-2).zip", + "disk0": "CYCLOPS.DSK", + "joy-right": "kjoy0", + "basic": "LOADM\"CYCLOPS\":EXEC\n" + }, + "dfabe6eefc3d2c929039fb25347eaa31520a8869": { + "filename": "Cyrus (Tandy).zip", + "disk0": "CYRUS.DSK", + "basic": "LOADM\"CYRUS\":EXEC\n" + }, + "37b6b8c7943fd0f1e6535fa591ca29508fe1bd5c": { + "filename": "Dancing Devil (Tom Mix Software).zip", + "disk0": "DDEVIL.DSK", + "basic": "LOADM\"DDEVIL\":EXEC\n" + }, + "19ed0464f7cfa00c56c89f3326430eb2e9f5303d": { + "filename": "Cyrus Chess (Burke & Burke) (OS-9) (Coco 3).zip", + "machine": "coco3", + "disk0": "cyrus.dsk", + "basic": "DOS\n" + }, + "25309ed15416476165801357d55305fd062ae87c": { + "filename": "Cyrus Chess (Burke & Burke) (OS-9) (Coco 3).zip", + "machine": "coco3", + "disk0": "cyrus.dsk", + "basic": "DOS\n" + }, + "752196e97b9c8a99c8c8b341cb175bfdf5ca9286": { + "filename": "Danger Ranger (Med Systems, Screenplay).zip", + "disk0": "RANGER.DSK", + "joy-right": "kjoy0", + "basic": "LOADM\"RANGER\":EXEC\n" + }, + "2434821aab336fc44124f0fca5065f7f12796138": { + "filename": "Dallas Quest, The (Tandy).zip", + "disk0": "DALLAS.DSK", + "basic": "RUN\"DALLAS\"\n" + }, + "fe513715ace8a89fe0abeecddefb9ef3dc0b299e": { + "filename": "Dark Castle (Darin Andersen).zip", + "disk0": "DKCASTLE.DSK", + "basic": "RUN\"DKCASTLE\"\n" + }, + "1cfe593b04cc28dae33d5946c420837f1a0e9544": { + "filename": "Dark Castle Game System (David Lionell Dawson).zip", + "disk0": "DARKCSTL.DSK", + "basic": "RUN\"DARKCAST\"\n" + }, + "bcac6b0219bb5a4fdcba1f8a92bf3f8728ecfe87": { + "filename": "Dark Pit, The (Microdeal Cornwall) (NTSC).zip", + "disk0": "DARKPIT.DSK", + "joy-right": "kjoy0", + "basic": "LOADM\"DARKPIT\":EXEC\n" + }, + "9d2d23dc63c59252562726891fca9314681ab5ca": { + "filename": "Dark Pit, The (Microdeal Cornwall) (PAL).zip", + "machine": "coco", + "disk0": "DARKPIT.DSK", + "joy-right": "kjoy0", + "basic": "LOADM\"DARKPIT\":EXEC\n" + }, + "1cdf128371a1ed5810a0633c5743eb2f05f9eef1": { + "filename": "Dark Star (Design Design) (NTSC).zip", + "disk0": "DARKSTAR.DSK", + "joy-right": "kjoy0", + "basic": "RUN\"DARKSTAR\"\n" + }, + "6272b308c4c4c47ebf8be11d2024a9ad44ed47c6": { + "filename": "Darkmoor Hold (Prickly-Pear Software).zip", + "disk0": "Darkmoor.dsk", + "joy-right": "kjoy0", + "basic": "LOADM\"MBOOT\"\n" + }, + "90e98e2bc2e6074dd4b93931d30c23b0f9009a0e": { + "filename": "Darkmoor Hold (Prickly-Pear Software).zip", + "disk0": "Darkmoor.dsk", + "joy-right": "kjoy0", + "basic": "LOADM\"MBOOT\"\n" + }, + "67ab24229e70bb5bda64f8ea421d78ca12defcc9": { + "filename": "Dartboard (Kenneth M. Fine).zip", + "disk0": "DARTS.DSK", + "joy-right": "kjoy0", + "basic": "RUN\"DARTS\"\n" + }, + "f4b5e1b4a16b3f60172051382042b235176ef7a3": { + "filename": "DBreak (Bryan Larsen).zip", + "disk0": "DBREAK.DSK", + "joy-right": "mjoy0", + "basic": "LOADM\"DBREAK\":EXEC\n" + }, + "fee6b877ce280f93d2c81422b336d7a500009acd": { + "filename": "Dark Star (Design Design) (PAL).zip", + "machine": "coco", + "disk0": "DARKSTAR.DSK", + "joy-right": "kjoy0", + "basic": "RUN\"DARKSTAR\"\n" + }, + "8c0391873ddb9fea9bbdacd88e73c2753507051e": { + "filename": "Data Fall (T&D Software).zip", + "disk0": "DATAFALL.DSK", + "joy-right": "mjoy0", + "basic": "LOADM\"DATAFALL\":EXEC\n" + }, + "bb17ab33917f1e0c360347c7cc7fd02ab801516d": { + "filename": "Deadly Dungeon, The (Sector Software).zip", + "disk0": "DEADLYD.DSK", + "basic": "RUN\"DEADLY D\"\n" + }, + "e34cab9e2605d74b110bce85e4de4e3630d358a3": { + "filename": "Death Trap (Soft Sector Marketing).zip", + "disk0": "DEATH.DSK", + "joy-left": "kjoy0", + "basic": "LOADM\"DEATH\":EXEC\n" + }, + "d84de681c66f3c618a08e7bc2ffa33558fd0a06e": { + "filename": "Deathchase (James McKay).zip", + "disk0": "dchs.dsk", + "joy-right": "kjoy0", + "basic": "RUN\"D\"\n" + }, + "aad711adf6c222b51f0938da669469b1358ffbf1": { + "filename": "Death Ship (Aardvark Technical Services).zip", + "disk0": "DSHIP.DSK", + "basic": "RUN\"DSHIP\"\n" + }, + "45030e8081cc321c2fc29f56b535bb937adaf21c": { + "filename": "Decathlon (Spectral Associates).zip", + "disk0": "DECATH.DSK", + "basic": "LOADM\"DECATH\":EXEC\n" + }, + "414d8d6f27ba531db92427fb06e9ffb78e2cc33d": { + "filename": "Decipher (Prickly-Pear Software).zip", + "disk0": "DECIPHER.DSK", + "basic": "LOADM\"DECIPHER\":EXEC\n", + "tv-input": "redblue" + }, + "839d7db61d4f3178b7990302fc291ff2acb7c315": { + "filename": "Defend (T&D Software).zip", + "disk0": "DEFEND.DSK", + "joy-left": "mjoy0", + "basic": "RUN\"DEFEND\"\n" + }, + "2f035ce4b5c5865019511dae22c5a5e3cfcf71ea": { + "filename": "Defender of Castle Darwin (Chris Spry) (Coco 3).zip", + "machine": "coco3", + "disk0": "darwin.dsk", + "joy-right": "mjoy0", + "basic": "RUN\"BOOT\"\n" + }, + "ce84b04430f8ee1924069dc89835c1518f41ad36": { + "filename": "Demolition Derby (Tandy).zip", + "disk0": "DERBY.DSK", + "joy-right": "kjoy0", + "basic": "LOADM\"DERBY\":EXEC\n" + }, + "846548fcfe857d61b65e6d17016b90813806ddbf": { + "filename": "Defense (Spectral Associates).zip", + "disk0": "DEFENSE.DSK", + "joy-right": "mjoy0", + "basic": "LOADM\"DEFENSE\":EXEC\n" + }, + "bf2b633888dac9d49d0467f027dd7c4a96000021": { + "filename": "Demon Attack (Tandy).zip", + "disk0": "DEMON.DSK", + "joy-left": "kjoy0", + "basic": "LOADM\"DEMON\":EXEC\n" + }, + "433370516ceaaeb8e002e087541179b6b50f286e": { + "filename": "Demon Fire (Steven Dufresne).zip", + "disk0": "DEMON.DSK", + "joy-right": "mjoy0", + "basic": "LOADM\"DEMON\":EXEC\n" + }, + "0c39769e3dc690a64bc00877b2965d4fad689319": { + "filename": "Demon Seed (Computer Shack-Michtron).zip", + "disk0": "DEMON.DSK", + "joy-right": "kjoy0", + "basic": "LOADM\"DEMON\":EXEC\n" + }, + "1df2b380292bca334169c4345b7b35b0ffdc4182": { + "filename": "Deserted Barracks, The (Raleigh Rivers).zip", + "disk0": "BARRACKS.DSK", + "joy-right": "kjoy0", + "basic": "LOADM\"BARRACKS\":EXEC\n" + }, + "ea4191022a6b1e27ce969dc5640354ceb7d30877": { + "filename": "Desert Golf (John Zivic).zip", + "disk0": "DESGOLF.DSK", + "basic": "RUN\"LOADER\"\n" + }, + "01830ecda268402d4e147af1a81cb638dea3ea97": { + "filename": "Desert Patrol (Arcade Animation Inc.).zip", + "disk0": "PATROL.DSK", + "joy-right": "kjoy0", + "basic": "LOADM\"PATROL\":EXEC\n" + }, + "15dcba1d56e85469466bcf3dce50aa85e93d34bd": { + "filename": "Devil Assault (Tom Mix Software).zip", + "disk0": "DEVIL.DSK", + "joy-right": "kjoy0", + "basic": "LOADM\"DEVIL\":EXEC\n" + }, + "da69dfe20523223978fde5356298126662391e2e": { + "filename": "Devious (Spectral Associates).zip", + "disk0": "DEVIOUS.DSK", + "joy-right": "kjoy0", + "basic": "LOADM\"DEVIOUS\":EXEC\n" + }, + "7db5bc6ffab7961eac56f25fb759dd90b8bb4f9a": { + "filename": "Destiny (Sean Stuckless) (Coco 3).zip", + "machine": "coco3", + "disk0": "WOF.DSK", + "basic": "RUN\"BOOT\"\n" + }, + "30eb571e169199946d82a896e2a184cc93bf48c9": { + "filename": "Desert Rider (Tandy).zip", + "disk0": "DESERT.DSK", + "joy-right": "kjoy0", + "basic": "RUN\"RIDER\"\n" + }, + "47da358587671bd8eaf357647b567e80b4a427c6": { + "filename": "Desert Rider (Tandy).zip", + "disk0": "DESERT.DSK", + "joy-right": "kjoy0", + "basic": "RUN\"RIDER\"\n" + }, + "420bd91b961c009a4c73d19d5191a11bdca56f15": { + "filename": "Digger (Hyper-Tech Software) (Coco 3).zip", + "machine": "coco3", + "disk0": "DIGGER.DSK", + "joy-left": "kjoy0", + "basic": "LOADM\"DIGGER\":EXEC\n" + }, + "927292a69f7f8d2e07f2c620ddb92a2723e48b95": { + "filename": "Dig (T&D Software).zip", + "disk0": "DIG.DSK", + "basic": "LOADM\"DIG\":EXEC\n" + }, + "e945785e72708115182bde394a5fd1bddebab34c": { + "filename": "Digger III Holiday Preview (Chet Simpson & Paul Thayer) (Coco 3).zip", + "machine": "coco3", + "disk0": "D3-V101a.DSK", + "joy-right": "kjoy0", + "basic": "LOADM\"D3\":EXEC\n" + }, + "5099a5e88ab6b025ec7d0adaadbba7fb95c2c285": { + "filename": "Dinowars (Tandy).zip", + "disk0": "DINOWARS.DSK", + "joy-right": "kjoy0", + "basic": "LOADM\"DINOWARS\":EXEC\n" + }, + "cb4c3f50f525d7867e006b9ed2392ca43ff2488c": { + "filename": "Dodgem (Tino Delbourgo).zip", + "disk0": "DODGEM.DSK", + "joy-right": "mjoy0", + "basic": "LOADM\"DODGEM\":EXEC\n" + }, + "582bb6adeb61bbb42033b024731788b33628b1db": { + "filename": "Domination (HAWKSoft) (Coco 3).zip", + "machine": "coco3", + "disk0": "domination.dsk", + "joy-right": "mjoy0", + "basic": "RUN\"DOMINATE\"\n" + }, + "df3329adbe95ed1f520bfd0f63490af3a7a3a4f8": { + "filename": "Dock Master (Diego Barizo).zip", + "disk0": "Dock.dsk", + "basic": "RUN\"DOCK\"\n" + }, + "5e8823d065fb0b893e82bb41a93800341a4e4e1b": { + "filename": "Digger III (HyperTech Gaming) (Coco 3).zip", + "machine": "coco3", + "disk0": "D3RGB.dsk", + "joy-right": "kjoy0", + "basic": "LOADM\"D3\":EXEC\n" + }, + "2d8456482b160ec3333c8405f4aee7262328f8bb": { + "filename": "Digger III (HyperTech Gaming) (Coco 3).zip", + "machine": "coco3", + "disk0": "D3RGB.dsk", + "joy-right": "kjoy0", + "basic": "LOADM\"D3\":EXEC\n" + }, + "231f49107a03ceca4486ecad03e00b0b4727faa7": { + "filename": "Donkey King (Tom Mix Software).zip", + "disk0": "DONKEY.DSK", + "joy-right": "kjoy0", + "basic": "LOADM\"DONKEY\":EXEC\n", + "tv-input": "redblue" + }, + "61f6dcc9192679f3a09313a34994f32131697dc4": { + "filename": "Donkey King XXX Hack (Tom Mix Software).zip", + "disk0": "KINGDONG.DSK", + "joy-right": "kjoy0", + "basic": "LOADM\"KINGDONG\":EXEC\n", + "tv-input": "redblue" + }, + "ee1584807eb8f28744e1f1c5fc1031abd80a0548": { + "filename": "Donkey Kong Remixed (John Kowalski) (Coco 3).zip", + "machine": "coco3", + "disk0": "DKRemix.dsk", + "joy-right": "kjoy0", + "basic": "LOADM\"DKREMIX\":EXEC\n" + }, + "1c0f9a0fd46fb8bcf857f1393f84f0999adb4ed8": { + "filename": "Don Pan (Tandy).zip", + "disk0": "DONPAN.DSK", + "joy-left": "kjoy0", + "basic": "LOADM\"DONPAN\":EXEC\n" + }, + "e9c1ac85150ed14ac81c854df13f6c09fa5b4748": { + "filename": "Donkey Kong Emulator (John Kowalski) (Coco 3).zip", + "machine": "coco3", + "disk0": "DK.DSK", + "joy-right": "kjoy0", + "basic": "RUN\"DONKEY\"\n" + }, + "31782d95653d5f7beeff1ec27112653f4f391de6": { + "filename": "Donut Dilemma Coco 3 Version (Game Point Software) (Coco 3).zip", + "machine": "coco3", + "disk0": "DONUT3.DSK", + "basic": "LOADM\"DONUT3\":EXEC\n" + }, + "78158411976cd576af339f64ee0d75cfb2ad39b2": { + "filename": "Donut Dilemma (Tandy, Game Point Software).zip", + "disk0": "donut.dsk", + "basic": "RUN\"DONUT\"\n" + }, + "cac22b6537f57a11a494d9a93fc5771127e6a9e5": { + "filename": "Double Back (Tandy).zip", + "disk0": "DBLBACK.DSK", + "joy-right": "kjoy0", + "basic": "LOADM\"DBLBACK\":EXEC\n" + }, + "8d228d7fd6c3d7bf8927273b271872b642a0b391": { + "filename": "Downland (Tandy).zip", + "disk0": "DOWNLAND.DSK", + "joy-right": "kjoy0", + "basic": "LOADM\"DOWNLAND\":EXEC\n" + }, + "0af5e3c8bb53faab6f0e7eb090cf12a98f9e9ba3": { + "filename": "Doodle Bug (Computerware).zip", + "disk0": "DOODLE.DSK", + "joy-right": "kjoy0", + "basic": "LOADM\"DOODLE\":EXEC\n" + }, + "65bec8822b8c250400c8b374ef259d5728d9cc09": { + "filename": "Draconian (Tom Mix Software).zip", + "disk0": "DRACO.DSK", + "joy-left": "kjoy0", + "basic": "LOADM\"DRACO\":EXEC\n" + }, + "62c294a77fd92172cebfdc5a3a4e0cc10ca02b53": { + "filename": "Dragon Castles (Charles Pelosi).zip", + "machine": "coco3", + "disk0": "dragon.dsk", + "basic": "RUN\"DRAGON\"\n" + }, + "e252a1b948f4238c94fa29bbfa4322d83ad7990d": { + "filename": "Dragon Lord (Australian Coco Magazine).zip", + "disk0": "DRAGLORD.DSK", + "basic": "RUN\"DRAGLORD\"\n" + }, + "cf6ea46f0d56a9bd331c8e671689fdeaa20f3a2b": { + "filename": "Dragon Blade (Prickly-Pear Software).zip", + "disk0": "drgnblde.dsk", + "basic": "LOADM\"BOOT\":EXEC\n" + }, + "c7339b2063a7f8fc76e90616a48abd1602631123": { + "filename": "Dragonfire (PAL 50Hz Coco 2) (Tandy).zip", + "machine": "coco", + "disk0": "DRAGFIR2.DSK", + "joy-left": "kjoy0", + "basic": "LOADM\"DRAGFIR2\":EXEC\n" + }, + "95ae06a3110f642673ec6aca9812d2d3d22cd6ff": { + "filename": "Dragons Castle, The (Steven C. Mitchell).zip", + "disk0": "DRAGONCS.DSK", + "basic": "RUN\"DRAGONCS\"\n" + }, + "fb9ddb90b185d5d7d25cd50f98940343e0dd2f76": { + "filename": "Dragon Slayer (Tom Mix Software).zip", + "disk0": "DRAGON.DSK", + "joy-right": "kjoy0", + "basic": "LOADM\"DRAGON\":EXEC\n" + }, + "8b8e42844973cb26bf718a9247eea48fc58c108e": { + "filename": "Dragonfire (Tandy).zip", + "disk0": "DRAGFIRE.DSK", + "joy-left": "kjoy0", + "basic": "LOADM\"DRAGFIRE\":EXEC\n" + }, + "5ce08508b1abe677f191e0823030b03c8114a904": { + "filename": "Dragon's Temple (Jade).zip", + "disk0": "Dragons Temple.dsk", + "joy-right": "kjoy0", + "basic": "RUN\"GO\"\n" + }, + "9650344e2837e5ea71a7d0b842b7141c32ea84fa": { + "filename": "Drag Race (Stephen Macri) (Coco 3).zip", + "machine": "coco3", + "disk0": "DRAGRACE.DSK", + "basic": "RUN\"DRAGRACE\"\n" + }, + "49916be019e94821199fb7f43cd038c5c4805686": { + "filename": "Draw Poker (Gary James) (Coco 3).zip", + "machine": "coco3", + "disk0": "cc3poker.dsk", + "basic": "RUN\"POKER\"\n" + }, + "250daeac068c41ff04454dc20d4dda6a586dd97a": { + "filename": "Draw Poker (Mike Burton) (Coco 3).zip", + "machine": "coco3", + "disk0": "DRAWPO.DSK", + "basic": "RUN\"DRAWPO\"\n" + }, + "418d0fd78fc44f59d8b9be799e27cd65c2273ac8": { + "filename": "Dragster (Brian M.G.).zip", + "disk0": "DRAGSTER.DSK", + "joy-right": "kjoy0", + "basic": "LOADM\"DRAGSTER\":EXEC\n" + }, + "c8b9465f0c2f6ef20a03b10cac9a91ace0192a5e": { + "filename": "Draw Poker (T&D Software) (Coco 3).zip", + "machine": "coco3", + "disk0": "POKER3.DSK", + "basic": "RUN\"POKER3\"\n" + }, + "90a00285187fd6e105dc81681d66fdec8867e793": { + "filename": "Draw Poker (T&D Software).zip", + "disk0": "DRAWPOKR.DSK", + "basic": "RUN\"DRAWPOKR\"\n" + }, + "ca73173eddf644c26d587dd11f0ab02ade7626ae": { + "filename": "Draw Poker (Tom Mix Software).zip", + "disk0": "POKER.DSK", + "basic": "LOADM\"POKER\":EXEC\n" + }, + "cd8c7037b1081154411e566e4f6b58dfcf9a83ba": { + "filename": "Droid War (Public Domain).zip", + "disk0": "DROIDWAR.DSK", + "joy-right": "kjoy0", + "basic": "LOADM\"DROIDWAR\":EXEC\n" + }, + "2f6a2f85878605d2d1cc30cc525933895c9d3006": { + "filename": "DropPack (T&D Software).zip", + "disk0": "DROP.DSK", + "joy-right": "kjoy0", + "basic": "LOADM\"DROP\":EXEC\n" + }, + "7077d3162bdf909bfdc1eb860981e9294ef46d21": { + "filename": "Dr. Who (Prickly-Pear Software).zip", + "machine": "coco3", + "disk0": "DRWHO.DSK", + "basic": "RUN\"RUNME\"\n" + }, + "282063112fe62b7678bab37bd2df4bb89ea9d97e": { + "filename": "Dungeon of Death (Sugar Software).zip", + "disk0": "DUNDEATH.DSK", + "basic": "RUN\"PLAYER\"\n" + }, + "eb699de6444934d60dfb5ced5a5de283a3dd7525": { + "filename": "Dungeon Quest (Computerware) (alt).zip", + "disk0": "DGNQUEST.DSK", + "joy-right": "kjoy0", + "basic": "RUN\"QUEST\"\n" + }, + "b9edbf1e9a2115fb5b64573cdbd593ed1683f81d": { + "filename": "Dungeon Quest (Prickly Pear Software).zip", + "disk0": "DUNQUEST.DSK", + "joy-right": "kjoy0", + "basic": "LOADM\"DUNQUEST\":EXEC\n" + }, + "5d33398fce394b6b0c6614ae54a58d145857a940": { + "filename": "Dungeon Raid (Microdeal UK).zip", + "disk0": "DUNGRAID.DSK", + "joy-right": "kjoy0", + "basic": "LOADM\"DUNGRAID\":EXEC\n" + }, + "1101bfac2304685ae23280c6d26015286fab6ac3": { + "filename": "Dungeons of Daggorath (Tandy).zip", + "disk0": "DAGGORAT.DSK", + "basic": "LOADM\"DAGGORAT\":EXEC\n" + }, + "a994afe76e98fdd59bf67de6b2b5bd86958158d8": { + "filename": "Dungeons of Daggorath (Shield Fix) (Aaron Oliver).zip", + "disk0": "DagShFix.dsk", + "basic": "RUN\"DAG\"\n" + }, + "aac3850bc704ee3a54178fdd5d50c02c30900aaa": { + "filename": "Dungeons of Despair, The (The Snail).zip", + "machine": "coco3", + "disk0": "DESPAIR.DSK", + "basic": "RUN\"BOOT\"\n" + }, + "063183498fcd5ca6eab1e69c2e007bea8b56c7ce": { + "filename": "Dungeons of Daggorath (Disk Patch) (Tandy).zip", + "disk0": "DAGGDISK.dsk", + "basic": "RUN\"DAGORATH\"\n" + }, + "116d40c58efb476aa8eccc3c86d3399935f73970": { + "filename": "Dungeons of Daggorath (Disk Patch) (Tandy).zip", + "disk0": "DAGGDISK.dsk", + "basic": "RUN\"DAGORATH\"\n" + }, + "dcec5ff1aa1525548d93f8462a9fd4bbf25ac783": { + "filename": "Dunjunz (2017) (Ciaran Anscomb).zip", + "disk0": "Dunjunz (2017) (Ciaran Anscomb).dsk", + "joy-right": "kjoy0", + "basic": "RUN\"DUNJUNZ\"\n", + "tv-input": "redblue" + }, + "efed7bcee9ef5d29d3ab813d3d06bbdf7339bb4c": { + "filename": "Dunjunz (2018) (Teipen Mwnci).zip", + "disk0": "Dunjunz (2018) (Teipen Mwnci).dsk", + "joy-right": "kjoy0", + "basic": "RUN\"DUNJUNZ\"\n", + "tv-input": "redblue" + }, + "d08fbbebe991f31b8b1ca5552a16c3b177ca474a": { + "filename": "Dunkey Monkey (Intellectronics).zip", + "disk0": "DUNKEYM.DSK", + "joy-right": "kjoy0", + "basic": "LOADM\"DUNKEYM\":EXEC\n" + }, + "2435173670ed46742300373906dda3503d68dd61": { + "filename": "Duo Deck Solitaire (Eversoft Games) (Coco 3).zip", + "machine": "coco3", + "disk0": "DUO.DSK", + "joy-right": "mjoy0", + "basic": "RUN\"DUO\"\n" + }, + "e052fb73d5ef6e8381974f5c0fc6f9c425b1d39f": { + "filename": "Earth's Foundations, The (Paul Ruby Jr).zip", + "disk0": "FOUNDATE.DSK", + "basic": "RUN\"FOUNDATE\"\n" + }, + "bb473ed0055282284be1bde1c7255b8460891e65": { + "filename": "Eagle Lander (Saguaro Software).zip", + "disk0": "EAGLE.DSK", + "basic": "RUN\"EG\"\n" + }, + "ea49942e4fad404db1db9ee52d457af94a7a9ae0": { + "filename": "East-World Karate (The Saint John Gallery).zip", + "disk0": "karate.dsk", + "joy-right": "kjoy0", + "basic": "RUN\"BOOT\"\n" + }, + "f37ab87699ae39dc0b9443341e08bca4062a072b": { + "filename": "Earthquake 1906 (Adventure International).zip", + "disk0": "earthquake1906.dsk", + "basic": "LOADM\"ERTHQAKE\":EXEC\n" + }, + "1dd08ae69019298d153e31746a766fe9a4e828f3": { + "filename": "Eggs (T&D Software).zip", + "disk0": "EGGS.DSK", + "joy-right": "kjoy0", + "basic": "RUN\"EGGS\"\n", + "tv-input": "redblue" + }, + "5dc61b573f6d2dae1a41cff7e48d9c63e3ebbcfb": { + "filename": "El Bandito (Mark Data Products).zip", + "disk0": "BANDITO.DSK", + "joy-right": "kjoy0", + "basic": "LOADM\"BANDITO\":EXEC\n" + }, + "aef27a73458d22d794ca5448d97d80cf3f659f12": { + "filename": "El Diablero (Computerware).zip", + "disk0": "DIABLERO.DSK", + "basic": "LOADM\"DIABLERO\":EXEC\n" + }, + "b58e32ed5b3d93bbbed705e694cf0f2db56357ba": { + "filename": "Electron (Tom Mix Software).zip", + "disk0": "ELECTRON.DSK", + "joy-right": "kjoy0", + "basic": "LOADM\"ELECTRON\":EXEC\n" + }, + "98157043faf95302f94ccbcaa21fd6ccc4ecef42": { + "filename": "Eliminator (Adventure International).zip", + "disk0": "ELIMNATE.DSK", + "joy-right": "kjoy0", + "basic": "LOADM\"ELIMNATE\":EXEC\n" + }, + "6e07905a046f05d90e92d8f54e0194d7602de66c": { + "filename": "Empire (Shards Software).zip", + "disk0": "empire.dsk", + "basic": "LOADM\"EMPIRE\":EXEC\n" + }, + "df5301a3808c3949b71d45aa473c5d2684ca16b0": { + "filename": "Eliza (Tandy).zip", + "disk0": "ELIZA.DSK", + "basic": "RUN\"ELIZA\"\n" + }, + "5b3882bfb3398df386a9050a528f90dd0e2d1fb6": { + "filename": "Enchanted Forest, The (Genesis Software).zip", + "disk0": "ENCHANT.DSK", + "basic": "RUN\"ENC-FORT\"\n" + }, + "66ce75c069a17b4c745046ae194504d1e9986435": { + "filename": "Eno (Jarb Software).zip", + "disk0": "eno.dsk", + "basic": "RUN\"ENO\"\n" + }, + "def13421a435dd83515409a0106b35656eac6363": { + "filename": "Entity, The (Mike Snyder) (Coco 3).zip", + "machine": "coco3", + "disk0": "ENTITY.DSK", + "basic": "LOADM\"ENTITY1\":EXEC\n" + }, + "c4d0604b6bd9901645f6c4072e2d3f872fe505c2": { + "filename": "Escape 2012 (Computerware).zip", + "disk0": "ESCAPE2K.DSK", + "basic": "LOADM\"ESCAPE2K\":EXEC\n" + }, + "0b11fe35a678ac923f1eeef9786bd29dfd4663a9": { + "filename": "Erland (Prickly-Pear Software).zip", + "disk0": "ERLAND.DSK", + "basic": "LOADM\"ERLAND\":EXEC\n" + }, + "88daf6c5fc69e03bbe4237826788cc786466f2cd": { + "filename": "Erazor (Tino Delbourgo).zip", + "disk0": "ERAZOR.DSK", + "basic": "LOADM\"ERAZOR\":EXEC\n" + }, + "17c1f49fb48dca88d8d7bb02985e61e179825339": { + "filename": "Escape (Colorsoft Software).zip", + "disk0": "ESCAPE.DSK", + "basic": "LOADM\"ESCAPE\":EXEC\n" + }, + "43c655fbdb17d006ebc03417397d1fb1d95e97fc": { + "filename": "Escape (Matt Hazard).zip", + "disk0": "ESCAPE.DSK", + "joy-right": "mjoy0", + "basic": "RUN\"ESCAPE\"\n" + }, + "1bd696b6a979850500ba9ae3b85b0d1cfad25c11": { + "filename": "Escape From Markessa (D&M Fighting Adventures).zip", + "disk0": "markessa.dsk", + "basic": "RUN\"MARK\"\n" + }, + "bbe64bcb6addeb7e7c8a50d49e94b86deb7af3a7": { + "filename": "Escape From Sparta (The Rainbow).zip", + "disk0": "SPARTA.DSK", + "basic": "RUN\"ESSPARTA\"\n" + }, + "f47a16fe26f334c46c8c7e52694917d31efcf72c": { + "filename": "Escape From SPECTRE (Tom Mix Software).zip", + "disk0": "SPECTRE.DSK", + "joy-right": "kjoy0", + "basic": "RUN\"SPECTRE\"\n" + }, + "d05418b0cf634cc0f74885c93c103a6fa3731482": { + "filename": "Escape From Tut's Tomb (The Rainbow).zip", + "disk0": "ESCTUT.DSK", + "joy-right": "kjoy0", + "basic": "LOADM\"TUT1\":EXEC\n" + }, + "1d025714fe585e35c88931df3e36e3a8e87967a8": { + "filename": "Escape (Microdeal Cornwall).zip", + "disk0": "ESCAPE.DSK", + "basic": "LOADM\"ESCAPE\":EXEC\n" + }, + "2b0dc25b2cbde7b84feb5c55c94265769fb4c186": { + "filename": "Escape (T&D Software).zip", + "disk0": "ESCAPE.DSK", + "basic": "LOADM\"ESCAPE\":EXEC\n" + }, + "8a1601e7ce297a7cf891a563afbb29a948ea9f98": { + "filename": "Espionage Island (Owls Nest Software).zip", + "disk0": "ESPIONI.DSK", + "basic": "RUN\"ISLE\"\n" + }, + "c6b9e23c33d7f824a77ea819ed74cbbcdf738b1b": { + "filename": "Euchre (Chroma-Systems Group).zip", + "disk0": "euchre.dsk", + "basic": "RUN\"EUCHRE\"\n", + "tv-input": "redblue" + }, + "bf3c1e6683a645ce63a799019c0ef80cd4ddb024": { + "filename": "Euchre (Mike Pounds) (Coco 3).zip", + "machine": "coco3", + "disk0": "EUCH.DSK", + "basic": "RUN\"EUCH\"\n" + }, + "df992c56fda7e78eb2af7dd0193c3b15b7276d57": { + "filename": "Everest (Derek Sullivan).zip", + "disk0": "everest.dsk", + "basic": "RUN\"EVEREST\"\n" + }, + "687fe2c789b7446653e5de9f23de84433b1d1afd": { + "filename": "Evil Crypt (Curtis Keisler).zip", + "disk0": "EVILCRPT.DSK", + "basic": "RUN\"EVILCRPT\"\n" + }, + "f1ae2c7957e210b944f49a9a3f58c4962f503759": { + "filename": "Eversoft Games Product Catalog (Eversoft Games) (Coco 3).zip", + "machine": "coco3", + "disk0": "EVERSOFT.DSK", + "basic": "RUN\"MENU\"\n" + }, + "b43fc747848812d78cb617d6928b30af91056c1d": { + "filename": "Eye Spy (David Crandall).zip", + "disk0": "EYESPY.DSK", + "joy-right": "kjoy0", + "basic": "LOADM\"EYESPY\":EXEC\n" + }, + "148ab9dc6015c5b0de9a0803c69d9987b32cf26e": { + "filename": "Exeter (Tandy) (Coco 3).zip", + "machine": "coco3", + "disk0": "EXETER.DSK", + "basic": "RUN\"EXETER\"\n" + }, + "d7cfb45fc111ccb2102dfdc8497f2b75fdeaab83": { + "filename": "EZ Ski (Chromassette).zip", + "disk0": "EZSKI.DSK", + "joy-right": "kjoy0", + "basic": "LOADM\"EZSKI\":EXEC\n" + }, + "88e593b2b1fac3bfdc51d3a400faaa488683505f": { + "filename": "F-15 Ground Assault Simulator (The Rainbow) (Coco 3).zip", + "machine": "coco3", + "disk0": "F15EAGLE.DSK", + "joy-right": "kjoy0", + "basic": "RUN\"F15EAGLE\"\n" + }, + "75ae66ee472c9f7400d14d85ca91197da5adbf5f": { + "filename": "F-16 High Performance Instrument Flight Simulator (Ken Elder-Randy Wright).zip", + "disk0": "F16SIM.DSK", + "joy-left": "kjoy0", + "basic": "RUN\"F16\"\n" + }, + "0ded26233a5af85ad2bcbad130aeadab8e10d434": { + "filename": "Fahrfall (Alpha-3) (John W. Linville).zip", + "disk0": "fahrfall.dsk", + "joy-left": "kjoy0", + "basic": "RUN\"FAHRFALL\"\n" + }, + "82fea4df992a508a0edea2dba024f97453aa88ca": { + "filename": "Fahrfall (Beta 3) (John W. Linville).zip", + "disk0": "fahrfall.dsk", + "joy-left": "kjoy0", + "basic": "RUN\"FAHRFALL\"\n" + }, + "8065110d2158d84e7838def23f41c8def5c8e2d5": { + "filename": "F-16 Assault (Diecom Products) (SSC).zip", + "disk0": "F16.DSK", + "joy-right": "kjoy0", + "basic": "LOADM\"F-16\":EXEC\n", + "tv-input": "redblue" + }, + "a53e6ad8689ad71fcac957d3fba14460d2772d1e": { + "filename": "F-16 Assault (Diecom Products) (SSC).zip", + "disk0": "F16.DSK", + "joy-right": "kjoy0", + "basic": "LOADM\"F-16\":EXEC\n", + "tv-input": "redblue" + }, + "4704dc9efe79c3b12a66c7ed03376aaea65ed1b7": { + "filename": "Fangman (Tom Mix Software).zip", + "disk0": "FANGMAN.DSK", + "joy-right": "kjoy0", + "basic": "LOADM\"FANGMAN\":EXEC\n" + }, + "8dd2fa48f78840c2269ce1b3155f197e5f49c6a9": { + "filename": "Fantasy Gamer's 32K Package (Prickly-Pear Software).zip", + "disk0": "FANTASY.DSK", + "basic": "RUN\"MENU\"\n" + }, + "ee5a679fe3905635b6403d37f28781e6d0d5603e": { + "filename": "Fearless Freddy (Pocket Money Software).zip", + "disk0": "fearless.dsk", + "joy-right": "kjoy0", + "basic": "LOADM\"FEARLESS\":EXEC\n" + }, + "aa741beda912617fd2772b1d1ab942a149dc66bc": { + "filename": "Feuer And Gasse (Picosoft).zip", + "disk0": "FIRE&GAS.DSK", + "basic": "PCLEAR1:RUN\"FIRE&GAS\"\n" + }, + "ee7323229ebf64033dd33c26451a4ded91e4d7bb": { + "filename": "Fembot's Revenge (Nelson Software Systems).zip", + "disk0": "FEMBOTS.DSK", + "basic": "LOADM\"FEMBOTS\":EXEC\n" + }, + "f5bd2e5b1a0a5dcff3525dd93b995c0234d9f6fc": { + "filename": "Fighter Pilot (Saguaro Software).zip", + "disk0": "FIGHTER.DSK", + "joy-left": "kjoy0", + "basic": "LOADM\"FIGHTER\":EXEC\n" + }, + "5bd54e63ec7f61471c0658f5068147dbfc1970e8": { + "filename": "Final Countdown, The (Bill Cook).zip", + "disk0": "FINALCNT.DSK", + "basic": "RUN\"FINALCNT\"\n" + }, + "ee63ea5665415e6be8d0574478b77a2ad09cdfa1": { + "filename": "Final Frontier, The (Ark Royal Software).zip", + "disk0": "FINALFRN.DSK", + "basic": "RUN\"FRONTIER\"\n" + }, + "1abc3ad0a519ef1115ad8bb98dc1943617e73c6e": { + "filename": "Fireball (M.C. Ironmonger).zip", + "disk0": "fireball.dsk", + "basic": "RUN\"FIREBALL\"\n" + }, + "7e45ceecf7a94d7cea82d31a3a50f07360c435e0": { + "filename": "Firefox (Max Bettridge).zip", + "disk0": "FIREFOX.DSK", + "joy-right": "kjoy0", + "basic": "RUN\"FIREFOX\"\n" + }, + "17b1c67ff5749cefc473dacb152951706cf8e80d": { + "filename": "Fire Force (Quickbeam Software) (Coco port).zip", + "disk0": "FFORCE.DSK", + "joy-right": "kjoy0", + "basic": "LOADM\"FFORCE\":EXEC\n" + }, + "45cfcb40040f07ce9b2a6c48cca8318d79906f72": { + "filename": "Fire Copter (Adventure International).zip", + "disk0": "FIRECOPT.DSK", + "joy-right": "kjoy0", + "basic": "LOADM\"FIRECOPT\":EXEC\n" + }, + "2d73a8f8c472b91735cbcb2aba735b96373866da": { + "filename": "Fire Runner (T&D Software).zip", + "disk0": "FIRERUNR.DSK", + "joy-right": "kjoy0", + "basic": "LOADM\"FIRERUNR\":EXEC\n", + "tv-input": "redblue" + }, + "fec075e119dc5dddfe94b8c4e5b47d2d77967e95": { + "filename": "Flagon Bird (Bosco).zip", + "disk0": "FLAGON.DSK", + "joy-right": "kjoy0", + "basic": "LOADM\"FLAGON\":EXEC\n" + }, + "f059a5f0ef1584729184d55dc8bec5efa2a69428": { + "filename": "Fire One (Ark Royal Games) (Coco 3).zip", + "machine": "coco3", + "disk0": "FIREONE.DSK", + "basic": "RUN\"GO\"\n" + }, + "f37726ab3bdfd7f6f5b8b9bd93979cfaefd9c416": { + "filename": "Flight 16 (Tom Mix Software).zip", + "disk0": "flt16.dsk", + "joy-left": "mjoy0", + "basic": "LOADM\"RICH-VA\":LOADM\"FLT16\":EXEC\n" + }, + "e29fcb05c811260a51be8235d2766fd0853999c4": { + "filename": "Flags (Rick's Computer Enterprise) (Coco 3).zip", + "machine": "coco3", + "disk0": "CC3FLAGS.DSK", + "joy-right": "kjoy0", + "basic": "RUN\"M\"\n" + }, + "2394ef33ee3237e99fc7294c62a22ced9706cdfb": { + "filename": "Flight Control (Skipp McKenzie).zip", + "disk0": "FLIGHT.DSK", + "basic": "RUN\"FLIGHT\"\n" + }, + "d1b7a4d05c070af3973c55d6f9f236921917e520": { + "filename": "Flight Sim I (Tandy).zip", + "disk0": "FLTSIM.DSK", + "basic": "LOADM\"FLTSIM\":EXEC\n" + }, + "7b3c3cfb31767983e21d6fc72311e8d89cd2f918": { + "filename": "Flight (Prickly-Pear Software).zip", + "disk0": "Flight Path.dsk", + "joy-right": "kjoy0", + "basic": "RUN\"FLIGHT\"\n" + }, + "3f9d69c7349e9fff04d0bbc73a83740e94af607c": { + "filename": "Flight (Prickly-Pear Software).zip", + "disk0": "Flight Path.dsk", + "joy-right": "kjoy0", + "basic": "RUN\"FLIGHT\"\n" + }, + "4a8dfcff0c94efc2151693a3c69ef869d76426fe": { + "filename": "Flight Simulator (Quark Data).zip", + "disk0": "FLIGHT.DSK", + "basic": "RUN\"FLIGHT\"\n" + }, + "d8e4fa6133b2a032003077ca701ec236e83d55f0": { + "filename": "Flipper (Anteco Software).zip", + "disk0": "FLIPPER.DSK", + "basic": "RUN\"FLIPPER\"\n" + }, + "cccb2c86740fc0e24c753d233e0cc1e3c807505e": { + "filename": "Flight Simulator (T&D Software).zip", + "disk0": "flghtsim.dsk", + "basic": "RUN\"FLGHTSIM\"\n" + }, + "81245e1c69d38dda9bcd072025f233317d184964": { + "filename": "Flippy (T&D Software).zip", + "disk0": "FLIPPY.DSK", + "joy-right": "mjoy0", + "basic": "LOADM\"FLIPPY\":EXEC\n", + "tv-input": "redblue" + }, + "bc10580ee093386b5142f046e01263f78e968338": { + "filename": "Fly By (Chromasette).zip", + "disk0": "FLYBY.DSK", + "basic": "LOADM\"FLYBY\":EXEC\n" + }, + "e9a4347d04fefbbea7979c7936b519bd2f61c103": { + "filename": "Flood It! (Evan C. Wright).zip", + "disk0": "FLOODIT.DSK", + "joy-right": "kjoy0", + "basic": "LOADM\"FLOODIT\":EXEC\n" + }, + "7f7cedc72c75ffdf2d16d94aaa4af59eca1f93e5": { + "filename": "Fly (Color Computer Magazine).zip", + "disk0": "FLY.DSK", + "basic": "LOADM\"FLY\":EXEC\n" + }, + "2ddf97faf6158af96a134b344498969651e92caf": { + "filename": "Foodwar (Arcade Animation Inc.).zip", + "disk0": "FOODWAR.DSK", + "joy-right": "kjoy0", + "basic": "LOADM\"FOODWAR\":EXEC\n" + }, + "f388fe89088a461a2622eab68bc7774d153769bc": { + "filename": "Flying Tigers (Sugar Software).zip", + "disk0": "TIGERS.DSK", + "joy-right": "kjoy0", + "basic": "LOADM\"TIGERS\":EXEC\n" + }, + "b5c26cbc6b58b6d4334e0104386b8e2cec01e8a5": { + "filename": "Football (Tandy).zip", + "disk0": "FOOTBALL.DSK", + "joy-left": "kjoy0", + "joy-right": "mjoy0", + "basic": "LOADM\"FOOTBALL\":EXEC\n" + }, + "44ad0b0d9cb86876cff23532aa640c98215b80d3": { + "filename": "Footrace (T&D Software).zip", + "disk0": "FOOTRACE.DSK", + "joy-left": "mjoy0", + "joy-right": "kjoy0", + "basic": "LOADM\"FOOTRACE\":EXEC\n", + "tv-input": "redblue" + }, + "a8fb19aa69f0e981d78856d8e1203c5a30eb8e4b": { + "filename": "Force, The ( Anteco Software).zip", + "disk0": "FORCE.DSK", + "joy-right": "kjoy0", + "basic": "LOADM\"FORCE\":EXEC\n" + }, + "881debac08d3fa14398cd3e0589e3d0fcd28c368": { + "filename": "Force Field (T&D Software).zip", + "disk0": "FORCFELD.DSK", + "basic": "LOADM\"FORCFELD\":EXEC\n" + }, + "3f7973a43d1361da599593a9dd0e030d5920a04d": { + "filename": "Forest Adventure (Public Domain).zip", + "disk0": "FOREST.DSK", + "basic": "LOADM\"FOREST\":EXEC\n" + }, + "801fcd6e3f7733975b9925fcc762c929006100ed": { + "filename": "Fortress of Mutant Waffles (T&D Software).zip", + "disk0": "WAFFLES.DSK", + "basic": "LOADM\"WAFFLES\":EXEC\n" + }, + "fe490e3138c3519c248505173360db45b2d55085": { + "filename": "Forgotten Pyramid, The (Chris Schneider).zip", + "disk0": "pyramid.dsk", + "basic": "DOS\n" + }, + "1ccaa66e82473beca21b0317953470d7a06963c5": { + "filename": "Four Cube (Novasoft).zip", + "disk0": "FOURCUBE.DSK", + "basic": "LOADM\"FOURCUBE\":EXEC\n" + }, + "e2c8a3deb65a450e230cf4c64b92322a1bd7a235": { + "filename": "Flight Simulator II (subLOGIC) (OS-9) (Coco 3).zip", + "machine": "coco3", + "disk0": "Flight Simulator II with scenery files.dsk", + "basic": "DOS\n" + }, + "fae519cef873200b11b88061178efb840a5bb267": { + "filename": "Flight Simulator II (subLOGIC) (OS-9) (Coco 3).zip", + "machine": "coco3", + "disk0": "Flight Simulator II with scenery files.dsk", + "basic": "DOS\n" + }, + "9a34e7722e857e7c81eff5b3958fc3edb13cf346": { + "filename": "Flight Simulator II (subLOGIC) (OS-9) (Coco 3).zip", + "machine": "coco3", + "disk0": "Flight Simulator II with scenery files.dsk", + "basic": "DOS\n" + }, + "006c9e74c3eb521a3526282ac79b9c6a6cd0195d": { + "filename": "Flight Simulator II (subLOGIC) (OS-9) (Coco 3).zip", + "machine": "coco3", + "disk0": "Flight Simulator II with scenery files.dsk", + "basic": "DOS\n" + }, + "0b5951f300bc49e28cf0167b8e127bc9ce73f67c": { + "filename": "Flight Simulator II (subLOGIC) (OS-9) (Coco 3).zip", + "machine": "coco3", + "disk0": "Flight Simulator II with scenery files.dsk", + "basic": "DOS\n" + }, + "dd7f7132ea631c2f5a56892e3b3c250f5643034b": { + "filename": "Frogday Afternoon (K-Soft) (Coco 3).zip", + "machine": "coco3", + "disk0": "FROGDAY.DSK", + "joy-right": "mjoy0", + "basic": "LOADM\"FROGDAY\":EXEC\n" + }, + "6047a8d708f7db7ee0d5f742f56348e8a7504745": { + "filename": "Franchise! (Computerware).zip", + "disk0": "FRANK.DSK", + "joy-right": "mjoy0", + "basic": "RUN\"FRANK\"\n", + "tv-input": "redblue" + }, + "e8adae992158cc1cfbb6328d2c963bb33e7d863b": { + "filename": "Frogger (The Cornsoft Group).zip", + "disk0": "FROGGER.DSK", + "basic": "LOADM\"FROGGER\":EXEC\n" + }, + "4142cae4f087c56a7d7e413e036ffdc3d59021ac": { + "filename": "Froggie (Spectral Associates) (SSC).zip", + "disk0": "FROGGIE.DSK", + "joy-right": "kjoy0", + "basic": "LOADM\"FROGGIE\":EXEC\n" + }, + "ec419d80ace180fc95e851e0cff10fe6a37015f7": { + "filename": "Frog Master (Darren Ottery).zip", + "machine": "coco3", + "disk0": "frogmast.dsk", + "joy-right": "kjoy0", + "basic": "RUN\"FROGMAST\"\n" + }, + "b59b74a1cad0938d20cc92722d36f7fd6dfbd52b": { + "filename": "Frog, The (Tom Mix Software).zip", + "disk0": "THEFROG.DSK", + "basic": "LOADM\"THEFROG\":EXEC\n" + }, + "5920c219bf79d4c9be5fd838548d64b1c235b500": { + "filename": "Frog Trek (Oelrich Publications).zip", + "disk0": "FROGTREK.DSK", + "basic": "LOADM\"FROGTREK\":EXEC\n" + }, + "3b352412be3687570209da2bfd5f07e060862cd5": { + "filename": "Fruit Multi-Bars Slot Machine (Tom Mix Software) (Coco 3).zip", + "machine": "coco3", + "disk0": "FRUITBAR.DSK", + "basic": "LOADM\"FRUITBAR\":EXEC\n" + }, + "de366deb5c968f7ad9056e2a19bc5b091a8d427f": { + "filename": "Fury (Computer Shack).zip", + "disk0": "FURY.DSK", + "joy-right": "kjoy0", + "basic": "LOADM\"FURY\":EXEC\n" + }, + "d5c72ea012f770384ea5fc258e746e73b890543b": { + "filename": "Furious Felines (Diego Barizo) (Coco 3).zip", + "machine": "coco3", + "disk0": "Furious Felines.dsk", + "basic": "RUN\"FELINES\"\n" + }, + "ff88992aee3fc4ff030430e66c7f66a2e132abeb": { + "filename": "Fyr-Draca (ColorQuest) (SG-24) (Coco 1-2).zip", + "disk0": "FYR-DRAC.DSK", + "basic": "LOADM\"FYR-DRAC\":EXEC\n" + }, + "9f339132ae0edb3197f7328bcb9b2a58270dc744": { + "filename": "Galacticans (Softek).zip", + "disk0": "GALACT.DSK", + "joy-left": "kjoy0", + "basic": "LOADM\"GALACT\":EXEC\n" + }, + "8660ae32881bebe197f4b70ef064e8d941ce78c3": { + "filename": "Galactic Attack (Tandy).zip", + "disk0": "GALACTIC.DSK", + "joy-left": "kjoy0", + "basic": "LOADM\"GALACTIC\":EXEC\n" + }, + "7ad0ffc1c37c26f4101ce71451ea9d1dae156a2c": { + "filename": "Galactic Conflict (The Rainbow).zip", + "disk0": "conflict.dsk", + "basic": "PCLEAR1:RUN\"GALACTIC\"\n" + }, + "f359e0711b04e6c61f01afc4ca50ba18e5cba902": { + "filename": "Galactic Trek (Color Computer News).zip", + "disk0": "GALTREK.DSK", + "joy-right": "kjoy0", + "basic": "RUN\"GALTREK\"\n" + }, + "584eb006a79725170e259a5e6aad0381f202d4ee": { + "filename": "Galactic Conquest (James West).zip", + "disk0": "CONQUEST.DSK", + "basic": "RUN\"CONQ\"\n" + }, + "678b3560195dea9e23cbee65c956e18af18ae1e6": { + "filename": "Galactic Fighter (Four Star Software).zip", + "disk0": "GFIGHTER.DSK", + "joy-right": "kjoy0", + "basic": "LOADM\"GFIGHTER\":EXEC\n", + "tv-input": "redblue" + }, + "aeef37e6f6c50d140e18294e233b88f42b193dc9": { + "filename": "Galactic Taipan (Ark Royal Games).zip", + "disk0": "GALTAIPN.DSK", + "basic": "RUN\"TAIPAN\"\n" + }, + "e8f4a5133da1278d7e66f899fc218977bc882aef": { + "filename": "Galactic Hangman (Sugar Software).zip", + "disk0": "GHANGMAN.DSK", + "basic": "RUN\"GHANGMAN\"\n" + }, + "20e22547a11ca7bb860a990e5d8202f2c2c8dcbb": { + "filename": "Galactic Revolution (James West).zip", + "disk0": "REVOLUT.DSK", + "basic": "RUN\"REVOLT\"\n" + }, + "807be18ebd37cce6f33456a7196df1bf572eb7dc": { + "filename": "Galax Attax Enhanced Graphics Edition (Spectral Associates, L. Curtis Boyle).zip", + "disk0": "GALAX3.DSK", + "joy-right": "kjoy0", + "basic": "LOADM\"GALAX3\":EXEC\n" + }, + "5360605493cff6f02c068ccb71c72f1ea123326c": { + "filename": "Galagon (Spectral Associates) (SSC).zip", + "disk0": "GALAGONV.DSK", + "joy-right": "kjoy0", + "basic": "LOADM\"GALAGONV\":EXEC\n" + }, + "c46e08c02c813d6a5743cd077fd36e8d882498f6": { + "filename": "Galagon (Spectral Associates).zip", + "disk0": "GALAGON.DSK", + "joy-right": "kjoy0", + "basic": "LOADM\"GALAGON\":EXEC\n" + }, + "8c8180798bad5457d0dca12a2f6a14c671e8525d": { + "filename": "Galax Attax (Spectral Associates).zip", + "disk0": "GALAX.DSK", + "joy-right": "kjoy0", + "basic": "LOADM\"GALAX\":EXEC\n" + }, + "c82e9f5206ecb36801335c5a98879c9c6cac1a42": { + "filename": "Game of Life (Jim Gerrie).zip", + "machine": "coco3", + "disk0": "life.dsk", + "basic": "RUN\"LIFE\"\n" + }, + "2235c54c22f92317491ac0d64da7c9a3916922f2": { + "filename": "Gang Buster (Prickly-Pear Software).zip", + "disk0": "GANGBUST.DSK", + "basic": "RUN\"GANGBUST\"\n" + }, + "20db8846370136647758588c817d6b8e25668caa": { + "filename": "Game of Life (Spectral Associates).zip", + "disk0": "GAMELIFE.DSK", + "basic": "LOADM\"GAMELIFE\":EXEC\n" + }, + "59a454029a4dc283e1a83df441e4f1a18229f589": { + "filename": "Game Point Software Demo Disk #1 (Game Point Software) (Coco 3).zip", + "machine": "coco3", + "disk0": "GPSDEMO.DSK", + "basic": "RUN\"DEMO\"\n" + }, + "db4dc63e15535f2b72b4ac1108b5b2b40fe5919a": { + "filename": "Gantelet (Diecom Products) (SSC).zip", + "disk0": "GANTLET.DSK", + "basic": "LOADM\"GANTLET\":EXEC\n", + "tv-input": "redblue" + }, + "df3f9f392ecf104fde0ad81179c5812aee5190ca": { + "filename": "Gauntlet (Britt Monk, later Avalon Hill).zip", + "disk0": "GAUNTLET.DSK", + "joy-right": "mjoy0", + "basic": "RUN\"GAUNTLET\"\n" + }, + "bd18924aaccec82c736f739a3907a0f379e6f91e": { + "filename": "Gate Crasher Demo (Nick Marentes) (Coco 3).zip", + "machine": "coco3", + "disk0": "GATEDEMO.DSK", + "basic": "RUN\"DEMO\"\n" + }, + "da7eda8d7b0faa767054d324aaffe4bcc2b384d9": { + "filename": "Gems 2 Demo (John Strong) (Coco 3).zip", + "machine": "coco3", + "disk0": "GEM2DEMO.DSK", + "joy-right": "mjoy0", + "basic": "RUN\"GEMS2\"\n" + }, + "e3b720cff847ed13322f60e67ffde8c457f061a8": { + "filename": "Gazon (K&K Computerware).zip", + "disk0": "GAZON.DSK", + "joy-right": "kjoy0", + "basic": "LOADM\"GAZON\":EXEC\n" + }, + "4c5e5581a6aba3cc1b0caedd2526ac3dee902983": { + "filename": "Gem Search (T&D Software).zip", + "disk0": "gem.dsk", + "basic": "RUN\"GEMLOAD\"\n" + }, + "6bc76f3fdc2278ee6aef8007ca02a2a7120ad5b6": { + "filename": "Germ Warfare (Chromasette).zip", + "disk0": "GERM.DSK", + "joy-right": "mjoy0", + "basic": "LOADM\"GERM\":EXEC\n" + }, + "9d7ef0f6fa32f6e8ad2e6b05d7e29b4fe3cbc310": { + "filename": "Get Me Outta Here! (CoCo Brothers Software) (Coco 3).zip", + "machine": "coco3", + "disk0": "ESCAPE.dsk", + "joy-right": "mjoy0", + "basic": "RUN\"BOOT\"\n" + }, + "22ad70762002598c0cff590d5e7d8a041826d084": { + "filename": "Geo Quiz (Spectral Associates).zip", + "disk0": "GEOQUIZ.dsk", + "basic": "RUN\"ASIA\"\n" + }, + "6df22ea2d508f74bcc840b082e8a7daecdbc5656": { + "filename": "Ghost Busters.zip", + "disk0": "GHOSTBTS.DSK", + "basic": "RUN\"GHOSTBTS\":EXEC\n" + }, + "ab6d607f825f538dcdc3910ca507fb3fc65ffb94": { + "filename": "GFL Championship Football II (Tandy) (Coco 3).zip", + "machine": "coco3", + "disk0": "FOOTBAL2.DSK", + "joy-right": "kjoy0", + "basic": "RUN\"FOOTBAL2\"\n" + }, + "d51419c83914864df8d4e2acd743b11cdbe664d8": { + "filename": "GFL Championship Football II (Tandy) (Coco 3).zip", + "machine": "coco3", + "disk0": "FOOTBAL2.DSK", + "joy-right": "kjoy0", + "basic": "RUN\"FOOTBAL2\"\n" + }, + "0b72e04d94b2a66ff59cfd0bef613e3b52a281d9": { + "filename": "Ghost Gobbler (Spectral Associates).zip", + "disk0": "GHOSTGOB.DSK", + "joy-right": "kjoy0", + "basic": "LOADM\"GGOBLR15\":EXEC\n" + }, + "c57a4a5124c8f1e8ed7fa8df054b1500c3d72fe5": { + "filename": "Ghana Bwana (Tandy) (SSC).zip", + "disk0": "Ghana Bwana.dsk", + "joy-right": "kjoy0", + "basic": "DOS\n", + "tv-input": "redblue" + }, + "1e59a178495cb09d5f2e8e35764e1390486d3db8": { + "filename": "Ghana Bwana (Tandy) (SSC).zip", + "disk0": "Ghana Bwana.dsk", + "joy-right": "kjoy0", + "basic": "DOS\n", + "tv-input": "redblue" + }, + "255fbc96fac09f9b02aa93d737a0f3682280cffa": { + "filename": "Ghana Bwana (Tandy) (SSC).zip", + "disk0": "Ghana Bwana.dsk", + "joy-right": "kjoy0", + "basic": "DOS\n", + "tv-input": "redblue" + }, + "7891887d712233369f47876d30525579ba275c35": { + "filename": "Ghost Rush Halloween Edition (Paul Shoemaker).zip", + "disk0": "GHOSTRHW.DSK", + "joy-left": "kjoy0", + "basic": "LOADM\"GRHW\":EXEC\n", + "tv-input": "redblue" + }, + "8eed4a1f242f116fa08223ff44d3c9b2a5e55ce2": { + "filename": "Ghost Rush (Paul Shoemaker).zip", + "disk0": "GHSTRUSH.DSK", + "joy-left": "kjoy0", + "basic": "LOADM\"GHSTRUSH\":EXEC\n" + }, + "db65327e12e1e5f889fa7e177299f37253303f8b": { + "filename": "Ghost Rush Halloween Edition (Paul Shoemaker) (Coco 3).zip", + "machine": "coco3", + "disk0": "GR16HW.DSK", + "joy-left": "kjoy0", + "basic": "LOADM\"GR16HW\":EXEC\n" + }, + "66f129853a788ae2f99728868e03f0e3296aed0f": { + "filename": "Ghost Rush Winter Edition 2022 (Paul Shoemaker).zip", + "disk0": "GRWIN.DSK", + "joy-left": "kjoy0", + "basic": "LOADM\"GRWIN\":EXEC\n" + }, + "f5bd62374d65825447f54d3d44f6291614d83000": { + "filename": "Ghost Saga (CoCo Power Software).zip", + "disk0": "g-saga.dsk", + "joy-right": "kjoy0", + "basic": "RUN\"G*SAGA\"\n" + }, + "27828cef43c7d70ed99f75d43e961d9d06d0c1bc": { + "filename": "Ghost Rush v2.0 (Paul Shoemaker).zip", + "disk0": "GHSTRUSH.DSK", + "joy-left": "kjoy0", + "basic": "LOADM\"GHSTRUSH\"\n" + }, + "3ff72027812becc8eb66cf875b2d95047c5610fb": { + "filename": "Ghost Rush v2.0 (Paul Shoemaker) (Coco 3).zip", + "machine": "coco3", + "disk0": "GHSTR16.DSK", + "joy-left": "kjoy0", + "basic": "LOADM\"GR16\":EXEC\n" + }, + "dca7c52f0b4bef21e6d61350dea6d93c6465a6ef": { + "filename": "Ghost Town (Peter Barella).zip", + "disk0": "ghosts.dsk", + "joy-right": "mjoy0", + "basic": "RUN\"GHOSTS\"\n" + }, + "39f3747b642e655bd0606dd553c33c9d2c5880b4": { + "filename": "Giligan's Island (Coco 3).zip", + "machine": "coco3", + "disk0": "giligan.dsk", + "joy-right": "kjoy0", + "basic": "RUN\"START\"\n" + }, + "f4aaa28e3ed0d6e16ba6f3fe2ced8ec5e27670b3": { + "filename": "Glaxxons (Mark Data Products).zip", + "disk0": "GLAXXONS.DSK", + "joy-right": "kjoy0", + "basic": "LOADM\"GLAXXONS\":EXEC\n" + }, + "0172309080ccb780fe4050dcc8630b4651080de7": { + "filename": "Gin Champion (Tandy).zip", + "disk0": "GINCHAMP.DSK", + "basic": "LOADM\"GINCHAMP\":EXEC\n" + }, + "410a7192e523549c845086bd7fea566038d2a1d8": { + "filename": "Gliblibs (T&D Software).zip", + "disk0": "GLIBLIBS.DSK", + "basic": "RUN\"GLIBLIBS\"\n" + }, + "b8877ce15582e8f41bc7d95f8e05b26912bc6a02": { + "filename": "Global Thermonuclear War (T&D Software).zip", + "disk0": "NUCLRWAR.DSK", + "basic": "RUN\"NUCLRWAR\"\n" + }, + "0a736222a94c7f91e7ddf7443f3e1c69136957f8": { + "filename": "Glove (James McKay).zip", + "disk0": "glove.dsk", + "joy-right": "kjoy0", + "basic": "RUN\"G\"\n" + }, + "fe985b17f609ff438aa464af0443f17f1b4be869": { + "filename": "Gobbler (David Crandall).zip", + "disk0": "GOBBLER.DSK", + "joy-right": "kjoy0", + "basic": "LOADM\"GOBBLER\":EXEC\n" + }, + "8927070f512a61a697c5779021c704ac1e04931d": { + "filename": "Go Fish (Tandy).zip", + "disk0": "GOFISH.DSK", + "basic": "RUN\"GOFISH\"\n" + }, + "702074303e616edf063535a52b5417c6068fd808": { + "filename": "Gold Child (Luis Ricardo Blando).zip", + "machine": "coco3", + "disk0": "The-Gold-Child.dsk", + "joy-right": "kjoy0", + "basic": "RUN\"DRIVER\"\n" + }, + "fac8b83e7d7e6e012a09fd1c34ea0cdea5a7475c": { + "filename": "Gold Finder (Tom Mix Software).zip", + "disk0": "GOLDFIND.DSK", + "joy-right": "kjoy0", + "basic": "LOADM\"GOLD\":EXEC\n", + "tv-input": "redblue" + }, + "d4496b08e1f4bbecb35ebac8758b03601eb5516b": { + "filename": "Gold Runner II (Diecom Products) (SSC).zip", + "disk0": "GOLDRUN2.DSK", + "joy-right": "kjoy0", + "basic": "LOADM\"GOLDRUN2\":EXEC\n", + "tv-input": "redblue" + }, + "aac617b8db1bf7e6516420b1ea975592351a3827": { + "filename": "Gold Runner (Tom Mix Software) (SSC).zip", + "disk0": "GOLDRUN.DSK", + "joy-left": "kjoy0", + "basic": "LOADM\"GOLDRUN\":EXEC\n", + "tv-input": "redblue" + }, + "298d2204a6c32f616658eb52722f120653402ad6": { + "filename": "Gomoku and Renju (Tandy) (SG-12 intro).zip", + "disk0": "GOMOKU.DSK", + "basic": "LOADM\"GOMOKU\":EXEC\n" + }, + "7cc10e6a980a040a9fd02dafc74e1c9502f28b04": { + "filename": "Golf (Aardvark-80).zip", + "disk0": "GOLFAARD.DSK", + "basic": "RUN\"GOLFAARD\"\n" + }, + "2fe38a0f5cde882c49a0cb1b1b0ae644e22db210": { + "filename": "Goop Rush (Paul Shoemaker).zip", + "machine": "coco3", + "disk0": "GOOPRUSH.DSK", + "joy-right": "kjoy0", + "basic": "LOADM\"GOOPRUSH\":EXEC\n" + }, + "60c5be4ebda61881487858b20eeab1e02c8259ae": { + "filename": "Gost Ship (Philip Courie).zip", + "disk0": "GOSTSHIP.DSK", + "basic": "RUN\"GOSTSHIP\"\n" + }, + "a9955301b4a4ce90caa070cb9a7b320b2e063f1e": { + "filename": "Gorf-Land (Graftographics) (Coco 3).zip", + "machine": "coco3", + "disk0": "GORFLAND.DSK", + "basic": "RUN\"GORFLAND\"\n" + }, + "98f854b216ea6861c9ed28891e01e923890c96df": { + "filename": "Grabber (Tom Mix Software) (6309 compatible).zip", + "disk0": "GRABBER.DSK", + "joy-right": "kjoy0", + "basic": "LOADM\"GRABBER\":EXEC\n" + }, + "f7a717741d42b7c78d405c4c5fd91dbbc69a03d9": { + "filename": "Grabber (Tom Mix Software).zip", + "disk0": "GRABBER.DSK", + "joy-right": "kjoy0", + "basic": "LOADM\"GRABBER\":EXEC\n" + }, + "7f0d3ae38db5caea0ff91c072027af03c3b84a4c": { + "filename": "Grand Prix Challenge (Diecom Products) (Coco 3).zip", + "machine": "coco3", + "disk0": "GRANDPRX.DSK", + "basic": "LOADM\"GRANDPRX\":EXEC\n" + }, + "1b60513e6eef6b318683406d6ed1e86f1b353f51": { + "filename": "Grand Prix (Intracolor).zip", + "disk0": "GRPRIX.DSK", + "joy-left": "kjoy0", + "basic": "LOADM\"GRPRIX\":EXEC\n" + }, + "ec5b52f93367a935f8e3217f098e137bd5705ccd": { + "filename": "Grand Slam Bridge (Greentree Software).zip", + "disk0": "BRIDGE.DSK", + "basic": "RUN\"BRIDGE\"\n" + }, + "7d371f46ca610177329eb30a9cedcc7f2803415e": { + "filename": "Graphic Mars (Aardvark Action Software).zip", + "disk0": "MARS.DSK", + "basic": "LOADM\"MARS\":EXEC\n" + }, + "1b3793c78b3d67555d61fee4438764d6388a9bc3": { + "filename": "Graphic Nine Card (T&D Software).zip", + "disk0": "NINECARD.DSK", + "basic": "RUN\"NINECARD\"\n" + }, + "14ef3a90360e157320372823f641b21496cc32df": { + "filename": "Graphic Pyramid (Aardvark-80).zip", + "disk0": "Graphic Pyramid.dsk", + "basic": "LOADM\"PYRAMID\":EXEC\n" + }, + "4129f1a4c198e390b7c7a69d7c2df6d1a97c3538": { + "filename": "Gravitor (William Bond) (alt).zip", + "disk0": "GRAVITOR.DSK", + "basic": "LOADM\"GRAVITOR\":EXEC\n" + }, + "876d045a5104e02335868e352ba613e70ea9113e": { + "filename": "Gravitor (L. Dos Santos).zip", + "disk0": "GRAVITOR.DSK", + "basic": "LOADM\"GRAVITOR\":EXEC\n" + }, + "18addf1d18ec3f878b7b3a9d8a9e6750cccd0eaf": { + "filename": "Gray Lady (Jarb Software).zip", + "disk0": "GRAYLADY.DSK", + "joy-left": "mjoy0", + "basic": "LOADM\"GRAYLADY\":EXEC\n", + "tv-input": "redblue" + }, + "cc92b85b99612a501e5120b8fd4b86c844e02ae1": { + "filename": "Gremlin (Chromasette).zip", + "disk0": "GREMLIN.DSK", + "joy-right": "kjoy0", + "basic": "LOADM\"GREMLIN\":EXEC\n" + }, + "3a2bf29b36ec0cc8123f751c176111506396bdac": { + "filename": "Greymoon (Computer Shack).zip", + "disk0": "GREYMOON.DSK", + "basic": "RUN\"GREY\"\n" + }, + "a454f642f480a2bde0ad511f75f0aa1ebf2aaac3": { + "filename": "Grid (Chromasette).zip", + "disk0": "GRID.DSK", + "joy-left": "mjoy0", + "joy-right": "kjoy0", + "basic": "LOADM\"GRID\":EXEC\n" + }, + "8588f2701d87d05244cf9df2a8971527023d9630": { + "filename": "Grid Runner (Salamander Software).zip", + "disk0": "GRIDRUNR.DSK", + "joy-right": "kjoy0", + "basic": "LOADM\"GRIDRUNR\":EXEC\n" + }, + "87a809e28e7a87feea78a82370e52bd8fdfaab4d": { + "filename": "Gridiron Strategy (Sportsware) (Coco 3).zip", + "machine": "coco3", + "disk0": "GRIDNFL.DSK", + "basic": "LOADM\"G\":EXEC\n" + }, + "0282a91d6e0e39d4ba81884ac7e61c37a8632d7c": { + "filename": "Gryphon (James Redmon).zip", + "disk0": "GRYPHON.DSK", + "basic": "RUN\"GRYPHON\"\n" + }, + "3515f6edb0d14a7023ad2ecbcd242f2bee4222ca": { + "filename": "Guadalcanal (Ark Royal Games).zip", + "disk0": "guadal.dsk", + "basic": "RUN\"GUADAL\"\n" + }, + "565bd4c36980a4e403ae94de6dca301cb058e1fe": { + "filename": "Guardian (Quasar Animations) (SG-24) (Coco 1-2).zip", + "disk0": "guardian.dsk", + "joy-right": "kjoy0", + "basic": "LOADM\"GUARDIAN\":EXEC\n" + }, + "82f6cd1ec5bdbbb2e17ef598bf90d9e291f99aea": { + "filename": "Guess The Animal (Elite Software).zip", + "disk0": "animal.dsk", + "basic": "LOADM\"ANIMAL32\":EXEC\n" + }, + "d29cdad0056947e23741925af599699c8fdd5b41": { + "filename": "Gun Fighter (T&D Software).zip", + "disk0": "GUNFIGHT.DSK", + "joy-right": "kjoy0", + "basic": "LOADM\"GUNFIGHT\":EXEC\n", + "tv-input": "redblue" + }, + "3e063309722d6c8c49a5d286fdc6938286f6127d": { + "filename": "Hang Man (Jon Ruhnow) (Coco 3).zip", + "machine": "coco3", + "disk0": "hangman3.dsk", + "basic": "RUN\"HANG\"\n" + }, + "b141444d654325b634b1e60cb9fc6541f25404da": { + "filename": "Hangman (Snailsoft) (Coco 3).zip", + "machine": "coco3", + "disk0": "HANGMAN.DSK", + "basic": "RUN\"HANGMAN\"\n" + }, + "ca7c65623d216387190611e4b3caf0f90e7d7115": { + "filename": "Haunted House (T&D Software).zip", + "disk0": "hauntedh.dsk", + "basic": "RUN\"HAUNTEDH\"\n" + }, + "82e4da2d8986e03f32dd40c101764de9c630367e": { + "filename": "Haunted House - Write your own Adventure Programs for your Microcomputer (Coco 3).zip", + "machine": "coco3", + "disk0": "HAUNTEDH.DSK", + "basic": "RUN\"HAUNTEDH\"\n" + }, + "a7dc8e114d0a692b94d27951ca4187b6bd6f1305": { + "filename": "Haywire (Mark Data Products).zip", + "disk0": "HAYWIRE.DSK", + "joy-right": "kjoy0", + "basic": "LOADM\"HAYWIRE\":EXEC\n" + }, + "8ae6c7be18ec1fc8e7b30e6984332bd9a8f7c955": { + "filename": "Head of the Beast (Mark Nelson).zip", + "disk0": "HEAD.DSK", + "joy-right": "kjoy0", + "basic": "RUN\"HEAD\"\n" + }, + "7ea3ef65234c28c47ab0b2416275b36a6e42c196": { + "filename": "Helo Battle (Jarb Software).zip", + "disk0": "HELOBATL.DSK", + "joy-left": "mjoy0", + "joy-right": "kjoy0", + "basic": "RUN\"HELOBATL\"\n" + }, + "5acad24a54d9ed58be4e0b0449c29602ae8359cf": { + "filename": "Heist (Doug Thorsvik).zip", + "disk0": "HEIST.DSK", + "basic": "RUN\"HEIST\"\n" + }, + "88cea842ecc8ab435082428870b24e418667a487": { + "filename": "Hex (D.R. Owen).zip", + "disk0": "HEX.DSK", + "basic": "RUN\"HEX\"\n" + }, + "3f10894561eff28cc5233c9745945b0dc2a6ada4": { + "filename": "Hi-Lo Dice (T&D Software).zip", + "disk0": "HILODICE.DSK", + "basic": "RUN\"HILODICE\"\n" + }, + "78c667b7e19411aa71db780ac6410211af9da673": { + "filename": "Home Base (Cable Software) (Coco Port).zip", + "disk0": "HOMEBASE.DSK", + "joy-right": "kjoy0", + "basic": "LOADM\"HOMEBASE\":EXEC\n" + }, + "fdee90681d6b0ef95dbc5fa35ec400330f9e2b81": { + "filename": "Home Bingo (Williams Enterprises).zip", + "disk0": "BINGO.DSK", + "basic": "RUN\"BINGO\"\n" + }, + "c46d1809e248062d8e8c0250219c7ad88b4fa4a9": { + "filename": "Hopbopper (Public Domain).zip", + "disk0": "HOPBOP.DSK", + "joy-right": "kjoy0", + "basic": "LOADM\"HOPBOP\":EXEC\n" + }, + "d1d5a58329a48e7bacbf571c29969ad8aa8d05e7": { + "filename": "Horace Goes Skiing (Melbourne House).zip", + "disk0": "HORACESK.DSK", + "joy-left": "kjoy0", + "basic": "LOADM\"HORACESK\":EXEC\n" + }, + "5e9eba4d5a6e53ffcd4174bca84ac93e55f4b327": { + "filename": "Hornets (J. Morrison Micros).zip", + "disk0": "hornets.dsk", + "basic": "LOADM\"HORNETS\":EXEC\n" + }, + "60d6e19adc27725183f4c0597e566fdec800b920": { + "filename": "Hot Slot (Moreton Bay Software) (alt).zip", + "disk0": "HOTSLOT.DSK", + "basic": "RUN\"HOTSLOT\"\n" + }, + "48ea8b46a48563e5ae38580b738a5ee7f63b8042": { + "filename": "Hot Slot (Moreton Bay Software).zip", + "disk0": "HOTSLOT.DSK", + "basic": "LOADM\"HOTSLOT\":EXEC\n" + }, + "1e3b55dbdcccd37deed71a2c2586fdf5b2c131fa": { + "filename": "Hyper Zone (Computerware).zip", + "disk0": "HYPRZONE.DSK", + "joy-right": "kjoy0", + "basic": "LOADM\"HYPRZONE\":EXEC\n" + }, + "c93fbacaa1a0e68ee8f7fd0e5908e5eee4db44bb": { + "filename": "Hungry Horace (Melbourne House).zip", + "disk0": "HORACE.DSK", + "basic": "LOADM\"HORACE\":EXEC\n" + }, + "dd42a87efce33e95157a412748ee7458c57d79d0": { + "filename": "Ice Hockey (Computerware) (alt).zip", + "disk0": "HOCKEY.DSK", + "joy-right": "kjoy0", + "basic": "LOADM\"HOCKEY\":EXEC\n" + }, + "1b1917b4a626e9b3adfed07b61e302257c4fbdde": { + "filename": "Ice Bird (Crystal Software).zip", + "disk0": "ICEBIRD.DSK", + "joy-right": "kjoy0", + "basic": "LOADM\"ICEBIRD\":EXEC\n" + }, + "c5f578c50654d5e66ce50177a46901828990035e": { + "filename": "Ice Hockey (Computerware).zip", + "disk0": "HOCKEY.DSK", + "joy-right": "kjoy0", + "basic": "LOADM\"HOCKEY\":EXEC\n" + }, + "e02e49f1b009e424175ea9f0b633a8bb305ea528": { + "filename": "Ice Master (Arcade Animation Inc.).zip", + "disk0": "ICEMSTR.DSK", + "joy-right": "kjoy0", + "basic": "LOADM\"ICEMSTR\":EXEC\n" + }, + "08155b35dd8f87ef75e16057896e80ab267ecdd3": { + "filename": "Ice Hockey (Intellectronics).zip", + "disk0": "HOCKEY.DSK", + "joy-right": "kjoy0", + "basic": "LOADM\"HOCKEY\":EXEC\n" + }, + "964efda056ffd6e4358c1572f359deb7b7bce9b0": { + "filename": "Iconia (Carlos Eduardo Rocha).zip", + "disk0": "ICONIA.DSK", + "joy-right": "mjoy0", + "basic": "RUN\"ICONIA\"\n" + }, + "0c3c44b569249d80fae1f33181bd3b34c4fc0551": { + "filename": "Icon of Naliphae, The (Adventureware).zip", + "disk0": "NALIPHAE.DSK", + "basic": "RUN\"BOOT\"\n" + }, + "19ca20a96c1a940aba9f8560cea4bdbd6114350b": { + "filename": "Identikit (The Rainbow).zip", + "disk0": "IDENTKIT.DSK", + "basic": "RUN\"IDENTKIT\"\n" + }, + "5b8ef6aa93fe5063c519abdb65cc46da58d2688a": { + "filename": "Impossiball! (Dragonfire Services) (Coco port).zip", + "disk0": "IBALL.DSK", + "joy-right": "mjoy0", + "basic": "LOADM\"IBALL\":EXEC\n" + }, + "71adb93a373bf743c281bb89db0063e1bba1a3e6": { + "filename": "Indoor Football (Quickbeam Software) (Coco port).zip", + "disk0": "INDFOOTY.DSK", + "joy-right": "kjoy0", + "basic": "LOADM\"INDFOOTY\":EXEC\n" + }, + "c1d7b0a14d28fbc32ca1439bc81b971a270da0f9": { + "filename": "Inspector Clueseau (Petrocci Freelance Associates).zip", + "disk0": "cluseau.dsk", + "joy-right": "kjoy0", + "basic": "RUN\"CLUESEAU\"\n" + }, + "0d19b5aecea48e5d12dfecb8d6919f1b470418b5": { + "filename": "Intercept 4 (Computer Shack-Michtron).zip", + "disk0": "INTERC4.DSK", + "joy-left": "mjoy0", + "basic": "LOADM\"INTERC4\":EXEC\n" + }, + "d595b8eba5435b04efc452020eb78287730d30b4": { + "filename": "Interplanetary Trash Collector (The Rainbow) (Coco 3).zip", + "machine": "coco3", + "disk0": "trash.dsk", + "basic": "RUN\"TRASH\"\n" + }, + "0868dc8d7bebde7de0e5d4e3176a5acf8232c494": { + "filename": "Intersteller Themonuclear War (Coco 3).zip", + "machine": "coco3", + "disk0": "ITW.DSK", + "joy-right": "mjoy0", + "basic": "RUN\"ITW\"\n" + }, + "e7e8d88eca678e6abccc5e5ef3dd087fb3abe4cd": { + "filename": "Invaders09 (Sub-Etha Software) (OS-9) (Coco 3).zip", + "machine": "coco3", + "disk0": "INVADE09.DSK", + "joy-left": "kjoy0", + "basic": "DOS\n" + }, + "9827d80e7fb6b825d51764aefe8c4c5b154a9451": { + "filename": "Invader's Revenge (Med Systems-Screenplay).zip", + "disk0": "INVREV.DSK", + "basic": "LOADM\"INVREV\":EXEC\n" + }, + "9203f075cc890903ad7b13c69ff8c2c84e70eaa6": { + "filename": "Invasion (Creative Computing Software).zip", + "disk0": "INVASION.DSK", + "joy-right": "kjoy0", + "basic": "LOADM\"INVASION\":EXEC\n" + }, + "68ab8e92204e47f99208446b36cc9c9f02b08fbd": { + "filename": "Iron Cross (Computerware).zip", + "disk0": "ironcros.dsk", + "basic": "RUN\"IRON\"\n", + "tv-input": "redblue" + }, + "228f4c06af2fe2425e23e6062e6faa2fac4959cf": { + "filename": "Island of Dead (Joe Semenik).zip", + "disk0": "ISLAND.DSK", + "basic": "RUN\"ISLAND\"\n" + }, + "200439d2c319b11a42b4f28881093f3389289cd4": { + "filename": "Jackpot Poker (Jack & Pegi Tindle).zip", + "disk0": "JACKPOT.DSK", + "basic": "LOADM\"POKER\":EXEC\n", + "tv-input": "redblue" + }, + "9fc8049756a2952ae33cf1f629d41aed95ee0c5c": { + "filename": "Jaws (Michael Freeman).zip", + "disk0": "JAWS.DSK", + "joy-right": "kjoy0", + "basic": "LOADM\"JAWS\":EXEC\n" + }, + "58b534622da1f758940711a7e0c09caa92b4e986": { + "filename": "Jeopardy (T&D Software).zip", + "disk0": "jeopardy.dsk", + "joy-left": "mjoy0", + "joy-right": "mjoy0", + "basic": "RUN\"JEOP\"\n" + }, + "7123efbf733258c1517d1edd6983f3cf64782b09": { + "filename": "Jetpack Challenge (The Rainbow).zip", + "disk0": "JETPACK.DSK", + "joy-right": "kjoy0", + "basic": "RUN\"JETPACK\"\n" + }, + "bdc0f593e64d20fd1a19759d86b04bab604278d7": { + "filename": "Jetset Willy (Software Projects) (Coco port).zip", + "disk0": "JETSET.DSK", + "joy-left": "kjoy0", + "basic": "LOADM\"JETSET\":EXEC\n" + }, + "549fb0d4cf554c1d6bd0cd149d37f3185ccf6a82": { + "filename": "Jeweled! (Roger Taylor) (Coco 3).zip", + "machine": "coco3", + "disk0": "jeweled.dsk", + "joy-left": "mjoy0", + "basic": "LOADM\"JEWELED\":EXEC\n" + }, + "dd2e0643d0298b3578fc544ba6af582d932976d4": { + "filename": "Jeweled! (Roger Taylor) (Coco 3).zip", + "machine": "coco3", + "disk0": "jeweled.dsk", + "joy-left": "mjoy0", + "basic": "LOADM\"JEWELED\":EXEC\n" + }, + "87b9afbd05d89a954c0b0a0cf78a8190df48a932": { + "filename": "Joker Poker (Coco 3) (The Rainbow).zip", + "machine": "coco3", + "disk0": "JOKERPKR.DSK", + "basic": "RUN\"JOKERPKR\"\n" + }, + "85ee88a9e95758e5686d77f0bd2b726c132b66d4": { + "filename": "Journey to Mt. Doom (Tom Mix Software).zip", + "disk0": "MTDOOM.DSK", + "basic": "RUN\"DOOM\"\n" + }, + "72b3b88eef1f61cba8d43ed6a828a773c4c703f8": { + "filename": "Joust v1.1 (Glen Hewlett) (Coco 3).zip", + "machine": "coco3", + "disk0": "JOUST.DSK", + "basic": "RUN\"JOUST\"\n" + }, + "4590a5fdbf59ff903a2509c249224ca8a50fd2c7": { + "filename": "Jubilex (GSW Software) (Coco 3).zip", + "machine": "coco3", + "disk0": "jubilex.dsk", + "joy-right": "kjoy0", + "basic": "LOADM\"JUBILEX\":EXEC\n" + }, + "8f433958504041d2e794fc00e0318c1a3b9838a9": { + "filename": "Jumbo Jet (aka 747C Flight Simulator 3.6) (Prickly-Pear Software).zip", + "disk0": "JET747.DSK", + "basic": "RUN\"JET747\"\n" + }, + "62f1940161459ad1e3670838bf3357bbbe2d638e": { + "filename": "Jumpship (Alison deNu).zip", + "disk0": "JUMPSHIP.DSK", + "basic": "RUN\"JUMPSHIP\"\n" + }, + "3457bf302ee973f814901d3afd7fac17398fa552": { + "filename": "Jumpy (MatchyGames) (SG-12) (CoCo PSG).zip", + "disk0": "jumpy.dsk", + "basic": "RUN\"JUMPY\"\n" + }, + "b2fceb2860b75c6e54b46601f30df3c117b1db86": { + "filename": "Jungle Queen (Zoso Software).zip", + "disk0": "JUNGLE.DSK", + "joy-right": "kjoy0", + "basic": "LOADM\"JUNGLE\":EXEC:LOADM\"JUNGLE2\":EXEC\n" + }, + "318c212875e42755547fd325d088fd7b5fe18f50": { + "filename": "Jungle (T&D Software).zip", + "disk0": "jungle.dsk", + "basic": "RUN\"JUNGLE\"\n" + }, + "cef2ce7dc5f7cbf2b2da8a979870cd82168ea726": { + "filename": "Junior's Revenge (Computerware).zip", + "disk0": "JUNIOR.DSK", + "joy-right": "kjoy0", + "basic": "LOADM\"JUNIOR\":EXEC\n", + "tv-input": "redblue" + }, + "e932998ec17309506901c0707042ff74f12d3598": { + "filename": "Junk Food (The Rainbow) (SG-24) (Coco 1-2).zip", + "disk0": "JUNKFOOD.DSK", + "joy-right": "kjoy0", + "basic": "LOADM\"JUNKFOOD\":EXEC\n" + }, + "34f718b962527da1b2cc7ca43558070ab2dc314b": { + "filename": "Juno (Federal Hill).zip", + "disk0": "JUNO.DSK", + "joy-right": "kjoy0", + "basic": "LOADM\"JUNO\":EXEC\n" + }, + "4965cc882e68a130939bcbf29206bcd651463728": { + "filename": "Jupiter Patrol (Aardvark-80).zip", + "disk0": "JUPATROL.DSK", + "joy-right": "kjoy0", + "basic": "LOADM\"JUPATROL\":EXEC\n" + }, + "b40d933fded3d78929ebf8d191a465e557ed372c": { + "filename": "Juxtaposition (Wintersoft) (Coco port).zip", + "disk0": "JUXTA.DSK", + "basic": "LOADM\"JUXTA\":EXEC\n" + }, + "dc6f257ab22a2f39a46ba254f46b810d3188c4a7": { + "filename": "Kamikaze (Ark Royal Games).zip", + "disk0": "K-32.DSK", + "joy-right": "kjoy0", + "basic": "RUN\"K32\"\n" + }, + "d066013154ed730b58cc0d09c779d3f8f96da159": { + "filename": "Karate (Diecom Products).zip", + "disk0": "KARATE.DSK", + "joy-left": "kjoy0", + "basic": "LOADM\"KARATE\":EXEC\n", + "tv-input": "redblue" + }, + "6449c16b400f5c3c8c5f9930d64993d73a0bef22": { + "filename": "Katerpillar Attack (Tom Mix Software).zip", + "disk0": "KATERPIL.DSK", + "joy-right": "mjoy0", + "basic": "LOADM\"KATERPIL\":EXEC\n" + }, + "ff36bc720439e7e88ef226558d33351ede2b9989": { + "filename": "Kellycheckers III (Dracman) (Coco 3).zip", + "machine": "coco3", + "disk0": "KELLYIII.DSK", + "basic": "RUN\"KELLYIII\"\n" + }, + "f6c10c0d8c247be89be0d5b5392900c1c0898737": { + "filename": "Keys of the Wizard (Spectral Associates) (SSC).zip", + "disk0": "WIZARD.DSK", + "basic": "LOADM\"WIZARD\":EXEC\n" + }, + "7c94c994c656fbaf55b37f2bc8d0346ce043a4c1": { + "filename": "Killer Mansion (T&D Software).zip", + "disk0": "KILLERM.DSK", + "basic": "RUN\"KILLERM\"\n" + }, + "eabcd8c2854ac15b13a4e9c29b8b0d48b1dd1517": { + "filename": "King Carlo (H. Allen Curtis) (Coco 3).zip", + "machine": "coco3", + "disk0": "KC.DSK", + "joy-right": "kjoy0", + "basic": "RUN\"KC\"\n" + }, + "9ed761c8d5fc862be9a9d9e7168239a8f8fa7b2a": { + "filename": "King Cuthbert (Microdeal).zip", + "disk0": "KINGCUTH.DSK", + "joy-right": "kjoy0", + "basic": "LOADM\"KINGCUTH\":EXEC\n" + }, + "f1570f72e86f75b132c114e1c7e62e03504d37eb": { + "filename": "Kingdom of Bashan (Owls Nest Software).zip", + "disk0": "BASHAN.DSK", + "basic": "PCLEAR1:RUN\"BASHAN\"\n" + }, + "7c94f756faabaf0a563c6f040053c01db3f9991c": { + "filename": "Kingpede (T&D Software).zip", + "disk0": "KINGPEDE.DSK", + "joy-right": "kjoy0", + "basic": "LOADM\"KINGPEDE\":EXEC\n" + }, + "35bfdcda58b5ab83d36195658eac51ea2d3b1425": { + "filename": "King, The (Tom Mix Software).zip", + "disk0": "theking.dsk", + "joy-right": "kjoy0", + "basic": "LOADM\"THEKING\":EXEC\n", + "tv-input": "redblue" + }, + "cd2a32929047a1ad81c804eeeb4777b02641e2e3": { + "filename": "King Tut (DSL Computer Products).zip", + "disk0": "KINGTUT.DSK", + "joy-left": "kjoy0", + "basic": "RUN\"KING TUT\"\n" + }, + "74f90b5d43ce3ef35d74bc43373e9ed5c66b931a": { + "filename": "King Tut (Tandy).zip", + "disk0": "KINGTUT.DSK", + "joy-right": "kjoy0", + "basic": "LOADM\"KINGTUT\":EXEC\n" + }, + "f05c8d28cc94fba4f435bd17ce22aaebb4bcaf0a": { + "filename": "Klendathu (Tandy).zip", + "disk0": "KLENDATH.DSK", + "basic": "RUN\"KLENDATH\"\n" + }, + "4b99a657df5f77a1ffc4a2b3e49904781d9d5216": { + "filename": "Klondike Solitaire (ColorSystems) (OS-9) (Coco 3).zip", + "machine": "coco3", + "disk0": "klondike.dsk", + "joy-right": "mjoy0", + "basic": "DOS\n" + }, + "3f774c1ff9e35650d771779611a9aebf63fe598b": { + "filename": "Komet Kaze (Thomas Czarnecki).zip", + "disk0": "KOMET.DSK", + "joy-right": "kjoy0", + "basic": "LOADM\"KOMET\":EXEC\n" + }, + "934af60804f09dbf85de2db91e77fa414e102a73": { + "filename": "Koronis Rift (Epyx) (OS-9) (Coco 3).zip", + "machine": "coco3", + "disk0": "KORONIS.DSK", + "joy-left": "mkjoy0", + "basic": "DOS\n" + }, + "400865559a4b4b2904c6288c3fa62c9b6812c39b": { + "filename": "Kosmic Kamikaze (Illustrated Memory Banks).zip", + "disk0": "KOSMIC.DSK", + "joy-right": "mjoy0", + "basic": "RUN\"KOSMIC\"\n" + }, + "81bb8086960adf33951b5f1ea0df3e76a3e687c2": { + "filename": "Kron (Oregon Color Computer Systems).zip", + "disk0": "KRON.DSK", + "joy-right": "kjoy0", + "basic": "LOADM\"KRON\":EXEC\n" + }, + "0deb8274818ba320e1a316df5c94cb0f0ac7fd87": { + "filename": "La Belle Lucie (Eversoft Games) (Coco 3).zip", + "machine": "coco3", + "disk0": "lucy.dsk", + "joy-right": "mjoy0", + "basic": "RUN\"START\"\n" + }, + "ac8adb2b3616213995a1194bbb0f871f41c98b58": { + "filename": "Kung-Fu Dude (Sundog Systems).zip", + "disk0": "KUNGFU.DSK", + "joy-right": "kjoy0", + "basic": "LOADM\"KUNGFU\":EXEC\n" + }, + "474f8f4bc518e5a523c483d0978356425aa7cb12": { + "filename": "Labyrinth (Aardvark-80).zip", + "disk0": "LABYRINT.DSK", + "basic": "RUN\"LABYRINT\"\n" + }, + "23e8174259797562836bb4e7a94be2952d00151b": { + "filename": "Labyrinth Master (T&D Software).zip", + "disk0": "LABMAS.DSK", + "basic": "LOADM\"LABMAS\":EXEC\n" + }, + "6bf601c06dbc5111445133425eef41ca7be14b83": { + "filename": "Ladderman 2 (Richard Kelly).zip", + "disk0": "LADDER2.DSK", + "basic": "RUN\"LADDER2\"\n", + "tv-input": "redblue" + }, + "1fc4cd8c4acaf510a7e469b6ac14ce973a1ed2de": { + "filename": "Lair of the Kraken, The (Beth Ann Norman).zip", + "disk0": "KRAKEN.DSK", + "joy-right": "kjoy0", + "basic": "RUN\"KRAKEN\"\n" + }, + "58b0670c5a4beb9246efed69f388259ada70e9e1": { + "filename": "Lair, The (Freebooter Software).zip", + "disk0": "THELAIR.DSK", + "joy-right": "kjoy0", + "basic": "LOADM\"THELAIR\":EXEC\n" + }, + "982f191100a175cb32c0ac927be1f2440822e1ac": { + "filename": "Lancer (Spectral Associates).zip", + "disk0": "LANCER.DSK", + "joy-right": "kjoy0", + "basic": "LOADM\"LANCER\":EXEC\n" + }, + "7ed72dbe2d91f18836d9725713b53124fe31db44": { + "filename": "Lady Buggy (David Crandall).zip", + "disk0": "LADYBUG.DSK", + "joy-right": "kjoy0", + "basic": "LOADM\"LADYBUG\":EXEC\n" + }, + "9930f099c4f804902339a434a4ac497046c4df25": { + "filename": "Lancer (Cheat) (Spectral Associates).zip", + "disk0": "LANCERTR.DSK", + "joy-right": "kjoy0", + "basic": "LOADM\"LANCERTR\":EXEC\n" + }, + "f1a30e3a29a455f9dcab5eb2fa7b3132630a18c1": { + "filename": "Lander (Duane D. Nelson).zip", + "disk0": "LANDER.DSK", + "basic": "LOADM\"LANDER\":EXEC\n" + }, + "692dc3a034b25e206498f799b7732b0284f4e670": { + "filename": "Lander (T&D Software).zip", + "disk0": "LANDER.DSK", + "basic": "LOADM\"LANDER\":EXEC\n" + }, + "09749155a7832a923d674949c90aef2bd4db1c7d": { + "filename": "Laser Blast (Reynold Wells).zip", + "disk0": "LASRBLST.DSK", + "joy-right": "kjoy0", + "basic": "RUN\"LASRBLST\"\n" + }, + "c860da31618ad04c1ec01fa4a19d4236e733f067": { + "filename": "Laser Star (Jarb Software).zip", + "disk0": "LASRSTAR.DSK", + "joy-left": "kjoy0", + "joy-right": "mjoy0", + "basic": "RUN\"LASRSTAR\"\n" + }, + "69e2430585853654040623b8dab1e6f8152afd4e": { + "filename": "Laser Command (formerly Laser Attack) (Spectral Associates).zip", + "disk0": "LASERCMD.DSK", + "joy-right": "mjoy0", + "basic": "RUN\"LASERCMD\"\n" + }, + "079b5ffc05aff1211e63733c9f3bffe1445406a3": { + "filename": "Laser Tank Duel (Renaissance Game Designs).zip", + "disk0": "TANKDUEL.DSK", + "basic": "LOADM\"TANKDUEL\":EXEC\n" + }, + "bdf7e6da37b420c1657f7076219e35ba9b71920c": { + "filename": "Laserworm & Firefly (Electrosoft Inc.).zip", + "disk0": "LASERWRM.DSK", + "joy-right": "kjoy0", + "basic": "RUN\"LASERWRM\"\n" + }, + "15c2ef5533c5632e3f0410d7893bc06b97018c63": { + "filename": "Last Pirate (Tandy).zip", + "disk0": "PIRATE.DSK", + "basic": "RUN\"PIRATE\"\n" + }, + "b97186cb6115617d0ab40fed794b125ef3619dc8": { + "filename": "Lava Hero (Paul Shoemaker).zip", + "machine": "coco3", + "disk0": "LAVAHERO.DSK", + "joy-right": "kjoy0", + "basic": "LOADM\"LAVAHERO\":EXEC\n" + }, + "0a6f196d7d6bbcd0c5399a1dd2615ad6deab6243": { + "filename": "L-Berto (Phoenix).zip", + "disk0": "LBERTO.DSK", + "joy-right": "kjoy0", + "basic": "RUN\"LBERTO\"\n" + }, + "f0939276d377e5732608eff46303e7e50490d494": { + "filename": "Leggit (Visual Imagine Software Ltd) (Coco port).zip", + "disk0": "LEGGIT.DSK", + "basic": "RUN\"LEGGIT\"\n" + }, + "d2809bf288481261b1200c6aae125d4aee202b1a": { + "filename": "Legend Quest (D&N Software).zip", + "disk0": "Legend Quest.dsk", + "joy-right": "kjoy0", + "basic": "RUN\"LEGEND\"\n", + "tv-input": "redblue" + }, + "d5e44c2a06d1648351eb51ee314465594499f675": { + "filename": "Liberty Ship (T&D Software).zip", + "disk0": "LIBERTY.DSK", + "joy-right": "kjoy0", + "basic": "LOADM\"LIBERTY\":EXEC\n" + }, + "64cf2a10e559df707890b4c93286892a7c65eaa4": { + "filename": "Le Mans (Spectral Associates).zip", + "disk0": "LEMANS.DSK", + "joy-right": "kjoy0", + "basic": "LOADM\"LEMANS\":EXEC\n" + }, + "7e5a5e1149d44c5c55a851898e53e75c3ccc421d": { + "filename": "Life (T&D Software).zip", + "disk0": "LIFE.DSK", + "basic": "LOADM\"LIFE\":EXEC\n" + }, + "eb321e2d0eadf63749cf30df3f33f57827a6e315": { + "filename": "Little Runner (The Rainbow).zip", + "disk0": "SMURF.DSK", + "joy-right": "kjoy0", + "basic": "RUN\"SMURF\"\n" + }, + "1fccac8305a1c955983fe1b70710f7531c14306c": { + "filename": "Lode Runner Beta 4.1 (Mark McDougall) (Coco 3).zip", + "machine": "coco3", + "disk0": "lrmono.dsk", + "basic": "RUN\"LR\"\n" + }, + "724bde4aa7a1298bec50f955f3ef575e44952169": { + "filename": "Lode Runner Beta 4.1 (Mark McDougall) (Coco 3).zip", + "machine": "coco3", + "disk0": "lrmono.dsk", + "basic": "RUN\"LR\"\n" + }, + "761cc18d30888650994e215fd4392be5ba15dbb6": { + "filename": "Life An Everyday Adventure (Stephen Berry) (Coco 3).zip", + "machine": "coco3", + "disk0": "LIFE.DSK", + "basic": "RUN\"BOOT\"\n" + }, + "564482df98596d6a96717a9a23ce16d97df15454": { + "filename": "Lokar's Magic Staff (Real Software Company).zip", + "disk0": "LOKAR.DSK", + "basic": "RUN\"LOKARSTF\"\n" + }, + "7207ef058d42182dc697536e1ebaa3e64ae74f0a": { + "filename": "Loopy (T&D Software).zip", + "disk0": "loopy.dsk", + "joy-right": "kjoy0", + "basic": "LOADM\"LOOPY\":EXEC\n" + }, + "267f2c801bf82b86f15e1d6bdfa24fb4265412d2": { + "filename": "Lost City, USA (Software Unlimited).zip", + "disk0": "LOSTCITY.DSK", + "basic": "RUN\"LOSTCITY\"\n" + }, + "9d1c1c2229c20389e7cc282516e4d040d70ffa1d": { + "filename": "Lucifer's Kingdom (Orange Software) (Coco Port) (6809 optimized).zip", + "disk0": "LUCIFR68.DSK", + "joy-left": "kjoy0", + "basic": "LOADM\"LUCIFER\":EXEC\n" + }, + "b54cc5502854ce98be2d5a8e244330a54ef51974": { + "filename": "Lost Dutchman, The (Marc Steinman) (OS-9).zip", + "disk0": "DUTCHMAN.DSK", + "basic": "DOS\n" + }, + "d5331aacd0b58955dbfa875151184d3076c219d1": { + "filename": "Lunar Lander (Greg Zumwalt).zip", + "disk0": "landergz.dsk", + "joy-right": "kjoy0", + "basic": "RUN\"LANDER\"\n" + }, + "46c5f2e5772590a82fc9edc3f3a51a00cab276cf": { + "filename": "Lunar Lander (Public Domain).zip", + "disk0": "LUNAR.DSK", + "joy-right": "kjoy0", + "basic": "LOADM\"LUNAR\":EXEC\n" + }, + "7726806fdfd22f374e5a16d983108e5e00fdf359": { + "filename": "Lunar Lander (The Rainbow) (Coco 3).zip", + "machine": "coco3", + "disk0": "LANDER.DSK", + "joy-right": "kjoy0", + "basic": "RUN\"LANDER\"\n" + }, + "38192e07dd5f5142838be625840053c8b1ff77f5": { + "filename": "Lunar Rescue (Clyde Johnson).zip", + "disk0": "RESCUE.DSK", + "joy-right": "kjoy0", + "basic": "RUN\"RESCUE\"\n" + }, + "6c695525ac0775c6eb5981159a31ee6cba829233": { + "filename": "Lunar-Rover Patrol (Spectral Associates).zip", + "disk0": "ROVER.DSK", + "joy-right": "kjoy0", + "basic": "LOADM\"ROVER\":EXEC\n" + }, + "c477e7a4100e55ae85d2647680a248c916ba5edb": { + "filename": "Lunattack (Hewson Consultants Ltd) (Coco port).zip", + "disk0": "LUNATACK.DSK", + "joy-right": "kjoy0", + "basic": "LOADM\"LUNATACK\":EXEC\n" + }, + "b60959f3e8e321bf6cf5946e365b313f139a6566": { + "filename": "Lunchtime (Tom Mix Software).zip", + "disk0": "LUNCH.DSK", + "joy-left": "kjoy0", + "basic": "LOADM\"LUNCH\":EXEC\n" + }, + "8a4aaceef18f803ad9fbee162646c1df7597e755": { + "filename": "Lurkley Manor (The Rainbow).zip", + "disk0": "LURKLEY.DSK", + "basic": "RUN\"LURKLEY\"\n" + }, + "665c9f669d98b7805eed6aff91acd107a0cbb759": { + "filename": "Madness and the Minotaur (Tandy).zip", + "disk0": "MINOTAUR.DSK", + "basic": "LOADM\"MINOTAUR\":EXEC\n" + }, + "6647c30d4466d3a5286f8f5e7eb6e91248de9c55": { + "filename": "Madam Rosa's Massage Parlor (Softcore Software Company).zip", + "disk0": "MADAM.DSK", + "basic": "LOADM\"MADAM\":EXEC\n" + }, + "e770725edb1d1b065789010a9d8f99917644ce75": { + "filename": "Lutins du Mont Rigaud, Les (Louis Parson).zip", + "machine": "coco3", + "disk0": "lutins.dsk", + "basic": "RUN\"LELUTIN3\"\n", + "tv-input": "composite" + }, + "72a92d416668abe4edd6e8382cd3addb89babd03": { + "filename": "Magic (Aftamonow & Bernico).zip", + "disk0": "MAGIC.DSK", + "basic": "RUN\"MAGIC\"\n" + }, + "9ea3d5c15b31be51dc1db437ea1a0964c7aa4e0e": { + "filename": "Magic Stones (Floyd Resler) (Coco 3).zip", + "machine": "coco3", + "disk0": "STONES.DSK", + "joy-right": "kjoy0", + "basic": "DOS\n" + }, + "077039e559f3d1cd92e5acf329c345c21f073550": { + "filename": "Manic Miner (Software Projects) (Coco port).zip", + "disk0": "MANIC.DSK", + "basic": "LOADM\"MANIC\":EXEC\n" + }, + "f2486cd8a176de56d00eb3e02b3fa4cc47e3f051": { + "filename": "Mansion Adventure (CLOAD).zip", + "disk0": "MANSION.DSK", + "basic": "RUN\"MANSION\"\n" + }, + "a31df7866a60786aa6aa1d871cf18391eafea58f": { + "filename": "Marathon (Afabear Software).zip", + "disk0": "MARATHON.DSK", + "joy-right": "kjoy0", + "basic": "RUN\"MARATHON\"\n" + }, + "a1d8951ebb00baad376d86f4314c6e076c2834c3": { + "filename": "Marble Maze (Cheat) (Diecom Products).zip", + "disk0": "MARBLE.DSK", + "joy-left": "kjoy0", + "basic": "LOADM\"MARBLE\":EXEC\n" + }, + "60ff43afe0a340764b18e855e28b7d0411482767": { + "filename": "Marble Maze (Diecom Products).zip", + "disk0": "MARBLE.DSK", + "joy-left": "kjoy0", + "basic": "LOADM\"MARBLE\":EXEC\n" + }, + "94af98836344927c2d3deada93750833174ea86a": { + "filename": "Major Istar (Computerware).zip", + "disk0": "MISTAR.DSK", + "basic": "LOADM\"MISTAR\":EXEC\n", + "tv-input": "redblue" + }, + "f5a70f7c4b4ed58ef0454263146657420f292cf3": { + "filename": "Major Istar (Computerware).zip", + "disk0": "MISTAR.DSK", + "basic": "LOADM\"MISTAR\":EXEC\n", + "tv-input": "redblue" + }, + "6ad6412cfceff3cb542e58fcbf2c218250786dc0": { + "filename": "Major Istar (Computerware).zip", + "disk0": "MISTAR.DSK", + "basic": "LOADM\"MISTAR\":EXEC\n", + "tv-input": "redblue" + }, + "28cd16d2cd17e445688b67e00b2b1ad6c66c40fa": { + "filename": "Major Istar (Computerware).zip", + "disk0": "MISTAR.DSK", + "basic": "LOADM\"MISTAR\":EXEC\n", + "tv-input": "redblue" + }, + "99cc4921365ec4a5f41b8f64ac700b19786f7033": { + "filename": "Marooned (Saguaro Software).zip", + "disk0": "MAROONED.DSK", + "basic": "RUN\"MAROONED\"\n", + "tv-input": "redblue" + }, + "020ad9469bf4eb535c7ebb0a1f189e00f6b9c213": { + "filename": "Mars (Aardvark-80).zip", + "disk0": "mars.dsk", + "basic": "RUN\"MARS\"\n" + }, + "5aed1f751961928f4a148ee7a27f058e70ee61af": { + "filename": "Martian Crypt, The (Tom Mix Software) (SSC).zip", + "disk0": "MARTIAN.DSK", + "basic": "LOADM\"MARTIAN\":EXEC\n", + "tv-input": "redblue" + }, + "9594436ea0e5000b83374fc3be57899b9165831d": { + "filename": "Mary and the Bees (Roger Taylor) (Coco 3).zip", + "machine": "coco3", + "disk0": "MARYATB.DSK", + "joy-right": "kjoy0", + "basic": "LOADM\"MARYATB\":EXEC\n" + }, + "cfcfb7f5307e5b410ce8f126534eee5e812e31cc": { + "filename": "Mary and the Butterflies Demo (Royer Taylor) (Coco 3).zip", + "machine": "coco3", + "disk0": "marydemo.dsk", + "joy-right": "kjoy0", + "basic": "LOADM\"MARYATB\":EXEC\n" + }, + "6f722c6827d980f7888acce3357c0979c6f50597": { + "filename": "Master Blaster (Ken Kalish).zip", + "disk0": "mblaster.dsk", + "joy-right": "kjoy0", + "basic": "LOADM\"MBLASTER\":EXEC\n" + }, + "5c4c0c8f132906a223b0272581deebbd61bf5427": { + "filename": "Master Handicapper (LNW Systems).zip", + "disk0": "MSTRHNDC.DSK", + "basic": "RUN\"FOOTBALL\"\n" + }, + "a76e46a80f4d4e50643814544eb91b26a9b94794": { + "filename": "Mastermind (Kim Bergman) (OS-9) (Coco 3).zip", + "machine": "coco3", + "disk0": "masmind.dsk", + "basic": "DOS\n" + }, + "8b86cba40d529ab53e2d62bdbce0639eec35d12d": { + "filename": "Master the Cube! (ColorSystems) (OS-9) (Coco 3).zip", + "machine": "coco3", + "disk0": "cubist.dsk", + "joy-right": "mjoy0", + "basic": "DOS\n" + }, + "d0815331413bd16e45704cc8d2a2b60570ea1094": { + "filename": "Match Game of Opposites (The Rainbow).zip", + "disk0": "MATCH.DSK", + "basic": "RUN\"MATCH1\"\n" + }, + "81a9c0b9a09e233c10b1017fc9c67b302a92ccc2": { + "filename": "Maui Vice (Tom Mix Software).zip", + "disk0": "MAUI.DSK", + "joy-right": "mjoy0", + "basic": "LOADM\"MAUI\":EXEC\n", + "tv-input": "redblue" + }, + "1dc9575925d78eb8f10b2c0cb6464534c43223cf": { + "filename": "Maximum (CLOAD).zip", + "disk0": "MAXIMUM.DSK", + "joy-left": "mjoy0", + "basic": "RUN\"MAXIMUM\"\n" + }, + "4b576e16e82ee6a64aefafa15d58526b65634ee8": { + "filename": "Maze, The (Chromasette).zip", + "disk0": "THEMAZE.DSK", + "joy-right": "kjoy0", + "basic": "RUN\"MAZE\"\n" + }, + "f0966726bad5999b279091aab92a5b69977fe811": { + "filename": "Maze Escape (Spectral Associates).zip", + "disk0": "escape.dsk", + "joy-right": "kjoy0", + "basic": "RUN\"ESCAPE\"\n" + }, + "303e39d80f5ace47d87e6255e6fcdf6ddd521035": { + "filename": "Maze Creator v1.00 (Retro Rick).zip", + "disk0": "MAZCREAT.DSK", + "basic": "RUN\"MAZE-GR\"\n" + }, + "6213aea719bd3487f8970a7afad14e7412d267bd": { + "filename": "Marty's Nightmare (SRB Software) (Coco 3).zip", + "machine": "coco3", + "disk0": "Marty's Nightmare (SRB Software) (Coco 3)/marty.dsk", + "joy-right": "kjoy0", + "basic": "DOS\n" + }, + "ad405a3bfb87630bc7fa3f8c519c0c5278ca0514": { + "filename": "Marty's Nightmare (SRB Software) (Coco 3).zip", + "machine": "coco3", + "disk0": "Marty's Nightmare (SRB Software) (Coco 3)/marty.dsk", + "joy-right": "kjoy0", + "basic": "DOS\n" + }, + "95b17b514cbd7cd38f53e72ad23df298f67e23ea": { + "filename": "Marty's Nightmare (SRB Software) (Coco 3).zip", + "machine": "coco3", + "disk0": "Marty's Nightmare (SRB Software) (Coco 3)/marty.dsk", + "joy-right": "kjoy0", + "basic": "DOS\n" + }, + "c9aebd7db1050d328b1580a23f4efd615725927b": { + "filename": "Marty's Nightmare (SRB Software) (Coco 3).zip", + "machine": "coco3", + "disk0": "Marty's Nightmare (SRB Software) (Coco 3)/marty.dsk", + "joy-right": "kjoy0", + "basic": "DOS\n" + }, + "608202d5181096d42e090c6d42de4d92933af272": { + "filename": "Marty's Nightmare (SRB Software) (Coco 3).zip", + "machine": "coco3", + "disk0": "Marty's Nightmare (SRB Software) (Coco 3)/marty.dsk", + "joy-right": "kjoy0", + "basic": "DOS\n" + }, + "a4766f752c792c992661639c2e4ccb30fd4dac29": { + "filename": "Marty's Nightmare (SRB Software) (Coco 3).zip", + "machine": "coco3", + "disk0": "Marty's Nightmare (SRB Software) (Coco 3)/marty.dsk", + "joy-right": "kjoy0", + "basic": "DOS\n" + }, + "6adbd33b6d5ad26c8fbfb82f93995d2a3c235c24": { + "filename": "Maze Land (Chromasette).zip", + "disk0": "MAZELAND.DSK", + "joy-right": "kjoy0", + "basic": "RUN\"RMAZE-GR\"\n" + }, + "8f547e5a0c2408ec3627fd67f396b6ee8550e1ef": { + "filename": "Maze Maker v3.0 (Retro Rick).zip", + "disk0": "MAZMAK30.dsk", + "joy-right": "kjoy0", + "basic": "RUN\"RMAZE-GR\"\n" + }, + "2cf88332c44f5b8485873d234cdb4c3b79189015": { + "filename": "Maze Maker v4.0 (Retro Rick).zip", + "disk0": "MAZMAK40.dsk", + "joy-right": "kjoy0", + "basic": "RUN\"RMAZE-GR\"\n" + }, + "e3b14eb103ffe22ded15a20506ffff362268f612": { + "filename": "Maze Master (Dough Tuttle) (Coco 3).zip", + "machine": "coco3", + "disk0": "MMASTER.DSK", + "joy-right": "kjoy0", + "basic": "LOADM\"MMASTER\":EXEC\n" + }, + "19d14f1eeca4636151f99f5f39c827e3a56ccc3c": { + "filename": "Maze Race (Tom Mix Software).zip", + "disk0": "MAZERACE.DSK", + "joy-left": "mjoy0", + "joy-right": "kjoy0", + "basic": "RUN\"MAZERACE\"\n" + }, + "4e3393f17af9cce9b5d8f3f9042be7cd89b145a8": { + "filename": "Mazes, The (Infotop).zip", + "disk0": "THEMAZES.DSK", + "basic": "RUN\"THE MAZE\"\n" + }, + "243b638e7e0c045c2b4157338270e6b6aec96d8c": { + "filename": "Maze, The (Colin North) (Coco 3).zip", + "machine": "coco3", + "disk0": "CRYSTAL3.DSK", + "basic": "RUN\"CRYSTAL3\"\n" + }, + "f1101cbe2e08bb53270aa0b6455b6f587fb82f0e": { + "filename": "Mazey (T&D Software).zip", + "disk0": "MAZEY.DSK", + "joy-right": "kjoy0", + "basic": "RUN\"MAZEY\"\n" + }, + "fb768b2f92ac3f80c74e5fefd13cf583d45520f0": { + "filename": "McCheckers (Greg Miller & Erik Gavriluk).zip", + "disk0": "MCCHECK.DSK", + "basic": "LOADM\"MCCHECK\":EXEC\n" + }, + "dc224dcf1c888f69e5c3a3a7373a6abdbdf0a09e": { + "filename": "Megabug (Tandy).zip", + "disk0": "MEGABUG.DSK", + "joy-right": "kjoy0", + "basic": "LOADM\"MEGABUG\":EXEC\n" + }, + "45ad19f87ae2759e28885630c9be05c0b06da02d": { + "filename": "Megamunk (Color Connection Software).zip", + "disk0": "MEGAMUNK.DSK", + "joy-right": "kjoy0", + "basic": "LOADM\"MEGAMUNK\":EXEC\n" + }, + "a29ef0c79e8ad2af56ae17ffe5edef92e8685f90": { + "filename": "Megapede (Computerware).zip", + "disk0": "MEGAPEDE.DSK", + "joy-right": "kjoy0", + "basic": "LOADM\"MEGAPEDE\":EXEC\n" + }, + "a4cddc31005d86182437137c1dc19ae2b6a12fe3": { + "filename": "Mega Shield (Winefred Washington Jr.).zip", + "disk0": "MEGASHLD.DSK", + "joy-right": "kjoy0", + "basic": "LOADM\"MEGASHLD\":EXEC\n" + }, + "f3a6b7919dd716866bf8a5772ac3a9956b16d36a": { + "filename": "Mega Tank (The Rainbow) (Coco 3).zip", + "machine": "coco3", + "disk0": "MEGATANK.DSK", + "joy-right": "kjoy0", + "basic": "RUN\"MEGATANK\"\n" + }, + "bdc883afd99ad0006ec2f43c8becc89f743af53e": { + "filename": "Memory Game, The (RAM Electronics 1988) (Coco 3).zip", + "machine": "coco3", + "disk0": "MEMORY.DSK", + "basic": "RUN\"MEMORY\"\n" + }, + "b105d2b84288a7d9e0ba4aeda52da701af918258": { + "filename": "Mercenary Force (GOSUB International).zip", + "disk0": "MERCFORC.DSK", + "basic": "RUN\"MERCFORC\"\n" + }, + "81717c7d4ebdecd2049c41f6fbc2af8f3975cb6d": { + "filename": "Merchant to the Stars (Larry M. Paroubek) (Coco 3).zip", + "machine": "coco3", + "disk0": "STARMERC.DSK", + "basic": "RUN\"STARS\"\n" + }, + "f9de1bb9e14d8efb7cf46feaf085c19431cd2ee4": { + "filename": "Microbes (Tandy).zip", + "disk0": "MICROBES.DSK", + "joy-right": "kjoy0", + "basic": "LOADM\"MICROBES\":EXEC\n" + }, + "be25a9b89b5d9df348edfa78468e2211c655c04f": { + "filename": "Micro Chess (Tandy).zip", + "disk0": "CHESS.DSK", + "basic": "LOADM\"CHESS\":EXEC\n" + }, + "e1f820a09b8cfdceb8c128edd3bd86b84eca6e99": { + "filename": "Micro Chess (PMODE 4) (Tandy).zip", + "disk0": "CHESSBW.DSK", + "basic": "LOADM\"CHESSBW\":EXEC\n" + }, + "e8affc2b71adccb0be44d206141693d47835df40": { + "filename": "Middle Kingdom (Computerware).zip", + "disk0": "KINGDOM.DSK", + "basic": "LOADM\"KINGDOM\":EXEC\n" + }, + "229c67da55a0d368d29386608c7fdf9d6ad8e8fe": { + "filename": "Midway Campaign (Avalon Hill).zip", + "disk0": "Midway.dsk", + "joy-right": "kjoy0", + "basic": "PCLEAR1:RUN\"MIDWAY\"\n" + }, + "dd96056074b21815c996f7de244785ba147a6f17": { + "filename": "Mille Bornes (HLO) (Coco port).zip", + "disk0": "MBv2.DSK", + "basic": "RUN\"MB2\"\n" + }, + "26b6e61ea4a7690cd5ffc606dd769bbec5016cff": { + "filename": "Milenium the Last Outpost (Tabaxi Software) (Coco 3).zip", + "machine": "coco3", + "disk0": "milenium.dsk", + "joy-right": "kjoy0", + "basic": "RUN\"X\"\n" + }, + "f02e5da5f08a25e87d279f0d1959bf256f44bf28": { + "filename": "Mind-Roll (Tandy) (Coco 1-2) (Coco 3).zip", + "machine": "coco3", + "disk0": "MINDROLL.DSK", + "joy-left": "kjoy0", + "basic": "RUN\"MINDROLL\"\n" + }, + "52fcc5fa564df55cb6a71144d255cfe369c8d980": { + "filename": "Mine Camp (Public Domain).zip", + "disk0": "MINECAMP.DSK", + "joy-right": "mjoy0", + "basic": "RUN\"MINECAMP\"\n" + }, + "63f89a19583736cf0e564112efa6711507d83095": { + "filename": "Minecamp, the next mission (Diego Barizo) (Coco 3).zip", + "machine": "coco3", + "disk0": "MINECMP2.DSK", + "basic": "RUN\"MC2\"\n" + }, + "ab8e79653b90bcbb817a0d9f608c8ff956a13651": { + "filename": "Mined Out (Quicksilva).zip", + "disk0": "MINEDOUT.DSK", + "basic": "RUN\"MINEDOUT\"\n" + }, + "3533784820111999dec68c0c67a7b85576d6e311": { + "filename": "Minesweeper (Tim Franklin) (Coco 3).zip", + "machine": "coco3", + "disk0": "SWEEP.DSK", + "joy-right": "mjoy0", + "basic": "LOADM\"SWEEP\":EXEC\n" + }, + "0202f927dba882330044730533688fd31c47b8c2": { + "filename": "Minigolf 3 (T&D Software) (Coco 3).zip", + "machine": "coco3", + "disk0": "MINIGOLF.DSK", + "joy-right": "kjoy0", + "basic": "RUN\"MINIGOLF\"\n" + }, + "20123b5d753ddb6d8048df7157ef1038762ac867": { + "filename": "Mine Rescue (SRB Software) (Coco 3).zip", + "machine": "coco3", + "disk0": "MINERESC.DSK", + "joy-right": "kjoy0", + "basic": "LOADM\"MINERESC\"\n" + }, + "be292e1f1b43adc8ddbbda230c28ca4b9a0d0d1b": { + "filename": "Mine Rescue (SRB Software) (Coco 3).zip", + "machine": "coco3", + "disk0": "MINERESC.DSK", + "joy-right": "kjoy0", + "basic": "LOADM\"MINERESC\"\n" + }, + "97571bcd8ae1a2fbef938d90519a2bb2c0a8fa7f": { + "filename": "Mine Rescue (SRB Software) (Coco 3).zip", + "machine": "coco3", + "disk0": "MINERESC.DSK", + "joy-right": "kjoy0", + "basic": "LOADM\"MINERESC\"\n" + }, + "5938adb70bf0ee4f1f5e6e65b012a026aefe5740": { + "filename": "Mine Rescue (SRB Software) (Coco 3).zip", + "machine": "coco3", + "disk0": "MINERESC.DSK", + "joy-right": "kjoy0", + "basic": "LOADM\"MINERESC\"\n" + }, + "bdf6244fccdfdcd6baa28396073e8513aa0fce5d": { + "filename": "Mis-Adventures of Eddie, The (Novasoft).zip", + "disk0": "eddie.dsk", + "basic": "LOADM\"EDDIE\":EXEC\n", + "tv-input": "redblue" + }, + "15235c7146dfd81f41865a49cb31d334cffdddc3": { + "filename": "Miss Gobbler (Spectral Associates).zip", + "disk0": "MGOBBLER.DSK", + "joy-right": "kjoy0", + "basic": "LOADM\"MGOBBLER\":EXEC\n" + }, + "67c8765e7ad1326906b9634c07ba794e7a635627": { + "filename": "Mission Empire (Ark Royal Games).zip", + "disk0": "empire.dsk", + "basic": "RUN\"MISSIONE\"\n" + }, + "d0340dece9d7b1c11553088f0f9392670e6845f3": { + "filename": "Miss Gobbler (Spectral Associates) (SSC).zip", + "disk0": "MISSPACV.DSK", + "joy-right": "kjoy0", + "basic": "LOADM\"MISSPACV\":EXEC\n" + }, + "06a62e7eb1d8f7a3e02d17dd4101e2fb1eebdbcc": { + "filename": "Mission of Vengeance (Saguaro Software).zip", + "disk0": "vengeanc.dsk", + "basic": "RUN\"START\"\n" + }, + "7b2a326f7cdceb89dee542b5270b74176595b712": { + "filename": "Mix-Up (Don Langkamp) (OS-9) (Coco 3).zip", + "machine": "coco3", + "disk0": "mixup.dsk", + "joy-right": "mjoy0", + "basic": "DOS\n" + }, + "ee57813fc1ac5e07be1f6e52d2d5868046a93052": { + "filename": "Module Man (Spectral Associates) (SSC).zip", + "disk0": "MODULE.DSK", + "joy-right": "kjoy0", + "basic": "LOADM\"MODULE\":EXEC\n" + }, + "03dd28ba82c960037c0619e9e50ee4ce2b3941fb": { + "filename": "Model Rocketry (David Matt).zip", + "disk0": "ROCKETRY.DSK", + "basic": "RUN\"ROCKETRY\"\n" + }, + "365702fa29d8433813e99ab5551a91ecd65cfb25": { + "filename": "Moneyopoly (Tom Mix Software).zip", + "disk0": "MONOPOLY.DSK", + "joy-left": "mjoy0", + "basic": "LOADM\"MONOPOLY\":EXEC\n" + }, + "d34fbb25c05f8e2385555cb2dbbf059be3d223a1": { + "filename": "Monkey Kong (Med Systems-Screenplay).zip", + "disk0": "MONKEYK.DSK", + "joy-right": "kjoy0", + "basic": "LOADM\"MONKEYK\":EXEC\n" + }, + "d995d3b4745282e5c478f60cc7cfd1ee196babcf": { + "filename": "Monkey Shines (T&D Software).zip", + "disk0": "MSHINE.DSK", + "joy-right": "kjoy0", + "basic": "RUN\"MONKEY\"\n" + }, + "8b3ab603910b744fd00d79c2a04a19c608a17833": { + "filename": "Monster Maze (Tandy).zip", + "disk0": "MMAZE.DSK", + "joy-right": "kjoy0", + "basic": "LOADM\"MMAZE\":EXEC\n" + }, + "556fcee2d1eeb06de894f1c42c9e771e2c55d80a": { + "filename": "Monsters & Magic (Prickly-Pear Software).zip", + "disk0": "MONSTERS.DSK", + "basic": "RUN\"MONSTERS\"\n" + }, + "a7f746b46127a700ad9db91e69361983fe6eb9ee": { + "filename": "Montezumas Dungeons (T&D Software).zip", + "disk0": "MONTEZUM.DSK", + "joy-right": "kjoy0", + "basic": "LOADM\"MONTEZUM\":EXEC\n" + }, + "59f51305bfef6be1a6968edee38cd5c6cde3259d": { + "filename": "Moon 1 (Pat Abramovitch) (Coco 3).zip", + "machine": "coco3", + "disk0": "MOON1.DSK", + "joy-right": "kjoy0", + "basic": "RUN\"MOON1\"\n" + }, + "7a011954d14a9f069a6e2fe600a16e03674490ba": { + "filename": "Moon Cresta (Incentive Software) (Coco port).zip", + "disk0": "CRESTA.DSK", + "joy-right": "kjoy0", + "basic": "LOADM\"CRESTA\":EXEC\n" + }, + "7e5459cf63ad2d3d5ee0fdb01de51d4e9f656e8f": { + "filename": "Moon Flight (Paul Griffit).zip", + "disk0": "MOONFL.DSK", + "joy-right": "kjoy0", + "basic": "RUN\"MOONFL\"\n" + }, + "54861dfcf2da6605922fad61d0785436fe2ee0fd": { + "filename": "Moonhopper (Computerware).zip", + "disk0": "MOONHOP.DSK", + "joy-right": "kjoy0", + "basic": "LOADM\"MOONHOP\":EXEC\n" + }, + "499669026e80653c24a92e9f8e7c6385a399bcdd": { + "filename": "Moon Lander (Tom Mix Software).zip", + "disk0": "MOONLAND.DSK", + "joy-right": "kjoy0", + "basic": "RUN\"LANDER\"\n" + }, + "6eae0a918060439e05ef68d5cf2b03fcc243c314": { + "filename": "Moon Shuttle (Datasoft).zip", + "disk0": "MOONSHUT.DSK", + "joy-right": "kjoy0", + "basic": "LOADM\"MOONSHUT\":EXEC\n" + }, + "467b8d9c3bba9780f028c2c1071fea3d063589f9": { + "filename": "Morocco GP (Computerware).zip", + "disk0": "MOROCCO.DSK", + "joy-right": "kjoy0", + "basic": "LOADM\"MOROCCO\":EXEC\n" + }, + "a06a0c41cc180706306792e9fbc8da62b16155cf": { + "filename": "Mortar Command (Overseas Software).zip", + "disk0": "MORTAR.DSK", + "joy-right": "mjoy0", + "basic": "RUN\"MORTAR\"\n" + }, + "8ec23a6eed58a68929655ec01d769c0d906e42c7": { + "filename": "Moria (Greg Olson).zip", + "machine": "coco3", + "disk0": "MORIA_CC3.DSK", + "joy-right": "kjoy0", + "basic": "RUN\"START\"\n" + }, + "6dea7a981457b4a606be19cfe589e91d94ee45d3": { + "filename": "Moria (Greg Olson).zip", + "machine": "coco3", + "disk0": "MORIA_CC3.DSK", + "joy-right": "kjoy0", + "basic": "RUN\"START\"\n" + }, + "a9f837cc67083fd7a4c7fc86687bdb335ce940ae": { + "filename": "Motorcycle Jump (CLOAD).zip", + "disk0": "motojump.dsk", + "basic": "RUN\"MOTOJUMP\"\n" + }, + "072904c17204893fc97732d0e5804ad686856855": { + "filename": "Mouse Maze (John Kowalski).zip", + "disk0": "mousemaz.dsk", + "basic": "RUN\"*\"\n" + }, + "7bfc7c0ea366c42451d86d2a4a5cefc52f60c1fe": { + "filename": "Mr. Dig (Computerware).zip", + "disk0": "MRDIG.DSK", + "joy-right": "kjoy0", + "basic": "LOADM\"MRDIG\":EXEC\n", + "tv-input": "redblue" + }, + "c6e95b0293293077422da5402bc7ef24fba0fad6": { + "filename": "Mr. Munch (Chromasette).zip", + "disk0": "MRMUNCH.DSK", + "joy-right": "kjoy0", + "basic": "RUN\"MRMUNCH\"\n" + }, + "6265ac3dc29c21e9219eed4fb4df545e98952d50": { + "filename": "Mr. Corey (Valkyrie Software).zip", + "disk0": "corey.dsk", + "basic": "LOADM\"COREY\":EXEC\n" + }, + "d7949533c899987effa8d2a8607edff5ebb366d0": { + "filename": "Mrs Pac (T&D Software).zip", + "disk0": "MRSPAC.DSK", + "joy-left": "kjoy0", + "basic": "LOADM\"MRSPAC\":EXEC\n" + }, + "2cb6825308eb93b7ed5d40cd3beef355f750e81f": { + "filename": "Ms. Paku (Paul Shoemaker).zip", + "disk0": "MSPAKU.DSK", + "joy-right": "kjoy0", + "basic": "LOADM\"MSPAKU\":EXEC\n" + }, + "adbe4be7f6123f0531cad424ad2b38d0f5d0fa5a": { + "filename": "Ms. Maze (Tom Mix Software).zip", + "disk0": "MS-MAZE.DSK", + "joy-right": "kjoy0", + "basic": "LOADM\"MS-MAZE\":EXEC\n" + }, + "97029a5219434f6f12e60db47d79628a9d69c639": { + "filename": "Ms. Paku (Paul Shoemaker) (Coco 3).zip", + "machine": "coco3", + "disk0": "MSPAKU3.DSK", + "joy-right": "kjoy0", + "basic": "LOADM\"MSPAKU3\":EXEC\n" + }, + "018b427ea0bf3c947c45305315097d6078a77695": { + "filename": "Mudpies (Computer Shack-Michtron).zip", + "disk0": "MUDPIES.DSK", + "joy-right": "kjoy0", + "basic": "LOADM\"MUDPIES\":EXEC\n", + "tv-input": "redblue" + }, + "35e3cb1327120820e57bbb6d21db164ffd20826f": { + "filename": "Multi-Bars Slot Machine (Tom Mix Software) (Coco 3).zip", + "machine": "coco3", + "disk0": "MULTIBAR.DSK", + "basic": "LOADM\"MULTIBAR\":EXEC\n" + }, + "9ff1a573cf3d652d78493550e96257504092cd5b": { + "filename": "Munchkin Blaster (Steve Donald).zip", + "disk0": "BLASTER.DSK", + "joy-right": "kjoy0", + "basic": "RUN\"BLASTER\"\n" + }, + "9336251a3cee54f14cb9a59c1e6b9c96ff24bd13": { + "filename": "Munchman (T&D Software).zip", + "disk0": "Munchman.dsk", + "joy-right": "kjoy0", + "basic": "LOADM\"MUNCHMAN\":EXEC\n" + }, + "0e2b5754e104d0eeeb994db793c9d27d74649368": { + "filename": "Mystery of the Java Star (Shards Software).zip", + "disk0": "JAVASTAR.DSK", + "basic": "RUN\"JAVASTAR\"\n" + }, + "28aedf818a9533ce96801b639a688e002dda8f88": { + "filename": "Nasa Pilot Training Program.zip", + "disk0": "NASA.DSK", + "basic": "RUN\"NASA\"\n" + }, + "9a3c1c1e730441d59e40b6257a3e8b622c7299ba": { + "filename": "Mystic Mansion.zip", + "disk0": "MysticM.dsk", + "joy-right": "kjoy0", + "basic": "RUN\"MYSTIC\"\n" + }, + "3324bfd8bed5352f20b8e36f5009899641e9d212": { + "filename": "Naugus, The (Spectral Associates).zip", + "disk0": "NAUGUS.DSK", + "joy-right": "kjoy0", + "basic": "LOADM\"NAUGUS\":EXEC\n" + }, + "ad4e98c7a8b56fd6ca725b0934363d9775743efe": { + "filename": "Nerble Force (Computerware).zip", + "disk0": "NERBLE.DSK", + "joy-right": "kjoy0", + "basic": "LOADM\"NERBLE\":EXEC\n" + }, + "50a53257585dc75d0392bf1b712e7a9120313c06": { + "filename": "Neutroid II (Nick Marentes) (SG-12) (Coco 1-2).zip", + "disk0": "NEUTROID.DSK", + "joy-right": "kjoy0", + "basic": "LOADM\"NEUTROID\":EXEC\n" + }, + "689c55438a4e3821edb6eaf02981fc92bab249e2": { + "filename": "Night of the Ninja (Jay Braxmaier).zip", + "disk0": "NIGHT.DSK", + "joy-left": "kjoy0", + "basic": "LOADM\"NIGHT\":EXEC\n" + }, + "985fdca7ccedf2aae389ef2101dec254595627f4": { + "filename": "Nibbler (Nelson Software Systems).zip", + "disk0": "NIBBLER.DSK", + "joy-right": "kjoy0", + "basic": "LOADM\"NIBBLER\":EXEC\n" + }, + "53aeb1ac3fb6e87884b5e304ed1f80cde00420c5": { + "filename": "Nightmare on Elm Street (Dan Larson) (Coco 3).zip", + "machine": "coco3", + "disk0": "FREDDIE.DSK", + "basic": "RUN\"FREDDIE\"\n" + }, + "b1035d15b5f636193dd021b5f4ab28001ffb9b7d": { + "filename": "Nine Card Choice (T&D Software).zip", + "disk0": "NINECARD.DSK", + "basic": "RUN\"NINECARD\"\n" + }, + "2576a8a5da2dc0e0f3698e9b834069edd78194f5": { + "filename": "Ninja Warrior (The Programmer's Guild).zip", + "disk0": "NINJA.DSK", + "joy-left": "kjoy0", + "basic": "LOADM\"NINJA\":EXEC\n" + }, + "9ba9eee7995ad4eaa39fff653fb8fbf7a64cca54": { + "filename": "Nort Chase (Alvin Kimball).zip", + "disk0": "NORT.DSK", + "basic": "RUN\"NORT\"\n" + }, + "7c949a7f2e0dbccf05f0de87e75d53d750f7d207": { + "filename": "Noids (T&D Software).zip", + "disk0": "noids.dsk", + "basic": "RUN\"NOIDLOAD\"\n" + }, + "62103e28d03d44dc0efcefae4dd8712c0bbc1908": { + "filename": "Not Another Expressway Ripoff! v1.10 (Retro Rick).zip", + "disk0": "NAER110.DSK", + "basic": "RUN\"NAER\"\n" + }, + "bdf2762fdd964705277555317fe697cb0fd1e783": { + "filename": "Nuclear Avenger (T&D Software).zip", + "disk0": "NUKEAVNG.DSK", + "joy-left": "kjoy0", + "basic": "LOADM\"NUKEAVNG\":EXEC\n" + }, + "d8c51fcb77a77b4ec4d4aa981cbd0c48fae94689": { + "filename": "Nuclear Reactor Simulator (The Rainbow).zip", + "disk0": "NUKESIM.DSK", + "joy-right": "kjoy0", + "basic": "LOADM\"NUKESIM\":EXEC\n" + }, + "f4bd2a75c5a71016c2ef905677153a08fab6602b": { + "filename": "Nova Pinball (Bumblebee Software).zip", + "disk0": "NOVA.DSK", + "basic": "LOADM\"NOVA\":EXEC\n" + }, + "01746b697b7b7040090ee9cd3c9498d7958a90d5": { + "filename": "Nuclear Submarine Adventure (Aardvark-80).zip", + "disk0": "NUCLRSUB.DSK", + "joy-right": "kjoy0", + "basic": "LOADM\"NUCLRSUB\":EXEC\n" + }, + "2f8a0847d4821a9fa5650f5939fd9bcb455ab36b": { + "filename": "Nukewar (Avalon Hill) (Coco port).zip", + "disk0": "NUKEWAR.DSK", + "joy-right": "kjoy0", + "basic": "LOADM\"NUKEWAR\":EXEC\n" + }, + "2a6b4b7536b18f7e533e3abb0eea8e37955769ef": { + "filename": "Ockywoky (Shooting Star Software).zip", + "disk0": "OCKYWOKY.DSK", + "joy-right": "kjoy0", + "basic": "LOADM\"OCKYWOKY\":EXEC\n" + }, + "e82209e86dd68d9c10f8c0ba18a8c9321d802131": { + "filename": "Offender (American Business Computers).zip", + "disk0": "OFFENDER.DSK", + "joy-right": "kjoy0", + "basic": "LOADM\"OFFENDER\":EXEC\n" + }, + "12389a289a84b8f6427b7890dfe9f6e65f9723a9": { + "filename": "Olympic Decathlon (The Rainbow).zip", + "disk0": "OLYMPICS.DSK", + "joy-right": "kjoy0", + "basic": "LOADM\"OLYMPICS\":EXEC\n" + }, + "12aee8d25e4ea9cc93f14833d42224bc6ce9868b": { + "filename": "Omniverse (Computerware) (SSC).zip", + "disk0": "Omniverse (alt).dsk", + "joy-right": "kjoy0", + "basic": "LOADM\"OMNIVERSE (ALT)\":EXEC\n" + }, + "7fbd9098c542f245bab35449c2b6e4eb40dde329": { + "filename": "Omniverse (Computerware) (SSC).zip", + "disk0": "Omniverse (alt).dsk", + "joy-right": "kjoy0", + "basic": "LOADM\"OMNIVERSE (ALT)\":EXEC\n" + }, + "2f4d25078b83fad0756da5f5cd4946219a119bcf": { + "filename": "One on One (Electronic Arts).zip", + "disk0": "1on1.dsk", + "joy-right": "kjoy0", + "basic": "LOADM\"1ON1\":EXEC\n" + }, + "a1049e19baaa50bf0deef68f0f8ddbb8635b8bfd": { + "filename": "One on One (Electronic Arts).zip", + "disk0": "1on1.dsk", + "joy-right": "kjoy0", + "basic": "LOADM\"1ON1\":EXEC\n" + }, + "24bdbbb4da5bc4a7df2b56758dbcec16580013dd": { + "filename": "Ono (Charles Bartlett) (coco 3).zip", + "disk0": "UNO.DSK", + "joy-right": "kjoy0", + "basic": "LOADM\"UNO\":EXEC\n" + }, + "02bb470c11da7e304423cc96145006c7e90d72dc": { + "filename": "Ono (Charles Bartlett) (Coco 3).zip", + "machine": "coco3", + "disk0": "UNO.DSK", + "joy-right": "kjoy0", + "basic": "LOADM\"UNO\":EXEC\n" + }, + "86661d6fd061466372ff238887fc42f56a2112e8": { + "filename": "Oodles (Paul Shoemaker).zip", + "disk0": "OODLES.DSK", + "joy-right": "kjoy0", + "basic": "LOADM\"OODLES\":EXEC\n" + }, + "4a31714c9ef95b7dee757a90e1ba310f739b6333": { + "filename": "Operation Barbarossa (Victor Koss).zip", + "disk0": "OPERBARB.DSK", + "joy-right": "kjoy0", + "basic": "LOADM\"OPERBARB\":EXEC\n" + }, + "39dd28830e50aff4871c0c8f564b8617689a90cf": { + "filename": "Operation Dark Horse (Ark Royal Software).zip", + "disk0": "horse.dsk", + "joy-right": "kjoy0", + "basic": "RUN\"HORSE\"\n" + }, + "57f507f0f75f4f40b6e1b3d4e98415a43337a736": { + "filename": "Operation Redstar.zip", + "disk0": "REDSTAR.DSK", + "joy-right": "kjoy0", + "basic": "LOADM\"REDSTAR\":EXEC\n" + }, + "68a3442f14793ddce74d680b421f1ba27d446699": { + "filename": "Nuke the Love Boat (Computerware ) (Coco 3).zip", + "machine": "coco3", + "disk0": "nuke2.dsk", + "joy-right": "kjoy0", + "basic": "LOADM\"NUKE2\":EXEC\n" + }, + "2490404ba2541b914f5d0d60003d6e003ada16e7": { + "filename": "Nuke the Love Boat (Computerware ) (Coco 3).zip", + "machine": "coco3", + "disk0": "nuke2.dsk", + "joy-right": "kjoy0", + "basic": "LOADM\"NUKE2\":EXEC\n" + }, + "1ebcfea9266bdae3e8ff600be1483c0a8b77c7c4": { + "filename": "Nuke the Love Boat (Computerware ) (Coco 3).zip", + "machine": "coco3", + "disk0": "nuke2.dsk", + "joy-right": "kjoy0", + "basic": "LOADM\"NUKE2\":EXEC\n" + }, + "dc80ab86c878eefdcf1d3a1348ca3198f2ddad5c": { + "filename": "Nuke the Love Boat (Computerware ) (Coco 3).zip", + "machine": "coco3", + "disk0": "nuke2.dsk", + "joy-right": "kjoy0", + "basic": "LOADM\"NUKE2\":EXEC\n" + }, + "63cc2690d51e7723b46da1881a06cb3f7a0bbc5e": { + "filename": "Nuke the Love Boat (Computerware ) (Coco 3).zip", + "machine": "coco3", + "disk0": "nuke2.dsk", + "joy-right": "kjoy0", + "basic": "LOADM\"NUKE2\":EXEC\n" + }, + "e27b7c278f04815b269f564eb987dd52be279667": { + "filename": "Orbitroids (Creative Computing Software).zip", + "disk0": "orbitor.dsk", + "joy-right": "kjoy0", + "basic": "LOADM\"ORBITOR\":EXEC\n" + }, + "0367e324e19dbeb4b90ad0aff24148f0d8934104": { + "filename": "Orbitron (AHL Computing).zip", + "disk0": "ORBITRON.DSK", + "joy-right": "kjoy0", + "basic": "LOADM\"ORBITRON\":EXEC\n" + }, + "1bf9193789890e8274df2a152a8781b8bf16f958": { + "filename": "Oregon Trail (Chris Pedersen).zip", + "disk0": "OREGON.DSK", + "joy-right": "kjoy0", + "basic": "LOADM\"OREGON\":EXEC\n" + }, + "c39422e2379b4b0cacf9fdd223f49aae1dc3faea": { + "filename": "Othello (alt) (Public Domain).zip", + "disk0": "OTHELLO.DSK", + "joy-right": "kjoy0", + "basic": "LOADM\"OTHELLO\":EXEC\n" + }, + "620194f9508d015ac7b6e7b6a17c8673056f1a25": { + "filename": "Othello (Public Domain).zip", + "disk0": "OTHELLO.DSK", + "joy-right": "kjoy0", + "basic": "LOADM\"OTHELLO\":EXEC\n" + }, + "36a6d0988c68326f4e986d3beae490aa76859fea": { + "filename": "Otho 3.6 (Alain Dussault).zip", + "disk0": "OTHO36.DSK", + "joy-right": "kjoy0", + "basic": "LOADM\"OTHO36\":EXEC\n" + }, + "d1080ff1a73828e7c5984c40461fc7c0c616e0fc": { + "filename": "Outhouse (Computer Shack).zip", + "disk0": "OUTHOUSE.DSK", + "joy-right": "kjoy0", + "basic": "LOADM\"OUTHOUSE\":EXEC\n" + }, + "650210ccb4cb430415567065598fe2742cda5dc1": { + "filename": "Overlord (Oblique Triad) (Coco 3).zip", + "machine": "coco3", + "disk0": "overlord.dsk", + "joy-right": "kjoy0", + "basic": "LOADM\"OVERLORD\":EXEC\n" + }, + "6383f8e6efbac41f9ba16cbceeac0db51deb15ea": { + "filename": "Oxxo (Star Kits).zip", + "disk0": "OXXO.DSK", + "joy-right": "kjoy0", + "basic": "LOADM\"OXXO\":EXEC\n" + }, + "d8dc4f71882e88fd1cc6ca60c195c6f59741aead": { + "filename": "P-51 Mustang Attack (Tom Mix Software).zip", + "disk0": "P-51.DSK", + "joy-right": "kjoy0", + "basic": "LOADM\"P-51\":EXEC\n" + }, + "9d66c52928f696101938cfaf378abb9cec86239b": { + "filename": "Pac Rat (Steven Dufresne).zip", + "disk0": "pacrat.dsk", + "basic": "RUN\"PACRAT\"\n" + }, + "a6d9a804ad95caa269ad0f9721ba2d2767b2a82a": { + "filename": "Pac Droids (The Programmer's Guild) (SG-12 intro).zip", + "disk0": "PACDROID.DSK", + "basic": "LOADM\"PACDROID\":EXEC\n" + }, + "d532bbb63376234bdffdeade12807e09c97b2eef": { + "filename": "Pac Droids (The Programmer's Guild) (SG-12 intro).zip", + "disk0": "PACDROID.DSK", + "basic": "LOADM\"PACDROID\":EXEC\n" + }, + "239b9d2b23a57f628542be7bd1c82e48db9ffc65": { + "filename": "Pacdude Monster Maze 2.0A (Brian O'Neill) (Coco 3).zip", + "machine": "coco3", + "disk0": "pacdude2.dsk", + "joy-left": "kjoy0", + "basic": "LOADM\"PACDUDE2\":EXEC\n" + }, + "36160c75d4b1e28ea7e28ad04eaaf9233cb961b2": { + "filename": "Pacdude 1.0 (Brian O'Neill) (Coco 3).zip", + "machine": "coco3", + "disk0": "pacdude.dsk", + "joy-left": "kjoy0", + "basic": "LOADM\"PACDUDE\":EXEC\n" + }, + "2387320fa60807190be7d40649764f02543f316b": { + "filename": "Pacdude 1.0 (Brian O'Neill) (Coco 3).zip", + "machine": "coco3", + "disk0": "pacdude.dsk", + "joy-left": "kjoy0", + "basic": "LOADM\"PACDUDE\":EXEC\n" + }, + "091580496af6566a8d6312e31cb52e0667ccf7f5": { + "filename": "Pacdude 1.3 with source (Brian O'Neill) (Coco 3).zip", + "machine": "coco3", + "disk0": "PACDUDE.DSK", + "joy-left": "kjoy0", + "basic": "LOADM\"PACDUDE\":EXEC\n" + }, + "a5a233ba7eb8d315cd9ac7a32fe7af5c992ed563": { + "filename": "Packet Man (American Business Computers).zip", + "disk0": "PACKMAN.DSK", + "joy-right": "kjoy0", + "basic": "LOADM\"PACKMAN\":EXEC\n" + }, + "23d6c3ff93466922fde0bf497f5c7379273fd318": { + "filename": "Pack Maze (DSL Computer Products).zip", + "disk0": "PACKMAZE.DSK", + "joy-left": "kjoy0", + "basic": "LOADM\"PACKMAZE\":EXEC\n" + }, + "911f38cf2fee48077b55cf21db5793e8fe7447fa": { + "filename": "Pacdude Monster Maze 2.1A (Brian O'Neill) (Coco 3).zip", + "machine": "coco3", + "disk0": "PACDUDE2.DSK", + "joy-left": "kjoy0", + "basic": "LOADM\"PAC2\":EXEC\n" + }, + "cdb6479b1db36fe53e3ce332e7fbbbb6c1faed47": { + "filename": "Pacman (Bruce Mackenzie).zip", + "disk0": "PACMAN.DSK", + "joy-left": "kjoy0", + "basic": "LOADM\"PACMAN\":EXEC\n" + }, + "d60a10f049ab67cb7f5998a7d5d9f7cf3fd8cfee": { + "filename": "Pac-Man Demo (Nick Marentes) (Coco 3).zip", + "machine": "coco3", + "disk0": "pacdemo.dsk", + "joy-right": "kjoy0", + "basic": "RUN\"PACDEMO\"\n" + }, + "08e7533612964cb159122117d2f61cfb08c9c1ba": { + "filename": "Pacman CoCo 3 Transcode (Glen Hewlett) (Coco 3).zip", + "machine": "coco3", + "disk0": "PACMAN.DSK", + "joy-right": "kjoy0", + "basic": "RUN\"PACMAN\"\n" + }, + "05adfd262b5408f2dce93013f79e5df897fd0f4e": { + "filename": "Pacoman (S. Bernier & G. Poirier).zip", + "disk0": "PACOMAN.DSK", + "joy-right": "kjoy0", + "basic": "LOADM\"PACOMAN\":EXEC\n" + }, + "4c8bf1ded4cc0767d68626cb4afdf6aaf4b29f88": { + "filename": "Pac-Tac (Computerware) (SG-12) (Coco 1-2).zip", + "disk0": "PAC-TAC.DSK", + "joy-right": "kjoy0", + "basic": "LOADM\"PAC-TAC\":EXEC\n" + }, + "080ff52877e4aeb1940edd636e8be79b5380d46d": { + "filename": "Pac-Panic (Cougar Software).zip", + "disk0": "PAKPANIC.DSK", + "joy-left": "kjoy0", + "basic": "LOADM\"PAKPANIC\":EXEC\n" + }, + "b19325575fbdbb381824c5dceb4f114e6b0751c1": { + "filename": "Pac-Tac II (Computerware).zip", + "disk0": "PAC-TAC2.DSK", + "joy-right": "kjoy0", + "basic": "LOADM\"PAC-TAC2\":EXEC\n" + }, + "fa4e0bc2771f4899a6ba61533bf4145f19eeb8a0": { + "filename": "Paku Paku (Paul Shoemaker).zip", + "machine": "coco3", + "disk0": "PAKUPAKU.DSK", + "joy-right": "kjoy0", + "basic": "LOADM\"PAKUPAKU\":EXEC\n" + }, + "e81b4b0115dd3fd233b7a7e77d519d49fc4eedc4": { + "filename": "Pac-OS9 (Larry Olson) (OS-9) (Coco 3).zip", + "machine": "coco3", + "disk0": "PACOS9.DSK", + "joy-right": "kjoy0", + "basic": "DOS\n" + }, + "fb0b753576bba305080157b898ef452996ac744c": { + "filename": "Pac-OS9 (Larry Olson) (OS-9) (Coco 3).zip", + "machine": "coco3", + "disk0": "PACOS9.DSK", + "joy-right": "kjoy0", + "basic": "DOS\n" + }, + "c72432101940428382483aaeb0f8b62ecd83326d": { + "filename": "Panic Button (Tandy).zip", + "disk0": "PANIC.DSK", + "joy-left": "kjoy0", + "basic": "LOADM\"PANIC\":EXEC\n" + }, + "61b2dc1f8a59461343185d0eb5c8ae5cf9375d03": { + "filename": "Panic (Tom Mix Software).zip", + "disk0": "PANIC.DSK", + "joy-left": "kjoy0", + "basic": "LOADM\"PANIC TM\":EXEC\n" + }, + "7b0ba203ee1e531cb706ad8d83285b6a16d53685": { + "filename": "Paper Route (Diecom Products).zip", + "disk0": "PAPER.DSK", + "joy-left": "kjoy0", + "basic": "LOADM\"PAPER\":EXEC\n", + "tv-input": "redblue" + }, + "0f4f931c328d3fffd75282c415311c98427f7686": { + "filename": "Panzer (T&D Software).zip", + "disk0": "PANZER.DSK", + "joy-right": "kjoy0", + "basic": "LOADM\"PANZER\":EXEC\n" + }, + "45bd4bd973af8f7bfea43d2855f832dee76a85a9": { + "filename": "Para-Jumper (The Rainbow).zip", + "disk0": "PARACHUT.DSK", + "basic": "RUN\"PARACHUT\"\n" + }, + "3558967c336167458218567cc7c1dd3c6db1ab71": { + "filename": "Patti-Pak (Petrocci Freelance Associates).zip", + "disk0": "PATTIPAK.DSK", + "joy-right": "kjoy0", + "basic": "LOADM\"PATTIPAK\":EXEC\n" + }, + "3263cf3d3e4b81d831d1c16f573c42ade1ce5fb6": { + "filename": "Peg Jump (T&D Software).zip", + "disk0": "PEGJUMP.DSK", + "basic": "RUN\"PEGJUMP\"\n" + }, + "ff0a2a0dd9e54137dea3bd8793c3b86d55f54749": { + "filename": "Pedro (Imagine).zip", + "disk0": "PEDRO.DSK", + "joy-right": "kjoy0", + "basic": "LOADM\"PEDRO\":EXEC\n" + }, + "79dbf68d38d20bdd9f6a6f2512425816370832f0": { + "filename": "Pegasus and the Phantom Riders (Tandy) (SSC).zip", + "disk0": "Pegasus.dsk", + "joy-right": "kjoy0", + "basic": "RUN\"PEGASUS\"\n" + }, + "8ca38ccf954b3986f55d020103b98a86dc1663d3": { + "filename": "Pegasus and the Phantom Riders (Tandy) (SSC).zip", + "disk0": "Pegasus.dsk", + "joy-right": "kjoy0", + "basic": "RUN\"PEGASUS\"\n" + }, + "3459df2b9288f7e6e0420fc985117082b93a951b": { + "filename": "Pelieti (Roger Taylor) (Coco 3).zip", + "machine": "coco3", + "disk0": "PELIETI.DSK", + "basic": "LOADM\"PELIETI\":EXEC\n" + }, + "683be58171844b25901b52814b51b7c82a016cb8": { + "filename": "Pendant of Death (Wayne Kely).zip", + "disk0": "Pendant.dsk", + "basic": "RUN\"PENDANTD\"\n" + }, + "0000c383d026eebb23abd8963b8839202b03bdd1": { + "filename": "Pengon (Spectral Associates).zip", + "disk0": "PENGON.DSK", + "joy-right": "kjoy0", + "basic": "LOADM\"PENGON\":EXEC\n", + "tv-input": "redblue" + }, + "6cb4af9ab99c358da89c5c04f42c0091cde2f68f": { + "filename": "Penguin Icarus (The Rainbow) (Coco 3).zip", + "machine": "coco3", + "disk0": "PENGUIN.DSK", + "joy-right": "kjoy0", + "basic": "RUN\"PENGUIN\"\n" + }, + "0c02f54c55219fb46c71b46325dfb821df1b82bb": { + "filename": "Pennypede (Chromasette) (SG-6) (Coco 1-2).zip", + "disk0": "PENIPEDE.DSK", + "basic": "LOADM\"PENIPEDE\":EXEC\n" + }, + "3f5c7013e655aa31602e58445538079fdd27aeeb": { + "filename": "Penguin (The Rainbow).zip", + "disk0": "PENGUIN.DSK", + "basic": "RUN\"PENGUIN\"\n" + }, + "d8629257dd46355ebd43a8755f2f69dc5504374a": { + "filename": "Phantom Slayer (Med Systems).zip", + "disk0": "PHANTOMS.DSK", + "basic": "LOADM\"PHANTOMS\":EXEC\n" + }, + "2488ac04d42b566b9bd89ed2e9aba0c7f50b9fc6": { + "filename": "Petals Around the Rose (Aaron Sebold) (Coco 3).zip", + "machine": "coco3", + "disk0": "petals.dsk", + "basic": "RUN\"PETALS\"\n" + }, + "7bf02bac0e97700a9ec8080187b42595602abe76": { + "filename": "Petals Around the Rose (Aaron Sebold) (Coco 3).zip", + "machine": "coco3", + "disk0": "petals.dsk", + "basic": "RUN\"PETALS\"\n" + }, + "6be5b3a5e1f3c64cc80f66bc1ce45dd938eb5a31": { + "filename": "Phoenix Project, The (Donald S. Cathcart) (Coco 3).zip", + "machine": "coco3", + "disk0": "PHOENIX.DSK", + "basic": "RUN\"PHOENIX.INS\"\n" + }, + "cbc9f882a6acf5b02abf7789df5a17b5c028e749": { + "filename": "Pick Which (Spectral Associates).zip", + "disk0": "PWHICH.DSK", + "joy-right": "mjoy0", + "basic": "LOADM\"PWHICH\":EXEC\n" + }, + "3d596e65d81818d9fc41a9a6a9ba946dbd9f0566": { + "filename": "Picnic (Creative Computing Software).zip", + "disk0": "PICNIC.DSK", + "joy-right": "kjoy0", + "basic": "LOADM\"PICNIC\":EXEC\n" + }, + "a416b7c8d3ce9b397626c0fa0ab3e802391125aa": { + "filename": "Picture Puzzles (JR & JR Softstuff) (Coco 3).zip", + "machine": "coco3", + "disk0": "PPUZZ.DSK", + "basic": "LOADM\"PPUZZ\":EXEC\n" + }, + "c40008f838e9a216d1aadcc7b2bd6eab60167603": { + "filename": "Piggy (Spectral Associates).zip", + "disk0": "PIGGY.DSK", + "joy-right": "kjoy0", + "basic": "LOADM\"PIGGY\":EXEC\n" + }, + "66dd74ecdeab2552a47642f4d9bbf72276787865": { + "filename": "Pinball (Anteco).zip", + "disk0": "ANTECPIN.DSK", + "joy-right": "kjoy0", + "basic": "LOADM\"ANTECPIN\":EXEC\n" + }, + "8196d572091fff5a597479774462ca07a31bf854": { + "filename": "Photon (Patched for emulators) (Sundog Systems) (Coco 3).zip", + "machine": "coco3", + "disk0": "Photon (Patched for emulators) (Sundog Systems) (Coco 3)/photon.dsk", + "joy-right": "kjoy0", + "basic": "LOADM\"PHOTON\"\n" + }, + "d33cba935a73250945f3ed48527ea739a101742e": { + "filename": "Photon (Patched for emulators) (Sundog Systems) (Coco 3).zip", + "machine": "coco3", + "disk0": "Photon (Patched for emulators) (Sundog Systems) (Coco 3)/photon.dsk", + "joy-right": "kjoy0", + "basic": "LOADM\"PHOTON\"\n" + }, + "c4a9663c41ddca1964109def5e3dc48ff62db7fc": { + "filename": "Pinball Factory (Michtron).zip", + "disk0": "PINBALL.DSK", + "basic": "RUN\"PINBALL\"\n" + }, + "151ac8a40230f1eb3e1890dcd19c6cbfcaa7e926": { + "filename": "Pirate Adventure.zip", + "disk0": "PIRATES.DSK", + "basic": "RUN\"PIRATES\"\n" + }, + "39f3f072f460c40d349617bdfad9cd5e19712b48": { + "filename": "Pit Fiend (Pocket Money Software).zip", + "disk0": "PITFIEND.DSK", + "basic": "LOADM\"PITFIEND\":EXEC\n" + }, + "6a5fd2a76fb09b4e4794041e39e1e5072be92aae": { + "filename": "Pirate's Treasure.zip", + "disk0": "Pirates Treasure.dsk", + "joy-right": "mjoy0", + "basic": "RUN\"PIRATE\"\n", + "tv-input": "redblue" + }, + "e6c23517484be8cfa51db4b2a271f05a9b5c72f0": { + "filename": "Pitstop 2 (Epyx).zip", + "disk0": "pitstop2.dsk", + "basic": "RUN\"*\"\n", + "tv-input": "redblue" + }, + "3b4bad31c4cd9eda399d5e23638b5152f0812f2f": { + "filename": "Planet Conquest (International Software).zip", + "disk0": "PLANCONQ.DSK", + "joy-right": "kjoy0", + "basic": "LOADM\"PLANCONQ\":EXEC\n" + }, + "2fe2fe689534e31339af0328de29faaf26c50fb6": { + "filename": "Planet Raider (Aardvark-80).zip", + "disk0": "PLNTRAID.DSK", + "joy-right": "kjoy0", + "basic": "LOADM\"PLNTRAID\":EXEC\n" + }, + "46bad782c9b97b2a905d166028f9d6216e915f43": { + "filename": "Pitfall 2 (Tandy) (SSC).zip", + "disk0": "pitfall2.dsk", + "joy-right": "kjoy0", + "basic": "RUN\"PITFALL2\"\n", + "tv-input": "redblue" + }, + "6e2d2b22ab84d3e257789ea99ce93a0ffcfbb51f": { + "filename": "Pitfall 2 (Tandy) (SSC).zip", + "disk0": "pitfall2.dsk", + "joy-right": "kjoy0", + "basic": "RUN\"PITFALL2\"\n", + "tv-input": "redblue" + }, + "e01baef95a5e1cbc05ca18159ea298ff6d8bec09": { + "filename": "Pitfall 2 (Tandy) (SSC).zip", + "disk0": "pitfall2.dsk", + "joy-right": "kjoy0", + "basic": "RUN\"PITFALL2\"\n", + "tv-input": "redblue" + }, + "6e2c9fe33a3c1ea20da29c59454f613e08355e1b": { + "filename": "Pitfall 2 (Tandy) (SSC).zip", + "disk0": "pitfall2.dsk", + "joy-right": "kjoy0", + "basic": "RUN\"PITFALL2\"\n", + "tv-input": "redblue" + }, + "b9acb9ec4a618527dcdc5685ecd36d107f4d36f0": { + "filename": "Planet X (Tony Stoneberger).zip", + "disk0": "PLANETX.DSK", + "basic": "RUN\"PLANET X\"\n" + }, + "b9fd6625de9ef912111ad88a85ecde19e42f9cbb": { + "filename": "Plateau of the Past (Zytek).zip", + "disk0": "plateau.dsk", + "basic": "LOADM\"POP\"\n", + "tv-input": "redblue" + }, + "52ed84e4dcaa539844e609ed064ec40cd3cc0fff": { + "filename": "Plunkit (Roger Taylor) (Coco 3).zip", + "machine": "coco3", + "disk0": "PLUNKIT.DSK", + "basic": "RUN\"PLUNKIT\"\n" + }, + "f66889f04466c1453658beb66e063e4df0cc6d9f": { + "filename": "Poker Squares (Original Edition) (Paul Shoemaker) (Coco 3).zip", + "machine": "coco3", + "disk0": "PSQUARES.DSK", + "basic": "RUN\"RUNME\"\n" + }, + "47fe89fac1574d5fae45fed2fe8fa7906fd82de2": { + "filename": "Poker Squares (Original Edition) (Paul Shoemaker).zip", + "disk0": "PSQUARE2.DSK", + "basic": "RUN\"RUNME\"\n" + }, + "ce7ab46840923477a8a5b0f02cc947a040a9c752": { + "filename": "Poker Squares (Paul Shoemaker) (Coco 3).zip", + "machine": "coco3", + "disk0": "PSQUARE5.DSK", + "basic": "RUN\"RUNME\"\n" + }, + "88f3b66a86d96cc4b55347970391c8161a418bcd": { + "filename": "Poker Squares (Paul Shoemaker).zip", + "disk0": "PSQUARE4.DSK", + "basic": "RUN\"RUNME\"\n" + }, + "5852be58e2857310ebbbe7e9bf28afb0dc32f100": { + "filename": "Polaris (Tandy).zip", + "disk0": "POLARIS.DSK", + "joy-right": "mjoy0", + "basic": "LOADM\"POLARIS\":EXEC\n" + }, + "be8e018ab157bdd338ff8c8b6cfcbd034dd09e4e": { + "filename": "Poltergeist (Tandy).zip", + "disk0": "POLTER.DSK", + "joy-left": "kjoy0", + "basic": "LOADM\"POLTER\":EXEC\n" + }, + "75872a744c6cfd6aea6de27d384a6b0f205d407e": { + "filename": "Pond, The (Tandy).zip", + "disk0": "ThePond.dsk", + "basic": "LOADM\"POND\":EXEC\n" + }, + "accb7f9a98e5fb682554186b6eca9baeb73a5c38": { + "filename": "Pong (Public Domain).zip", + "disk0": "PONG.DSK", + "joy-left": "mjoy0", + "joy-right": "kjoy0", + "basic": "LOADM\"PONG\":EXEC\n" + }, + "9b774810f7a2b87b44591457fb806b7f2b475668": { + "filename": "Ponk (Greg Helton) (Coco 3).zip", + "machine": "coco3", + "disk0": "PONK.DSK", + "joy-left": "mjoy0", + "joy-right": "kjoy0", + "basic": "LOADM\"PONK\":EXEC\n" + }, + "650f1f063c0eb4e14b09c03644d4dadd37e67f51": { + "filename": "Pooyan (Tandy).zip", + "disk0": "POOYAN.DSK", + "joy-right": "kjoy0", + "basic": "LOADM\"POOYAN\":EXEC\n" + }, + "c06173efe4024167c78d8fe2b7f266ca9f900807": { + "filename": "Pooyan (with intro) (Tandy).zip", + "disk0": "POOYAN.DSK", + "joy-right": "kjoy0", + "basic": "LOADM\"POOYAN\":EXEC\n" + }, + "2bcb3fe9ba35926830ce603b522610c4c97ea097": { + "filename": "Popcorn (Tandy).zip", + "disk0": "POPCORN.DSK", + "joy-right": "kjoy0", + "basic": "LOADM\"POPCORN\":EXEC\n" + }, + "5afccc0b076c18c2a2789e67c59ca05757c44df0": { + "filename": "Possum Run (Hot Coco).zip", + "disk0": "POSSUM.DSK", + "joy-right": "kjoy0", + "basic": "RUN\"POSSUM\"\n" + }, + "eb2202854dae0f16caff88faf5533343c1e6958c": { + "filename": "Predator (Tandy) (Coco 3).zip", + "machine": "coco3", + "disk0": "predator.dsk", + "joy-right": "kjoy0", + "basic": "RUN\"PRED\"\n" + }, + "63e9bc714330ef6577524d021eac0ccbe10dfbb9": { + "filename": "Power Stones of Ard, The (Three C's Projects) (Coco 3).zip", + "machine": "coco3", + "disk0": "STONES1.DSK", + "basic": "LOADM\"ARD\":EXEC\n" + }, + "50a946d2655b6f02803f4792f50e7ddb62db14f8": { + "filename": "Project Nebula (Tandy).zip", + "disk0": "NEBULA.DSK", + "joy-right": "kjoy0", + "basic": "LOADM\"NEBULA\":EXEC\n" + }, + "041de4d0de877b9ad95f5630180aa85cdc3d49e4": { + "filename": "Prospector (The Rainbow).zip", + "disk0": "PROSPECT.DSK", + "joy-right": "kjoy0", + "basic": "LOADM\"PROSPECT\":EXEC\n" + }, + "f73d9ae052cd1d235173b2016d95449de5451b9f": { + "filename": "Pro Golf (Computerware).zip", + "disk0": "PRO-GOLF-COMPUTERWARE.dsk", + "basic": "RUN\"GOLF\"\n", + "tv-input": "redblue" + }, + "2cc96d0a75be276e2d014c83714949171b9d1df8": { + "filename": "Pro Golf (Computerware).zip", + "disk0": "PRO-GOLF-COMPUTERWARE.dsk", + "basic": "RUN\"GOLF\"\n", + "tv-input": "redblue" + }, + "7c1f682f8d5315208170e6e8f223dae0cc5f238c": { + "filename": "Protector II (Synapse Software) (SG-24) (Coco 1-2).zip", + "disk0": "PROTECTR.DSK", + "joy-right": "kjoy0", + "basic": "LOADM\"PROTECTR\":EXEC\n" + }, + "79d6c6564d975f7e5d71174ef4d90a9035569046": { + "filename": "Proto 7 (Loren J. Howell) (Coco 3).zip", + "machine": "coco3", + "disk0": "PROTO7.DSK", + "basic": "RUN\"PROTO7\"\n" + }, + "ed175e5bdf50b0b05716e430e2703ed2b09c61da": { + "filename": "Protectors (Tom Mix Software).zip", + "disk0": "PROTECTO.DSK", + "joy-right": "kjoy0", + "basic": "LOADM\"PROTECTO\":EXEC\n" + }, + "83bdd31d441be79831bafbcf7006fd783d1ac897": { + "filename": "Pub Crawl (B&H Software).zip", + "disk0": "PUB.DSK", + "basic": "LOADM\"PUB\":EXEC\n" + }, + "ff0641faa48bdeb0ad3f64ee092fcc2c9b9ec298": { + "filename": "Pursuit (Tandy) (Coco 3).zip", + "machine": "coco3", + "disk0": "PURSUIT.DSK", + "joy-right": "kjoy0", + "basic": "LOADM\"PURSUIT\":EXEC\n" + }, + "9f2bfa95113cf51ce92b5a2e13b8b3bb7bdb1315": { + "filename": "Pumpman (Saguaro Software).zip", + "disk0": "PUMPMAN.DSK", + "joy-left": "kjoy0", + "basic": "LOADM\"PUMPMAN\":EXEC\n", + "tv-input": "redblue" + }, + "df0bb154f214159b5bc51e41512d290b5f39d3ab": { + "filename": "Puzzle Madness (TRS-80 Computing).zip", + "disk0": "puzlemad.dsk", + "basic": "RUN\"BOOT\"\n" + }, + "6fc867c458901200c7e2a17b81c1ef866e2243e4": { + "filename": "Puzzler (Color Connection Software).zip", + "disk0": "puzzler.dsk", + "basic": "LOADM\"PUZZLER\":EXEC\n" + }, + "ace8892bee5091f09cbfec5387047aa4bfb04731": { + "filename": "Puzzles (Rick's Computer Enterprise) (Coco 3).zip", + "machine": "coco3", + "disk0": "PUZZLES.DSK", + "joy-right": "mjoy0", + "basic": "RUN\"PUZZLES\"\n" + }, + "7a7890dbcefb7149118bffdc4a920b7c9542a520": { + "filename": "Puzzle.zip", + "disk0": "puzzle.dsk", + "basic": "RUN\"PUZZLE\"\n" + }, + "580c973ad0898893152c9fce651b6bcbb0611a36": { + "filename": "Pyramid 2000 (Tandy).zip", + "disk0": "PYRAMID.DSK", + "basic": "LOADM\"PYRAMID\":EXEC\n" + }, + "b6bb5bd4e6aeff11621228f140fdd3e6739d5e59": { + "filename": "Pyramid (Aardvark-80).zip", + "disk0": "pyramid.dsk", + "basic": "RUN\"PYRAMID\"\n" + }, + "6ceababce4f414a72114bd17a057fd8eb91803fc": { + "filename": "Pyramid Solitaire (ColorSystems) (OS-9) (Coco 3).zip", + "machine": "coco3", + "disk0": "pyramid.dsk", + "joy-right": "mjoy0", + "basic": "DOS\n" + }, + "1d170528e27e3daf878dbefb594e29d565f6bb6b": { + "filename": "Qazimodo (Cable Soft).zip", + "disk0": "QAZIMODO.DSK", + "joy-right": "kjoy0", + "basic": "LOADM\"QAZIMODO\":EXEC\n" + }, + "610e719c09ce02993568482574bfeffb94fe168b": { + "filename": "Pyramix (Colorventure) (Coco 3).zip", + "machine": "coco3", + "disk0": "PYRAMIX.DSK", + "joy-right": "kjoy0", + "basic": "LOADM\"PYRAMIX\":EXEC\n" + }, + "d71064b7d2919f69338b08d023f811fe25c71833": { + "filename": "Pyramix (Colorventure) (Coco 3).zip", + "machine": "coco3", + "disk0": "PYRAMIX.DSK", + "joy-right": "kjoy0", + "basic": "LOADM\"PYRAMIX\":EXEC\n" + }, + "2198d9784954d29b01ac4b0c7731f2414c4219f7": { + "filename": "QBee (Cload).zip", + "disk0": "QBEE.DSK", + "joy-right": "kjoy0", + "basic": "RUN\"QBEE\"\n" + }, + "eed54d54d87716f5ba83e0609f976ff52502b815": { + "filename": "Q-Bert Redux (Dragon User) (Enhanced by sixxie).zip", + "disk0": "q-bert.dsk", + "joy-right": "kjoy0", + "basic": "RUN\"Q*BERT%2B\"\n" + }, + "ef9db458e5490eaca6d3f8e8a92e66af4dd75064": { + "filename": "Qiks (Spectral Associates) (SSC).zip", + "disk0": "QIKSV.DSK", + "joy-right": "kjoy0", + "basic": "LOADM\"QIKSV\":EXEC\n" + }, + "f7724ad549fe919dd231a031f17ae23cdd617d6d": { + "filename": "Q-Man (Genesis Software).zip", + "disk0": "QMAN.DSK", + "joy-right": "kjoy0", + "basic": "LOADM\"QMAN\":EXEC\n" + }, + "17162fa4615607567d2ca4e3a3ef156646ffa928": { + "filename": "Quasar Commander (Tandy).zip", + "disk0": "QUASAR.DSK", + "joy-left": "mjoy0", + "joy-right": "kjoy0", + "basic": "LOADM\"QUASAR\":EXEC\n" + }, + "8965af68f1021219da9ecb52d74b680ace406dcf": { + "filename": "Que Bit (Mike Ro Products).zip", + "disk0": "QUEBIT.DSK", + "joy-right": "kjoy0", + "basic": "LOADM\"QUEBIT\":EXEC\n" + }, + "d4e3dbbf09aeaf68aa0d8022ddde087fb6b44d6b": { + "filename": "Quest (Aardvark).zip", + "disk0": "QUEST.DSK", + "joy-right": "kjoy0", + "basic": "PCLEAR1:RUN\"QUEST\"\n" + }, + "e410bbc21703ef186b219610b1e3944a04e518fe": { + "filename": "Quest (Dragon Data).zip", + "disk0": "QUEST.DSK", + "joy-right": "kjoy0", + "basic": "PCLEAR1:RUN\"QUEST\"\n" + }, + "9dc0b0c43f9858be57793298d15fc2d28965270e": { + "filename": "Quest for Fire (Sanyo and the CIP) (Coco port by JimG).zip", + "disk0": "FIREWAR.DSK", + "basic": "RUN\"FIREWAR\"\n" + }, + "b69e338e62665a5483e67fbb3e86b8622ff40369": { + "filename": "Quest (Aardvark) (alt).zip", + "disk0": "QUEST.DSK", + "joy-right": "kjoy0", + "basic": "PCLEAR1:RUN\"QUEST\"\n" + }, + "f9b9c3bdeff89cdd293e92df508ce1014941b720": { + "filename": "Quix (Tom Mix Software).zip", + "disk0": "QUIX.DSK", + "joy-left": "kjoy0", + "basic": "LOADM\"QUIX\":EXEC\n" + }, + "e4bc944b6757d1c7ac1928861c69d0ef9c903a30": { + "filename": "Raaka-Tu (Tandy).zip", + "disk0": "RAAKATU.DSK", + "basic": "LOADM\"RAAKATU\":EXEC\n" + }, + "d09f15d245fddf0b8f6a1a57aaf3c4f7dae0e515": { + "filename": "Racer Ball (Microdeal Ltd).zip", + "disk0": "RACEBALL.DSK", + "joy-right": "kjoy0", + "basic": "LOADM\"RACEBALL\":EXEC\n" + }, + "25a862d0547edbcd9c28bed99b74fe0c8171e244": { + "filename": "Radio Ball (Tandy).zip", + "disk0": "RAD-BALL.DSK", + "basic": "LOADM\"RAD-BALL\":EXEC\n" + }, + "0dd06ecb0941eaed13f0765c2b25d646900c31bb": { + "filename": "Rad Warrior (Epyx) (Coco 3).zip", + "machine": "coco3", + "disk0": "RAD.DSK", + "joy-left": "kjoy0", + "basic": "LOADM\"RAD\":EXEC\n" + }, + "4fc0f7d1b6f7bd713011f027ec621f21a2f759bb": { + "filename": "Rail Runner (Computerware) (SG-24) (Coco 1-2).zip", + "disk0": "RAILRUN.DSK", + "basic": "LOADM\"RAILRUN\":EXEC\n" + }, + "ed03fad96f5ec5c1a4684794974e5b85c6e1932d": { + "filename": "Rainbow Roach (The Rainbow).zip", + "disk0": "RBWROACH.DSK", + "joy-right": "kjoy0", + "basic": "RUN\"ROACHD\"\n" + }, + "bc7fb735aea4aeb999d57132d819087b9dd10e4f": { + "filename": "Rally (Tino Delbourgo).zip", + "disk0": "RALLY.DSK", + "joy-right": "kjoy0", + "basic": "LOADM\"RALLY\":EXEC\n" + }, + "f33808357a4577a8d58f2a3fb79b866878de7b30": { + "filename": "Rambo (T&D Software).zip", + "disk0": "rambo.dsk", + "joy-right": "kjoy0", + "basic": "RUN\"RAMBO\"\n" + }, + "b3e72f71a8ab6a70ccbd124f0236b4ba76437def": { + "filename": "Rampage (Tandy) (Coco 3).zip", + "machine": "coco3", + "disk0": "rampage.dsk", + "joy-right": "kjoy0", + "basic": "RUN\"RAMPAGE\"\n" + }, + "75de6265012f33b3ffc9bc698e3ea312c6790d78": { + "filename": "Rat Attack (T&D Software).zip", + "disk0": "RAT.DSK", + "joy-right": "kjoy0", + "basic": "LOADM\"RAT\":EXEC\n" + }, + "ab2a18038f9f80114f066940a95c3c4600a7df35": { + "filename": "Reactoid (Tandy).zip", + "disk0": "REACTOID.DSK", + "joy-right": "kjoy0", + "basic": "LOADM\"REACTOID\":EXEC\n" + }, + "0bd2a2bc89ed89d298029ed8e4bfba0a44a748f1": { + "filename": "Realm of Nauga, The (Thomas E. Stephenson).zip", + "disk0": "NAUGA.DSK", + "joy-right": "kjoy0", + "basic": "RUN\"NAUGAINS\"\n" + }, + "d510820c2ae5071a3c5432ec8982f869152d779d": { + "filename": "Rear Guard (Adventure International).zip", + "disk0": "REARGARD.DSK", + "joy-right": "kjoy0", + "basic": "LOADM\"REARGARD\":EXEC\n" + }, + "55025e961c55f38aea99ec9fe2c1501f4395b806": { + "filename": "Recochet.zip", + "disk0": "RECOCHET.DSK", + "joy-right": "kjoy0", + "basic": "LOADM\"RECOCHET\":EXEC\n" + }, + "3571d6db27cb87efe0ccf3212c07eec3bbb0cb5f": { + "filename": "Red Dog (T&D Software).zip", + "disk0": "REDDOG.DSK", + "basic": "RUN\"REDDOG\"\n" + }, + "9028081494111cf73117d64f6b973a3ccfae1f3f": { + "filename": "Rescue on Alpha II (The Rainbow).zip", + "disk0": "RESCUE.DSK", + "basic": "RUN\"BOOT\"\n" + }, + "f8b29cee35ead14aeae86cf33e4a3e158525fae0": { + "filename": "Rescue on Alpha II (The Rainbow).zip", + "disk0": "RESCUE.DSK", + "basic": "RUN\"BOOT\"\n" + }, + "da277419bee783560aaeeee40484edd2d7ca7830": { + "filename": "Rescue on Fractalus (Epyx) (OS-9) (6309 optimized) (Coco 3).zip", + "machine": "coco3h", + "disk0": "RESCUE-6309.DSK", + "joy-left": "kjoy0", + "basic": "DOS\n" + }, + "714e1c885c6d5e71a397bfa6ebbfe115063fea98": { + "filename": "Rescue on Fractalus (Epyx) (OS-9) (Coco 3).zip", + "machine": "coco3", + "disk0": "RESCUE.DSK", + "joy-left": "kjoy0", + "basic": "DOS\n" + }, + "e964af77c7853c56598c84a5f94969e9a364c84d": { + "filename": "Rescue on Fractalus (Epyx) (OS-9) (6809 optimized) (Coco 3).zip", + "machine": "coco3", + "disk0": "RESCUE-6809.DSK", + "joy-left": "kjoy0", + "basic": "DOS\n" + }, + "966f6105e59ded257bc2d372f3ee2e10f149faf1": { + "filename": "Recue, The (Floyd Resler) (OS-9) (Coco 3).zip", + "machine": "coco3", + "disk0": "tank.dsk", + "joy-right": "kjoy0", + "basic": "DOS\n" + }, + "22efe7b5d7b6fe82c30a975ccb90aa77408f9835": { + "filename": "Recue, The (Floyd Resler) (OS-9) (Coco 3).zip", + "machine": "coco3", + "disk0": "tank.dsk", + "joy-right": "kjoy0", + "basic": "DOS\n" + }, + "2c13155641886723d9910acac57cc0a250bffe6e": { + "filename": "Recue, The (Floyd Resler) (OS-9) (Coco 3).zip", + "machine": "coco3", + "disk0": "tank.dsk", + "joy-right": "kjoy0", + "basic": "DOS\n" + }, + "3998d4dcee4b9e70bd200f576c08dd73e529084f": { + "filename": "Restaurant (Dabid Mann).zip", + "disk0": "RESTRANT.DSK", + "basic": "RUN\"RESTRANT\"\n" + }, + "4f265bd1c4173e6c8eb2b7be010857783abfe393": { + "filename": "Return of Junior’s Revenge (Computerware) (Coco 3).zip", + "machine": "coco3", + "disk0": "JUNIORS.DSK", + "joy-right": "kjoy0", + "basic": "LOADM\"JUNIORS\":EXEC\n" + }, + "36725f01c614032e900886550e04fb8b38ef0416": { + "filename": "Return of the Jet-I (Thundervision) (SG-8 intro).zip", + "disk0": "JET-I.DSK", + "joy-right": "kjoy0", + "basic": "LOADM\"JET-I\":EXEC\n" + }, + "81f006beaed58d1d585a8eb3113064fb3a9fc61e": { + "filename": "Revenge of the Alien Bongo Beast (Kouga) (Coco port).zip", + "disk0": "BONGO.DSK", + "joy-right": "kjoy0", + "basic": "LOADM\"BONGO\":EXEC\n" + }, + "45f88cad30db9f439af3298aa8b8f82cd56f6282": { + "filename": "Revenge of the Mutant Miners (JR & JR Softstuff) (Coco 3).zip", + "machine": "coco3", + "disk0": "REVMINER.DSK", + "joy-right": "kjoy0", + "basic": "LOADM\"REVENGE\":EXEC\n" + }, + "d03dda8a6dfa20298e9fd590764ae1d4d720f434": { + "filename": "Revenge of the Germs (The Software System).zip", + "disk0": "GERMS.DSK", + "basic": "RUN\"GERMS\"\n" + }, + "0e086fde0f410cc7c0d6c32bd567fc36633945d2": { + "filename": "Reverse (Donald Clerk).zip", + "disk0": "REVERSE.DSK", + "basic": "RUN\"REVERSE\"\n" + }, + "7ddbd0f66114b48482e6a10f4dfc53c4fc2ae59e": { + "filename": "Reversi (Scott Nudds).zip", + "disk0": "REVERSI.DSK", + "basic": "RUN\"REVERSI\"\n" + }, + "42aeadec0bfc3d11e99320da2d65c6766ea0000f": { + "filename": "Revolution (InterAction).zip", + "disk0": "Revolution.dsk", + "joy-right": "kjoy0", + "basic": "RUN\"%2B\"\n" + }, + "628866feab4abf470b0fea8ea8e8280f04cd6f74": { + "filename": "Riddle of Randomar, The (Robert Poppe).zip", + "disk0": "RANDOMAR.DSK", + "basic": "RUN\"BOOT\"\n" + }, + "abc159b315e9de0d3e65c15efbfa6ed0f449b491": { + "filename": "Ring Quest (L. Curtis Boyle).zip", + "disk0": "RNGQUEST.DSK", + "basic": "RUN\"BOOT\"\n" + }, + "82157da36fe6ebf03efe87858c3c43ad350144ce": { + "filename": "Ringquest (The Rainbow).zip", + "disk0": "RNGQUEST.DSK", + "basic": "RUN\"RNGQUEST\"\n" + }, + "05dbb9fff56260ff4f6400ddb7bcdcdda8b05940": { + "filename": "River of Fire (J. Morrison Micros) (Coco port).zip", + "disk0": "RIVER.DSK", + "basic": "LOADM\"RIVER\":EXEC\n" + }, + "b8c9eef2e5ea18773b6d93e373ccf86b47bfac81": { + "filename": "RL Video Slot Machine.zip", + "disk0": "RITELEFT.DSK", + "joy-right": "kjoy0", + "basic": "LOADM\"RITELEFT\":EXEC\n" + }, + "090eeb9a7dfda29643ab1650d536dab2cb8af79d": { + "filename": "Robin Hood (Pocket Money Software) (Coco port).zip", + "disk0": "ROBIN.DSK", + "joy-right": "kjoy0", + "basic": "LOADM\"ROBIN\":EXEC\n" + }, + "ec80417a6343f9c9e549014bdeecbb869cd79480": { + "filename": "Robo Hang (The Peninsula Color Computer Club) (Coco 3).zip", + "machine": "coco3", + "disk0": "ROBOHANG.DSK", + "basic": "RUN\"BOOT\"\n" + }, + "497a7488fdacddd6b7905ebc2115e894b4150c34": { + "filename": "Robotack (Intracolor).zip", + "disk0": "ROBOTACK.DSK", + "joy-right": "kjoy0", + "basic": "LOADM\"ROBOTACK\":EXEC\n" + }, + "5404c52e7f3cfd7a37a8695d78f960aa8e5e3a80": { + "filename": "Robotron (Glen Hewlett) (Coco Port) (Coco 3).zip", + "machine": "coco3", + "disk0": "ROBOTRON.DSK", + "joy-left": "kjoy0", + "joy-right": "mjoy0", + "basic": "RUN\"ROBOTRON\"\n" + }, + "297e0b8b487b626a743871e200d83b2cd3bd877f": { + "filename": "Robocop (Data East) (Coco 3).zip", + "machine": "coco3", + "disk0": "ROBOCOP.DSK", + "joy-right": "kjoy0", + "basic": "RUN\"ROBOCOP\"\n" + }, + "82e06e1dd1b5f392580adf1f7409565a47766e52": { + "filename": "Robot Battle (Spectral Associates).zip", + "disk0": "ROBOT.DSK", + "joy-right": "kjoy0", + "basic": "LOADM\"ROBOT\":EXEC\n" + }, + "cd6680148651c20f6be8a6b460bb16ceb2708153": { + "filename": "Robot Bomber (T&D Software).zip", + "disk0": "ROBOTBMR.DSK", + "joy-left": "mjoy0", + "joy-right": "kjoy0", + "basic": "LOADM\"ROBOTBMR\":EXEC\n" + }, + "8711e0d874c5caf71dd57b75bc735cdb57af9ffb": { + "filename": "Robots (T&D Software).zip", + "disk0": "ROBOTSTD.DSK", + "basic": "RUN\"ROBOTS\"\n" + }, + "a5117b4078879bedc9ed841ab2c72d5ad386bdd3": { + "filename": "Roman Checkers (Tandy).zip", + "disk0": "ROMAN.DSK", + "basic": "LOADM\"ROMAN\":EXEC\n" + }, + "964ac994349b50584ad6a8d344b8a5524334de78": { + "filename": "Roller Controller (Spectral Associates) (Coco 3).zip", + "machine": "coco3", + "disk0": "ROLLER.DSK", + "joy-right": "kjoy0", + "basic": "RUN\"ROLLER\"\n" + }, + "c17462e8ceea4d47d684a3ee0a729f981b72806c": { + "filename": "Roller Controller (Spectral Associates) (Coco 3).zip", + "machine": "coco3", + "disk0": "ROLLER.DSK", + "joy-right": "kjoy0", + "basic": "RUN\"ROLLER\"\n" + }, + "3a922761a7ec571381395013f5702085273cb07b": { + "filename": "Rogue (Tandy) (OS-9) (Coco 3).zip", + "machine": "coco3", + "disk0": "ROGUE512.DSK", + "basic": "DOS\n" + }, + "9071a6e17fc33ff84cc3ff6afc858726c35a7c1a": { + "filename": "Rogue (Tandy) (OS-9) (Coco 3).zip", + "machine": "coco3", + "disk0": "ROGUE512.DSK", + "basic": "DOS\n" + }, + "3aac28ae6f0e9c73bf726f14e2049b07ded53842": { + "filename": "Rommel 3D (Michtron).zip", + "disk0": "ROMMEL3D.DSK", + "joy-right": "kjoy0", + "basic": "RUN\"ROMMEL\"\n", + "tv-input": "redblue" + }, + "14a1583a9a2f6813b55337f5267c48748d83e02d": { + "filename": "Rommel's Revenge (Design Design) (NTSC).zip", + "disk0": "ROMMELS.DSK", + "joy-right": "kjoy0", + "basic": "LOADM\"ROMMELS\":EXEC\n" + }, + "a7bdc46388f3cecdd6c4cbad394b029f4026ae3a": { + "filename": "Rommel's Revenge (Design Design) (PAL).zip", + "machine": "coco", + "disk0": "ROMMELS.DSK", + "joy-right": "kjoy0", + "basic": "LOADM\"ROMMELS\":EXEC\n" + }, + "e2e9fe79aa53d80a32cf79133ce78575567f3b6c": { + "filename": "Roulette (The Rainbow).zip", + "disk0": "roulette.dsk", + "basic": "RUN\"ROULETTE\"\n" + }, + "e31b87e00505865b4315e9a1ef20d67499f82482": { + "filename": "Rowboat (Chromasette).zip", + "disk0": "ROWBOAT.DSK", + "joy-left": "mjoy0", + "joy-right": "kjoy0", + "basic": "RUN\"ROWBOOT\"\n" + }, + "91aa5abb9cd100b84acc4b34b03c534929ac1a05": { + "filename": "Rubicon II (Ark Royal Games).zip", + "disk0": "RUBICON2.DSK", + "basic": "PCLEAR1:RUN\"RUBIIY\"\n" + }, + "9efdc88d8a3d2a791a7e3d05e8649af473faeca8": { + "filename": "Ruby Robba (Blaby) (Coco port).zip", + "disk0": "ROBBA.DSK", + "joy-right": "kjoy0", + "basic": "LOADM\"ROBBA\":EXEC\n" + }, + "1499aff2fc7b82d3907349ba940828e3100ef84a": { + "filename": "Run Dino Run (Paul Fiscarelli) (Coco 3).zip", + "machine": "coco3", + "disk0": "DINORUN.DSK", + "joy-right": "kjoy0", + "basic": "LOADM\"DINORUN\":EXEC\n" + }, + "632cf8db261dc0b3f56e8b23b31c93fdc3b40343": { + "filename": "Roulette (T&D Software).zip", + "disk0": "RouletTD.dsk", + "basic": "RUN\"ROULETTE\"\n" + }, + "2349b4d2c36fdfe129b8b3e860f4fd85f8bc90d6": { + "filename": "Run For Your Life (The Rainbow).zip", + "disk0": "RUNLIFE.DSK", + "joy-right": "kjoy0", + "basic": "RUN\"RUNLIFE\"\n" + }, + "9657f411a4a5d66943d28f2f8736f21b3adff303": { + "filename": "Rupert Rythm (Tandy, Game Point Software) (Coco 3).zip", + "machine": "coco3", + "disk0": "RUPERT.DSK", + "joy-right": "kjoy0", + "basic": "RUN\"RUPERT\"\n" + }, + "7241d80b92f8108ec8f12bc45c452f8a3cbfc82d": { + "filename": "Rush'n Assault (Diecom Products) (6809 optimized).zip", + "disk0": "RUSH69.DSK", + "joy-left": "kjoy0", + "basic": "LOADM\"RUSH\"\n", + "tv-input": "redblue" + }, + "0f71cdb5fa3537779e2d4361df0b8b0a51a98f7b": { + "filename": "Saigon (Adventure International).zip", + "disk0": "SAIGON.DSK", + "basic": "LOADM\"SAIGON\":EXEC\n" + }, + "5160bd11953ed49c8c10762ee28af73d358e8203": { + "filename": "Rush'n Assault (Diecom Products).zip", + "disk0": "RUSH.DSK", + "joy-left": "kjoy0", + "basic": "LOADM\"RUSH\"\n", + "tv-input": "redblue" + }, + "fe8a52212d7e3e3a6e60a411fe4a23a62f98d609": { + "filename": "Rush'n Assault (Diecom Products).zip", + "disk0": "RUSH.DSK", + "joy-left": "kjoy0", + "basic": "LOADM\"RUSH\"\n", + "tv-input": "redblue" + }, + "55aa2fdc3176ba003650dee1f7dabe5976262c16": { + "filename": "Sailor Man (Tom Mix Software).zip", + "disk0": "Sailor Man.dsk", + "joy-right": "kjoy0", + "basic": "RUN\"SAILOR\"\n", + "tv-input": "redblue" + }, + "273cf37dca99ee9dc4dd6a1a4d72b142346b2abd": { + "filename": "Sailor Man (Tom Mix Software).zip", + "disk0": "Sailor Man.dsk", + "joy-right": "kjoy0", + "basic": "RUN\"SAILOR\"\n", + "tv-input": "redblue" + }, + "7670847c05e45096b19278348195dea0fab5a002": { + "filename": "Sam Diamond PI (Moreton Bay Software).zip", + "disk0": "SAMDIAM.DSK", + "basic": "LOADM\"CRIME\":EXEC\n" + }, + "9159472767ed08e3ae5185caa89234db0bee3259": { + "filename": "Sand Rover (T&D Software).zip", + "disk0": "ROVER.DSK", + "joy-right": "kjoy0", + "basic": "LOADM\"ROVER\":EXEC\n" + }, + "9d30a819adb708a1217719c3485eee58f9b49498": { + "filename": "Sands of Egypt (Tandy).zip", + "disk0": "sands.dsk", + "joy-right": "kjoy0", + "basic": "RUN\"EGYPT\"\n", + "tv-input": "redblue" + }, + "8fd28f2f2ce2b06b6aafac8bb3b6530db3da40b2": { + "filename": "Sands of Egypt (Tandy).zip", + "disk0": "sands.dsk", + "joy-right": "kjoy0", + "basic": "RUN\"EGYPT\"\n", + "tv-input": "redblue" + }, + "246f3179c717536daba83e498e85763a1dd4320a": { + "filename": "Scarfman (The Cornsoft Group).zip", + "disk0": "SCARFMAN.DSK", + "joy-right": "kjoy0", + "basic": "LOADM\"SCARFMAN\":EXEC\n" + }, + "6fa84fdf4ea74f8472d705bc176fda1a36db2cd8": { + "filename": "Sam Sleuth (Computerware).zip", + "disk0": "Sam Sleuth (alt 1).dsk", + "joy-right": "kjoy0", + "basic": "LOADM\"SLEUTH1\":EXEC\n", + "tv-input": "redblue" + }, + "aaed6fc39909778afa48afe48e90bbaa6147f6b5": { + "filename": "Sam Sleuth (Computerware).zip", + "disk0": "Sam Sleuth (alt 1).dsk", + "joy-right": "kjoy0", + "basic": "LOADM\"SLEUTH1\":EXEC\n", + "tv-input": "redblue" + }, + "9205fe43eaa8606bf7bf3739e0152f74aa807cab": { + "filename": "Sam Sleuth (Computerware).zip", + "disk0": "Sam Sleuth (alt 1).dsk", + "joy-right": "kjoy0", + "basic": "LOADM\"SLEUTH1\":EXEC\n", + "tv-input": "redblue" + }, + "f84aed32b1c40d9073776008a262717a0d19b560": { + "filename": "Scepter of Kzirgla (Rainbow Connection Software).zip", + "disk0": "KZIRGLA.DSK", + "basic": "RUN\"SCEPTER\"\n" + }, + "fb93329d08828ae8aaefa328a9cb8fddd30dbda0": { + "filename": "Scrabble (Eversoft Games) (Coco 3).zip", + "machine": "coco3", + "disk0": "Scrabble.dsk", + "basic": "RUN\"SCRABBLE\"\n" + }, + "dba0d680b80149874d1f1117ef2db371f80e6296": { + "filename": "Scrabble (T&D Software).zip", + "disk0": "SCRABBLE.DSK", + "basic": "RUN\"SCRABBLE\"\n" + }, + "e5eff04e77566cdd45df354fe7c73becfc3775c0": { + "filename": "Scramble (Tom Mix Software).zip", + "disk0": "SCRAMBLE.DSK", + "basic": "LOADM\"SCRAMBLE\":EXEC\n" + }, + "f973be7174a8fc6fbe6e46852bc17622ac4b008f": { + "filename": "Scepter of Ursea (Prickly Pear Software).zip", + "disk0": "URSEA.DSK", + "basic": "RUN\"URSEA\"\n" + }, + "ddcf273816889e89ca56a0f382d99a2061871e79": { + "filename": "Scepter of Ursea (Prickly Pear Software).zip", + "disk0": "URSEA.DSK", + "basic": "RUN\"URSEA\"\n" + }, + "f03818d283741d789b32c481408fe77bdd5ff56e": { + "filename": "Scramble (Tom Mix Software) (alt).zip", + "disk0": "SKRAMBLE.DSK", + "joy-right": "kjoy0", + "basic": "LOADM\"SKRAMBLE\":EXEC\n" + }, + "c0831b2d4e850a8a4230b79d8e5f5c8a68f1f932": { + "filename": "Sea Dragon (Adventure International) (alt).zip", + "disk0": "SEADRAGN.DSK", + "joy-right": "kjoy0", + "basic": "LOADM\"SEADRAGN\":EXEC\n" + }, + "0a8ce0a26bf46a35a2fbbc2dccaa27d1c1ea0cf1": { + "filename": "Sea Quest (Mark Data Products).zip", + "disk0": "SEAQUEST.DSK", + "basic": "LOADM\"SEAQUEST\":EXEC\n", + "tv-input": "redblue" + }, + "0d6dadd7913c11a3914c55d8f7166e8d12c3355b": { + "filename": "Sea Dragon (Adventure International).zip", + "disk0": "SEADRAG.DSK", + "joy-right": "kjoy0", + "basic": "LOADM\"SEADRAG\":EXEC\n" + }, + "11d16f91ddd9976be521a8d00fbafe8f29d9c05f": { + "filename": "Search for Almazar, The (Hot Coco).zip", + "disk0": "almazar.dsk", + "basic": "RUN\"ALMAZAR\"\n" + }, + "220dfca446a445386b1ab38e7ced763b09287cbf": { + "filename": "Searching for Daryl (Tim Hartnell) (Coco 3).zip", + "machine": "coco3", + "disk0": "DARYL.DSK", + "basic": "RUN\"DARYL\"\n" + }, + "07885083b6ac50eab8e639b5bded5fb7387c258b": { + "filename": "Sea Search (Mark Data Products).zip", + "disk0": "SEASEARC.DSK", + "basic": "LOADM\"SEASEARC\":EXEC\n", + "tv-input": "redblue" + }, + "e7ae301d2f670ad05d9016d3cc80a88eec0dbb13": { + "filename": "Secret Agent Man (David L. Dawson).zip", + "disk0": "AGENT.DSK", + "basic": "RUN\"AGENT\"\n" + }, + "3f60b201fc028d957e8b1ba1f28cc335918dc791": { + "filename": "Sea War (The Rainbow).zip", + "disk0": "SEAWAR.DSK", + "joy-right": "kjoy0", + "basic": "RUN\"SEAWAR\"\n" + }, + "ed2324601b1aebcdba854ed27d822207d807104a": { + "filename": "Sex & Violence (Circle City Software).zip", + "disk0": "SEX&VIOL.DSK", + "basic": "RUN\"SEX&VIOL\"\n" + }, + "531f7aadccad134db6ba0f7373b803377a260b04": { + "filename": "Seiddab Attack (Hewson Consultants Ltd) (Coco port).zip", + "disk0": "SEIDDAB.DSK", + "joy-right": "kjoy0", + "basic": "LOADM\"SEIDDAB\":EXEC\n" + }, + "657401fdc1d33700cb21c085c857af8a6b23cb4c": { + "filename": "Shaft (Prickly-Pear Software).zip", + "disk0": "SHAFT.DSK", + "joy-right": "kjoy0", + "basic": "LOADM\"SHAFT\":EXEC\n" + }, + "4fb1f75680bd980775caeeae702f1c1664ec885d": { + "filename": "Shanghai (Tandy) (Coco 3).zip", + "machine": "coco3", + "disk0": "SHANGHAI.DSK", + "joy-right": "mjoy0", + "basic": "LOADM\"SHANGHAI\":EXEC\n" + }, + "108c23e9d74a82084736359c10b73ddae5c5a879": { + "filename": "Shamus (Tandy).zip", + "disk0": "SHAMUS.DSK", + "joy-right": "kjoy0", + "basic": "LOADM\"SHAMUS\":EXEC\n" + }, + "a2f297180efbb636005fb635d05323c3693f0560": { + "filename": "Shamus (Tandy).zip", + "disk0": "SHAMUS.DSK", + "joy-right": "kjoy0", + "basic": "LOADM\"SHAMUS\":EXEC\n" + }, + "bba008213ec975fc5c6d76c38118c303dc44035a": { + "filename": "Shark Treasure (Computerware).zip", + "disk0": "SHARK.DSK", + "joy-left": "kjoy0", + "basic": "LOADM\"SHARK\":EXEC\n" + }, + "90c8a9fa7c5f44ab3e6650ca9417bc91c3501e45": { + "filename": "Shenanigans (Mark Data Products).zip", + "disk0": "SHENANI.DSK", + "basic": "LOADM\"SHENANI\":EXEC\n", + "tv-input": "redblue" + }, + "64ea6b0bd7f582b021545b396bc39b4851fa41cf": { + "filename": "Shanghai (Earther Enterprises) (OS-9) (Coco 3).zip", + "machine": "coco3", + "disk0": "shanghai.dsk", + "joy-right": "mjoy0", + "basic": "DOS\n" + }, + "4845d9ca909e69a4008a754d36fcae86ee53d5f6": { + "filename": "Shooting Gallery (Tandy).zip", + "disk0": "SHOOT.DSK", + "joy-right": "kjoy0", + "basic": "LOADM\"SHOOT\":EXEC\n" + }, + "c33319a3cb17f2e9b8a73f767d1ad09921f8011d": { + "filename": "Shiphunt (Free Coco Software).zip", + "disk0": "SHIPHUNT.DSK", + "basic": "LOADM\"SHIPHUNT\":EXEC\n" + }, + "000da96d5f1436e8837fc5cc2584f0202bbc5c6d": { + "filename": "Shock Trooper (Cheat) (Mark Data Products).zip", + "disk0": "TROOPER.DSK", + "joy-right": "kjoy0", + "basic": "LOADM\"TROOPER\":EXEC\n" + }, + "dac1cf81a705ad3eb8d6f1c7d2eb53ef10762ab5": { + "filename": "Shock Trooper (Mark Data Products).zip", + "disk0": "SHOCK.DSK", + "joy-right": "kjoy0", + "basic": "LOADM\"SHOCK\":EXEC\n" + }, + "70021c33058f03a6d1a6f600c86c949b1d203c6f": { + "filename": "Shoot'n Range (Albert Marsh) (Coco 3).zip", + "machine": "coco3", + "disk0": "SHOOTN.DSK", + "joy-right": "mjoy0", + "basic": "RUN\"SHOOTN\"\n" + }, + "208a31539c72c8baf13840bc016390254bb93134": { + "filename": "Shootout (Justin Lipton).zip", + "disk0": "SHOOTOUT.DSK", + "joy-right": "kjoy0", + "basic": "LOADM\"SHOOTOUT\":EXEC\n" + }, + "353e7dee76a8000417f3453051663e71bb67a4ea": { + "filename": "Shootout at the OK Galaxy (Avalon Hill) (SG-8) (Coco 1-2).zip", + "disk0": "SHOOTOUT.DSK", + "joy-right": "kjoy0", + "basic": "RUN\"BOOT\"\n" + }, + "425f5d2a676cb856629d3bd7d14ea0f22c7facb1": { + "filename": "Shuffle (Phil Beistel).zip", + "disk0": "SHUFFLE.DSK", + "basic": "RUN\"SHUFFLE\"\n" + }, + "b0426a6c7760a97128866bb62fbffaf430c33729": { + "filename": "Shuttle Simulator (Tom Mix Software).zip", + "disk0": "SHUTTLE.DSK", + "basic": "LOADM\"SHUTTLE\":EXEC\n" + }, + "3edd9a08264da9682deddb9a4e2d4e42c386b455": { + "filename": "Shrink (Star-Kits).zip", + "disk0": "SHRINK.DSK", + "basic": "LOADM\"SHRINK\":EXEC\n" + }, + "d9b6295083a6fc168094f19d7ce2aef42543d71b": { + "filename": "Sidney (KLG Systems) (Coco 3).zip", + "machine": "coco3", + "disk0": "sidney.dsk", + "joy-right": "kjoy0", + "basic": "RUN\"SIDNEY\"\n" + }, + "ddd1f04ef690181e46cc548ef18df9e3d009346a": { + "filename": "Skiing (Tandy).zip", + "disk0": "SKIING.DSK", + "joy-right": "kjoy0", + "basic": "LOADM\"SKIING\":EXEC\n" + }, + "2b9adf87f3ff54778a6db5a8672ce3416e092c16": { + "filename": "Skid-Row Adventures (Microdeal).zip", + "disk0": "skidrow.dsk", + "basic": "RUN\"SKID-ROW\"\n" + }, + "b0906eb7fcdcb1ff42c40d565b3a4fc99ff90bff": { + "filename": "Simply Better Blackjack (Alison deNu).zip", + "disk0": "SIMJACK2.DSK", + "basic": "RUN\"SIMJACK2\"\n" + }, + "650ccafa61e7a17f02ae8f2af68a61dfae4f9c06": { + "filename": "Sky-Defense (Quasar Animations).zip", + "disk0": "SKY-DEF.DSK", + "joy-left": "kjoy0", + "basic": "LOADM\"SKY-DEF\":EXEC\n" + }, + "3c182b14f4e3cad7a1e4e06cc784cacae4a3d95f": { + "filename": "Silpheed (Tandy) (Coco 3).zip", + "machine": "coco3", + "disk0": "SILPHEED.DSK", + "joy-right": "kjoy0", + "basic": "RUN\"SILPHEED\"\n" + }, + "9007b389004a00f6f2d3e2c6884d2f2d7b12d255": { + "filename": "Skyway (Tom Mix Software).zip", + "disk0": "SKYWAY.DSK", + "joy-right": "kjoy0", + "basic": "LOADM\"SKYWAY\":EXEC\n", + "tv-input": "redblue" + }, + "db7bdb477b15caff33c4bd9835d76044fcd51923": { + "filename": "Slashball (Aardvark).zip", + "disk0": "SLASH.DSK", + "basic": "RUN\"SBALL\":EXEC\n" + }, + "1d863c92af898849b04ef23e590da560332ccbcd": { + "filename": "Slay the Nereis (Tandy).zip", + "disk0": "NEREIS.DSK", + "joy-right": "kjoy0", + "basic": "LOADM\"NEREIS\":EXEC\n" + }, + "612534b9901048d3bf3c4c4181e3a288b8aa20f5": { + "filename": "Slither (Public Domain).zip", + "disk0": "SLITHER.DSK", + "joy-right": "kjoy0", + "basic": "LOADM\"SLITHER\":EXEC\n" + }, + "459a65fa278e70ec83f0f58b7f6d7592d61233e5": { + "filename": "Slots (Public Domain).zip", + "disk0": "SLOTS.DSK", + "basic": "LOADM\"SLOTS\":EXEC\n" + }, + "72589f1517f4f6552c148a4e2533b15b831880da": { + "filename": "Smash (T&D Software).zip", + "disk0": "SMASH.DSK", + "joy-right": "kjoy0", + "basic": "LOADM\"SMASH\":EXEC\n" + }, + "12021e90311eb76a1e693f0426aae24923e73283": { + "filename": "Sluzzle (John W. Linville) (Coco 3).zip", + "machine": "coco3", + "disk0": "sluzzle.dsk", + "basic": "RUN\"SLUZZLE\"\n" + }, + "d9c4ad1b85e4d4eb2feaf01c21bcdd69023845e7": { + "filename": "Snafus (T&D Software).zip", + "disk0": "snafus.dsk", + "joy-right": "kjoy0", + "basic": "LOADM\"SNAFUS\":EXEC\n" + }, + "16ebf557e2faefc52ac9dadd8abe02310f62f4b2": { + "filename": "Slots & Cards (MicroDeal USA) (Coco 3).zip", + "machine": "coco3", + "disk0": "SLOTCRD3.DSK", + "joy-right": "kjoy0", + "basic": "DOS\n" + }, + "389340745e5fe60819a1aa7a512f0f94b5f4a244": { + "filename": "Slots & Cards (MicroDeal USA) (Coco 3).zip", + "machine": "coco3", + "disk0": "SLOTCRD3.DSK", + "joy-right": "kjoy0", + "basic": "DOS\n" + }, + "0d36b13968bdbcb8ff97a234af5bda30e5cd3683": { + "filename": "Slots & Cards (MicroDeal USA) (Coco 3).zip", + "machine": "coco3", + "disk0": "SLOTCRD3.DSK", + "joy-right": "kjoy0", + "basic": "DOS\n" + }, + "cb646b4625b659fa1e86e48c83232c19d9910aae": { + "filename": "SOKO64+ (Emanuele Feronato & Marco Spedaletti).zip", + "disk0": "SOKO64.DSK", + "joy-left": "kjoy0", + "basic": "LOADM\"SOKO64\":EXEC\n" + }, + "27afd0ee9f7efb7151469229dac9d45d9665314d": { + "filename": "Sneaky Snake (Peter Kerckhoff).zip", + "disk0": "SNEAKY.DSK", + "joy-left": "kjoy0", + "basic": "RUN\"SNEAKY\"\n" + }, + "a3999a12ee1fe491a6447698fd826f15e8706617": { + "filename": "SnakeByte (Domenic Troilo) (OS-9) (Coco 3).zip", + "machine": "coco3", + "disk0": "snakebyte.dsk", + "basic": "DOS\n" + }, + "e936b44be3c4217ba38621f195b0c435005be170": { + "filename": "Snak-Pac (Tom Mix Software).zip", + "disk0": "SNAKPAC.DSK", + "joy-right": "kjoy0", + "basic": "LOADM\"SNAKPAC\":EXEC\n" + }, + "817277a8dd878a4279c50bc674aef5560c6e4233": { + "filename": "SOKO64+ (Emanuele Feronato & Marco Spedaletti) (Coco 3).zip", + "machine": "coco3", + "disk0": "SOKO64C3.DSK", + "joy-left": "kjoy0", + "basic": "RUN\"LOADER\"\n" + }, + "2e4837c50519129b9aa13625b1a4ddd86678c9d5": { + "filename": "Sokoban (Tandy) (Coco 1-2) (Coco 3).zip", + "machine": "coco3", + "disk0": "SOKOBAN.DSK", + "joy-right": "kjoy0", + "basic": "LOADM\"SOKOBAN\":EXEC\n" + }, + "06aaf4e9ede4377fa4b19b24c09b60beb3e0f2a8": { + "filename": "Sokoban 1 (Wayne Campbell) (Coco 3).zip", + "machine": "coco3", + "disk0": "SOKOBAN.DSK", + "joy-right": "mjoy0", + "basic": "RUN\"SOKOBAN\"\n" + }, + "a703afe8de47441e7f79b0a8492059416609952c": { + "filename": "Snake Pit (SRB Software).zip", + "disk0": "Snake Pit (SRB Software)/SNAKEPIT.DSK", + "joy-left": "kjoy0", + "basic": "LOADM\"SNAKE\":EXEC\n" + }, + "de176e7f4b343f1089f198d8e6f8bd998dc9d29a": { + "filename": "Snake Pit (SRB Software).zip", + "disk0": "Snake Pit (SRB Software)/SNAKEPIT.DSK", + "joy-left": "kjoy0", + "basic": "LOADM\"SNAKE\":EXEC\n" + }, + "d757673e90d3cad97c9374070f4a70a08d013842": { + "filename": "Solgan's Salvation (Craig Springett).zip", + "disk0": "SOLGAN.DSK", + "basic": "RUN\"BOOT\"\n" + }, + "fb7bf274165a08ddadbb92baf1a779d941d48f85": { + "filename": "Solitaire (Public Domain) (Coco 3).zip", + "machine": "coco3", + "disk0": "SOLITAIR.DSK", + "basic": "RUN\"SOLITAIR\"\n" + }, + "4d5b490dd355d1a35efb5e88253a0c90c50e34f8": { + "filename": "Solitaire (Tandy).zip", + "disk0": "SOLITARE.DSK", + "basic": "RUN\"SOLITARE\"\n" + }, + "ed783705237ff3baaa886633b9f4a5fc814d3a57": { + "filename": "Solo Poker (Tandy).zip", + "disk0": "SOLOPKR.DSK", + "basic": "RUN\"SOLOPKR\"\n" + }, + "81af7f89c54edcf8ce016f755a02052c973fe58d": { + "filename": "Solitaire (Torsten Dittel).zip", + "disk0": "solitair.dsk", + "joy-right": "mjoy0", + "basic": "RUN\"SOLITAIR\"\n" + }, + "fb81ea8b974d2c9c72dff0aecf883a26bae98d9b": { + "filename": "Solo Poker (T&D Software).zip", + "disk0": "SOLOPKR.DSK", + "basic": "RUN\"SOLOPKR\"\n" + }, + "fa8947f17872afa6e1a5e8dec5bd6d717903f430": { + "filename": "Solo Pool (Tom Mix Software).zip", + "disk0": "SOLOPOOL.DSK", + "joy-right": "mjoy0", + "basic": "RUN\"SOLOPOOL\"\n" + }, + "49e6ee904f304c652e9c0eb4672633368dc109a5": { + "filename": "Soooper Pac (Bear Bones Software).zip", + "disk0": "SOOPER.DSK", + "joy-right": "kjoy0", + "basic": "LOADM\"SOOPER\":EXEC\n" + }, + "375f1774c5858703d4cbb9568c4e6d26330dd8ec": { + "filename": "Space Ace (CLOAD).zip", + "disk0": "SPACEACE.DSK", + "basic": "LOADM\"SPACEACE\":EXEC\n" + }, + "201bc2b4ba4adf2487cd9ce38ccb17988a7a1518": { + "filename": "Soviet Bloc Demo (Strongware) (Coco 3).zip", + "machine": "coco3", + "disk0": "BLOCDEMO.DSK", + "basic": "LOADM\"BLOCDEMO\":EXEC\n" + }, + "a1f75a3958e89fc2dc3006dc6f692c983e730f28": { + "filename": "Sokoban 1 (Wayne Campbell) (OS-9) (Coco 3).zip", + "machine": "coco3", + "disk0": "sokoban1.dsk", + "joy-right": "mjoy0", + "basic": "DOS\n" + }, + "c1d6ef47775454ac78c7855dab60ba63870d631f": { + "filename": "Sokoban 1 (Wayne Campbell) (OS-9) (Coco 3).zip", + "machine": "coco3", + "disk0": "sokoban1.dsk", + "joy-right": "mjoy0", + "basic": "DOS\n" + }, + "0c80f4beadf66a8c9630dc9b013110b0ab28f2c8": { + "filename": "Space Ambush (Computerware).zip", + "disk0": "AMBUSH.DSK", + "joy-right": "kjoy0", + "basic": "LOADM\"AMBUSH\":EXEC\n" + }, + "7dea7e95d7ea1761d6c511e462c53c86fda9f809": { + "filename": "Space Ambush Original Promotional Copy (Andy Kluck).zip", + "disk0": "AMBPROMO.DSK", + "joy-right": "kjoy0", + "basic": "LOADM\"AMBPROMO\":EXEC\n" + }, + "11e8b7be70503c61b8ad32a41b3676bc5d5ac557": { + "filename": "Space Assault (Tandy).zip", + "disk0": "ASSAULT.DSK", + "joy-left": "kjoy0", + "basic": "LOADM\"ASSAULT\":EXEC\n" + }, + "7b199930d286a25738b0e1158039f530c9ff908d": { + "filename": "Space Escape (Computer Hut Software).zip", + "disk0": "s-escape.dsk", + "basic": "RUN\"S-ESCAPE\"\n" + }, + "4068d531ffde75004dc2bfbb055a8d90b4d5d2f8": { + "filename": "Spaced Invaders (Stuart Wyss-Gallifent).zip", + "machine": "coco3", + "disk0": "SPACED.DSK", + "joy-right": "kjoy0", + "basic": "LOADM\"SPACED\":EXEC\n" + }, + "619dee2288f1fa1cec6136196405f8fec6941821": { + "filename": "Space Intruders (Radio Shack, Game Point Software) (Coco 3).zip", + "machine": "coco3", + "disk0": "spaceint.dsk", + "joy-right": "kjoy0", + "basic": "RUN\"SPACE\"\n" + }, + "42eee9455730f646ea31fdf98389de71f277d0bc": { + "filename": "Space Hawk (Public Domain).zip", + "disk0": "HAWK.DSK", + "joy-right": "kjoy0", + "basic": "LOADM\"HAWK\":EXEC\n" + }, + "a49de54f9fba37b3d69939fa5550926332df7f4c": { + "filename": "Space Intruders (Adventure International).zip", + "disk0": "SPCINTR.DSK", + "joy-right": "kjoy0", + "basic": "RUN\"INTRUDER\"\n" + }, + "bb47a5f42920b3f3b88e71f12e06c25a5bb2db88": { + "filename": "Space Bandits 0.16 (Jamie Cho) (Coco 3).zip", + "machine": "coco3h", + "disk0": "BNDT6309.DSK", + "joy-right": "kjoy0", + "basic": "LOADM\"BANDITS\":EXEC\n", + "tv-input": "bluered" + }, + "46227e5de67196934cd65041ee37e33e99c03e0f": { + "filename": "Space Bandits 0.16 (Jamie Cho) (Coco 3).zip", + "machine": "coco3h", + "disk0": "BNDT6309.DSK", + "joy-right": "kjoy0", + "basic": "LOADM\"BANDITS\":EXEC\n", + "tv-input": "bluered" + }, + "32eecf337ffda0e77bb7649a3545aad7e4a82fc5": { + "filename": "Space Marauder 1.0 (Tandy) (Coco 3).zip", + "machine": "coco3", + "disk0": "MARAUDER.DSK", + "joy-left": "kjoy0", + "basic": "LOADM\"MARAUDER\":EXEC\n" + }, + "108a45348a005c210ed30a84e71949986de0efc8": { + "filename": "Space Invaders 8080 to 6809 port (Glen Hewlett) (Coco 3).zip", + "machine": "coco3", + "disk0": "Disk.dsk", + "joy-right": "kjoy0", + "basic": "LOADM\"SI\":EXEC\n" + }, + "b84542df5f713d562637ac019ebc6c86b5a8daa5": { + "filename": "Space Marauder 2.0 (Tandy) (Coco 3).zip", + "machine": "coco3", + "disk0": "MARAUDER.DSK", + "joy-left": "kjoy0", + "basic": "LOADM\"MARAUDER\":EXEC\n" + }, + "538e7246b8d2e2057a83333364eed2f2e0c049c8": { + "filename": "Space Invaders Alpha v0.9 (Mark McDougall) (Coco 3).zip", + "machine": "coco3", + "disk0": "SPACEINV.DSK", + "joy-right": "kjoy0", + "basic": "RUN\"SI\"\n" + }, + "09275805a90e369f2c64b2a76b4b7b5fedce40f7": { + "filename": "Space Merchant (GOSUB Internatonal).zip", + "disk0": "SPCEMERC.DSK", + "basic": "RUN\"SPCEMERC\"\n" + }, + "d56f1babfa8baa33d1cdd7dcd74dbf9f5e90d972": { + "filename": "Space Protector (Hewson Software).zip", + "disk0": "SPACPROT.DSK", + "basic": "LOADM\"SPACPROT\":EXEC\n" + }, + "d644b52ca2c325f704f19ce4d05a2d788a782836": { + "filename": "Space Race (Spectral Associates).zip", + "disk0": "SPACERAC.DSK", + "joy-right": "kjoy0", + "basic": "LOADM\"SPACERAC\":EXEC\n" + }, + "a96153f2d42096218e5da850d194d898a090b9d4": { + "filename": "Space Raider (T&D Software).zip", + "disk0": "RAIDER.DSK", + "joy-right": "kjoy0", + "basic": "LOADM\"RAIDER\":EXEC\n" + }, + "b931853defa47de9669ab9b883e3ed50a2fa8e3d": { + "filename": "Space Raiders (John Crawford).zip", + "disk0": "SRAIDERS.DSK", + "joy-right": "kjoy0", + "basic": "LOADM\"SRAIDERS\":EXEC\n" + }, + "7789f1c91e48908befc8489085b44d52e2e2b606": { + "filename": "Space Raiders (Mark Data Products).zip", + "disk0": "SRAIDERS.DSK", + "joy-left": "kjoy0", + "basic": "LOADM\"SRAIDERS\":EXEC\n" + }, + "45a670007ee566c28eda13875afc2605a2b2c361": { + "filename": "Space Sentry (Spectral Associates).zip", + "disk0": "SENTRY.DSK", + "joy-right": "kjoy0", + "basic": "LOADM\"SENTRY\":EXEC\n" + }, + "ce61607860440f13a3d1852a17d803714e6e09b8": { + "filename": "Spacewar! 1.0 (Rich Natili).zip", + "disk0": "SWDISK.dsk", + "basic": "LOADM\"SPWAR21\":EXEC\n" + }, + "183e7035876ae23781b446faa52a2dcdc4a36cda": { + "filename": "Spacewar (Jim Jewett).zip", + "disk0": "Spacewar.dsk", + "basic": "RUN\"SPACEWAR\"\n" + }, + "0888fd9b4d3fd9121f8bacb2b703fb0b65e7c908": { + "filename": "Space Zapper (Aardvark-80).zip", + "disk0": "ZAPPER.DSK", + "basic": "RUN\"ZAPPER\"\n" + }, + "41da83b418cc1134bfe0fc1e7b8493956e039593": { + "filename": "Spacewar! 2.0 (Rich Natili).zip", + "disk0": "swdisk2.dsk", + "basic": "LOADM\"SPWARV2\":EXEC\n" + }, + "2bb124abbe614a016998d5b8046ee85db6f5a71a": { + "filename": "Space War (ASBC).zip", + "disk0": "SPACEWAR.DSK", + "joy-left": "mjoy0", + "joy-right": "kjoy0", + "basic": "LOADM\"SPACEWAR\":EXEC\n" + }, + "ae39fbc59d4cf3e8f6b6787e7d936800457a46ae": { + "filename": "Space War (Spectral Associates).zip", + "disk0": "SPACEWAR.DSK", + "joy-right": "kjoy0", + "basic": "LOADM\"SPACEWAR\":EXEC\n" + }, + "f8c123d3c6b9d516812439cf53130c981726b652": { + "filename": "Space Wrek (Spectral Associates) (SSC).zip", + "disk0": "WREK.DSK", + "joy-right": "kjoy0", + "basic": "LOADM\"WREK\":EXEC\n" + }, + "807328d5c7d933f0aa009c28150ea0be7df702ac": { + "filename": "Speed Racer (Michtron).zip", + "disk0": "S-RACER.DSK", + "joy-right": "kjoy0", + "basic": "LOADM\"S-RACER\":EXEC\n" + }, + "145b19acc67f3965f7ff27fb6ca57767eae6806d": { + "filename": "Spectrum Adventure Generator (Spectrum Projects).zip", + "disk0": "ADV-GEN.DSK", + "basic": "RUN\"SAG\"\n" + }, + "49340c394aa4f609a7b764fbdc0cda857aff1edc": { + "filename": "Spellbound (Thor Software).zip", + "disk0": "SPELLBND.DSK", + "basic": "RUN\"S\"\n" + }, + "d6cdaece9c61f1468678c5945d617b8976ca7ef1": { + "filename": "Spidercide (Tandy).zip", + "disk0": "SPIDER.DSK", + "joy-right": "kjoy0", + "basic": "LOADM\"SPIDER\":EXEC\n" + }, + "570b280b3def4b5cdd1356ba2111e98487d5a1bf": { + "filename": "Spider-Hype (Roger Taylor) (Coco 3).zip", + "machine": "coco3", + "disk0": "SPIDER.DSK", + "basic": "LOADM\"SPIDER\":EXEC\n" + }, + "cbd1f8e5fa886153734f18d0df48b01fd6783c3c": { + "filename": "Spell Down (WKM Softwate) (Coco 3).zip", + "machine": "coco3", + "disk0": "spelld.dsk", + "basic": "RUN\"WORD\"\n" + }, + "69ed445e8d58dbb46a1c8605e90fdfdde987f9ce": { + "filename": "Spider-Hyper (Roger Taylor) (6309) (Coco 3).zip", + "machine": "coco3h", + "disk0": "SPID6309.DSK", + "basic": "LOADM\"SPID6309\":EXEC\n" + }, + "a3ba75b1b1aecc7f0bdcbdf22b7aa9efb173815f": { + "filename": "Splinter (Jamie Leecho) (Coco 3).zip", + "machine": "coco3", + "disk0": "Splinter1.dsk", + "basic": "RUN\"SPLINTER\"\n" + }, + "790d99b4f1355b60011b0c6f4a73a4f90f7e68ec": { + "filename": "Springster (Tandy) (Coco 3).zip", + "machine": "coco3", + "disk0": "SPRING.DSK", + "joy-right": "kjoy0", + "basic": "LOADM\"SPRING\":EXEC\n" + }, + "3d08f20514508e3d79297bc91535df6aecab45c4": { + "filename": "Spy Against Spy (Pulser) (Coco port).zip", + "disk0": "SPY.DSK", + "joy-right": "kjoy0", + "basic": "CLEAR10:LOADM\"SPY\":EXEC\n" + }, + "7734213cc424d8e30244a8b148a671cd5c333ac1": { + "filename": "Spy Hunter v2 (Rich Natili).zip", + "disk0": "SPYHUNT.DSK", + "joy-right": "kjoy0", + "joy-left": "mjoy0", + "basic": "RUN\"SH20\"\n" + }, + "60a489d2d4d4a51ad2698adcdac20a666dcdd38a": { + "filename": "Spymaster (Scott McCleary).zip", + "disk0": "SPYMASTR.DSK", + "basic": "RUN\"BOOT\"\n" + }, + "73ac6f41ae35f3797fb877cbb6957dee5b7b7031": { + "filename": "Squares (T&D Software).zip", + "disk0": "SQUARE.DSK", + "basic": "LOADM\"SQUARE\":RUN\n" + }, + "8bb2fc0f54c6a4bc4731e083485e3a6c033497cf": { + "filename": "Squash (T&D Software).zip", + "disk0": "SQUASH.DSK", + "joy-right": "mjoy0", + "basic": "LOADM\"SQUASH\":EXEC\n" + }, + "c49426d9e1ebef8a5a74aabb5a5b3da9ede3a8ea": { + "filename": "Squeeze (Real Software Company).zip", + "disk0": "SQUEEZE.DSK", + "basic": "RUN\"SQUEEZE\"\n" + }, + "2d6025f320178633c1333c654229e60c1650be84": { + "filename": "SR-71 (Tom Mix Software).zip", + "disk0": "SR-71.dsk", + "joy-right": "kjoy0", + "basic": "RUN\"SR71\"\n" + }, + "47d0c3d2fee03dc1bc24dac4bf6ba77e8f9e666b": { + "filename": "Stalin Grad (Ark Royal Software).zip", + "disk0": "STALIN.DSK", + "basic": "RUN\"RUSSIA\"\n", + "tv-input": "redblue", + "notes": "At the OK prompt type RUN and press ENTER." + }, + "344bd263cec54d5fc702c8490eb04e7d31270e4f": { + "filename": "Star Blaster (Micro Works).zip", + "disk0": "BLASTER.DSK", + "joy-right": "kjoy0", + "basic": "LOADM\"BLASTER\":EXEC\n" + }, + "76cd53ab2c39d2265ce68d85fa5af8f24571511e": { + "filename": "Star Fighter (Aardvark Action Software).zip", + "disk0": "STRFIGHT.DSK", + "joy-right": "kjoy0", + "basic": "LOADM\"STRFIGHT\":EXEC\n" + }, + "8579a28623e2ae45de0beb0c46aa1b700a4e673b": { + "filename": "Star Blaze (Tandy).zip", + "disk0": "STARBLAZ.DSK", + "joy-right": "kjoy0", + "basic": "LOADM\"STARBLAZ\":EXEC\n" + }, + "469189d7ce12181402fb97c0e8a4e2f4d5d5c503": { + "filename": "Starbase Attack (Illustrated Memory Banks).zip", + "disk0": "STARBASE.DSK", + "joy-right": "mjoy0", + "basic": "RUN\"STARBASE\"\n" + }, + "5a48d0dba63a83dd6a699346e32a592fe1265a4e": { + "filename": "Starfighter (John Morrison) (SG-8 intro).zip", + "disk0": "SFIGHTER.DSK", + "joy-right": "kjoy0", + "basic": "LOADM\"SFIGHTER\":EXEC\n" + }, + "504d97ea1d84f7c479a2210aea22b5d51f24a6f5": { + "filename": "Starblazer (Ark Royal Games).zip", + "disk0": "STARBLZR.DSK", + "basic": "RUN\"STAR\"\n" + }, + "c1f4402a691601c0606dfc5f05f377eced39b064": { + "filename": "Star Lanes (Martyn Phillips) (Coco 3).zip", + "machine": "coco3", + "disk0": "STARLANE.DSK", + "basic": "RUN\"STARLANE\"\n" + }, + "0f4ca570a9eb210536b009bfcdd51be9555ae0fb": { + "filename": "Starfire (Intellectronics).zip", + "disk0": "STARFIRE.DSK", + "joy-right": "kjoy0", + "basic": "LOADM\"STARFIRE\":EXEC\n" + }, + "ab531570beed9170552486cdf7e969ab081ad204": { + "filename": "Starship Chameleon (Computerware).zip", + "disk0": "STARSHIP.DSK", + "joy-right": "kjoy0", + "basic": "LOADM\"STARSHIP\":EXEC\n" + }, + "4ead175f81311287419f6e9f55b982c10f151c96": { + "filename": "Starship Control (Bill Barbe).zip", + "disk0": "STARSHIP.DSK", + "basic": "RUN\"STARSHIP\"\n" + }, + "2f186eb752050c6f8ff85fbfccb338f9685780a2": { + "filename": "Starship Falcon (Cer-Comp).zip", + "disk0": "STARFALC.DSK", + "basic": "RUN\"ADVENT\"\n" + }, + "8553f3044ec0473f767dccc53aec4e83d0746da7": { + "filename": "Star Skid (T&D Software).zip", + "disk0": "STARSKID.DSK", + "joy-right": "kjoy0", + "basic": "LOADM\"STARSKID\":EXEC\n" + }, + "5fa8c1f378805e212b99ab510daf8d26672d755a": { + "filename": "Star Swoop (Blaby Computer Games) (Coco port) (SG-8) (Coco 1-2).zip", + "disk0": "swoop.dsk", + "joy-right": "kjoy0", + "basic": "LOADM\"SWOOP\":EXEC\n" + }, + "4af4d30471a92347e9b177c591ea01ba1ce9ed59": { + "filename": "Star Spores (Spectral Associates) (alt).zip", + "disk0": "STARPORE.DSK", + "joy-right": "kjoy0", + "basic": "LOADM\"STARPORE\":EXEC\n" + }, + "87295942d52cdbfbb97e2fb6eaa88cc5aca6ed92": { + "filename": "Star Spores (Spectral Associates).zip", + "disk0": "SPORES.DSK", + "joy-right": "kjoy0", + "basic": "LOADM\"SPORES\":EXEC\n" + }, + "1fd678dcc1929285b3a4c4ea1f8cd3e567e3c774": { + "filename": "Star Trek Adventure #2 (Howard F. Batie).zip", + "disk0": "TREKADV2.DSK", + "basic": "PCLEAR1:RUN\"TREKADV2\"\n" + }, + "230f5453aa3a1554a21494819f48e7125212a306": { + "filename": "Star Trader (Computerware).zip", + "disk0": "TRADER.DSK", + "joy-right": "mjoy0", + "basic": "LOADM\"TRADER\":EXEC\n" + }, + "770b196e034cd4a99dca1f98e937c2f7dc4b1a9d": { + "filename": "Star Trek Adventure.zip", + "disk0": "TREKADV.DSK", + "basic": "RUN\"TREKADV\"\n" + }, + "a73b7e393c381ed866b0b7bd5353653755a76216": { + "filename": "Star Trek III 4.00 (Adventure International).zip", + "disk0": "TREKIII.DSK", + "basic": "RUN\"TREKIIID\"\n" + }, + "0556042b07e8dc0a0817de8ac2e80e5508e98186": { + "filename": "Star Trek Mission Simulation (Binary Systems) (Coco 3).zip", + "machine": "coco3", + "disk0": "TREKSIM.DSK", + "basic": "RUN\"TREKKIE\"\n" + }, + "dc61b4e2c5c2f12b2625810466e46fc898401665": { + "filename": "Star Trek Trivia 2.0 (Chris Presley).zip", + "disk0": "TREK20.DSK", + "basic": "RUN\"TREK20\"\n" + }, + "b6291acb376fd58ad05b71628ee9727fcf03794d": { + "filename": "Star Trek Trivia (Chris Presley).zip", + "disk0": "TREKTRIV.DSK", + "basic": "RUN\"TREKTRIV\"\n" + }, + "75f3545de35b72f750dd59c545da37203447fa94": { + "filename": "Starwars (T&D Software).zip", + "disk0": "STARWARS.DSK", + "joy-right": "kjoy0", + "basic": "RUN\"STARWARS\"\n" + }, + "eaa1380edb9db5e13efcfb9f743f7701a8cd7716": { + "filename": "Startrek (Tom Mix Software).zip", + "disk0": "STARTREK.DSK", + "basic": "RUN\"TREK-16\"\n" + }, + "4907219bee585604bde8f395de72b946753799a4": { + "filename": "Stellar Empire (Chromasette).zip", + "disk0": "STELLEMP.DSK", + "basic": "RUN\"STELLINS\"\n" + }, + "2c66765582498eb24b676e8c4d075fb443fe456c": { + "filename": "Stellar Lifeline (Tandy).zip", + "disk0": "LIFELINE.DSK", + "joy-right": "kjoy0", + "basic": "LOADM\"LIFELINE\":EXEC\n" + }, + "3980071b0f906d46aec7e8d243d8cf9a8c8f4efd": { + "filename": "Steps (T&D Software).zip", + "disk0": "STEPS.DSK", + "basic": "LOADM\"STEPS\":EXEC\n" + }, + "27266790db0f9e160ec8ff94fbf87595ca1c36c2": { + "filename": "Stellar Search (Owlware).zip", + "disk0": "stellar.dsk", + "basic": "RUN\"INTRO\":EXEC\n" + }, + "9a30208fa443859b37631a275f0a95083005a374": { + "filename": "Stinger (Eigen Systems).zip", + "disk0": "STINGER.DSK", + "joy-right": "kjoy0", + "basic": "LOADM\"STINGER\":EXEC\n" + }, + "347d89282de77030e9a9def0f08ffd0869c63288": { + "filename": "Stockticker 88 (The Rainbow) (Coco 3).zip", + "machine": "coco3", + "disk0": "STOCK88.DSK", + "basic": "RUN\"STOCKS3\"\n" + }, + "eabc13ad70b9dda3c0c50aad72a054e1923b82a9": { + "filename": "Steve Ostrom Games (Steve Ostrom).zip", + "disk0": "OSTROM2.DSK", + "joy-right": "kjoy0", + "basic": "DIR\n" + }, + "ff1324b184965fe877749a4c1de055c709b25830": { + "filename": "Steve Ostrom Games (Steve Ostrom).zip", + "disk0": "OSTROM2.DSK", + "joy-right": "kjoy0", + "basic": "DIR\n" + }, + "67ea79710cd2b71f49ea9d89823da481d6a4acf9": { + "filename": "Stone Raider II (Microdeal Cornwall).zip", + "disk0": "STONRAID.DSK", + "joy-right": "kjoy0", + "basic": "LOADM\"STONRAID\":EXEC\n" + }, + "0277fc32cdf72b0f8f5a1dcb06b8f5fe6b32fb95": { + "filename": "Storm Arrows (Spectral Associates).zip", + "disk0": "SARROW.DSK", + "joy-right": "kjoy0", + "basic": "LOADM\"SARROW\":EXEC\n" + }, + "8ed67da21863be7dbe2be017bb4f0d9eebeb1e79": { + "filename": "Starwars (John Frontuto & Christian Miller) (OS-9) (Coco 3).zip", + "machine": "coco3", + "disk0": "starwars.dsk", + "basic": "DOS\n" + }, + "4b6d522233dfd4dc34e4b7aba7439373bc7aa480": { + "filename": "Starwars (John Frontuto & Christian Miller) (OS-9) (Coco 3).zip", + "machine": "coco3", + "disk0": "starwars.dsk", + "basic": "DOS\n" + }, + "831835f7c5ad0e2c1bf13a8793276146f90b5886": { + "filename": "Starwars (John Frontuto & Christian Miller) (OS-9) (Coco 3).zip", + "machine": "coco3", + "disk0": "starwars.dsk", + "basic": "DOS\n" + }, + "3674604f3ee9b424291c722a137c44cc4081ba6c": { + "filename": "Storm (Computerware) (SG-24) (Coco 1-2).zip", + "disk0": "STORM.DSK", + "joy-right": "kjoy0", + "basic": "LOADM\"STORM\":EXEC\n" + }, + "344392f1ee73cc50b9fe5a79e20a093cb9a47a17": { + "filename": "Strata (Michael Sirolly) (Coco 3).zip", + "machine": "coco3", + "disk0": "STRATA.DSK", + "joy-right": "kjoy0", + "basic": "RUN\"STRATA\"\n" + }, + "f4e3aafad41d0f95aba4808bdb2a3a8f67c239e3": { + "filename": "Stranded (Shawn Driscoll) (OS-9) (Coco 3).zip", + "machine": "coco3", + "disk0": "stranded.dsk", + "joy-right": "kjoy0", + "basic": "DOS\n" + }, + "07c1ed8c2056567825b5a914d441f8fa2eb71e0d": { + "filename": "Strip Tails (Boudoir Software).zip", + "disk0": "STRIPTLS.DSK", + "joy-left": "mjoy0", + "joy-right": "kjoy0", + "basic": "RUN\"TAILS\"\n" + }, + "7ee150ed761fdc0d7d35fb6f71b5d8161ac7e8e7": { + "filename": "Styx (T&D Software).zip", + "disk0": "STYX.DSK", + "joy-right": "kjoy0", + "basic": "LOADM\"STYX\":EXEC\n" + }, + "ada7e557aae09b6248903b546db8c54c2381540b": { + "filename": "Stronghold (Computer Shack).zip", + "disk0": "STRGHOLD.DSK", + "joy-right": "kjoy0", + "basic": "LOADM\"STRGHOLD\":EXEC\n" + }, + "82a125482df97a9dff83e973d2bbb72ff9544fe9": { + "filename": "Strip Poker (Artworx) (OS-9) (Coco 3).zip", + "machine": "coco3", + "disk0": "spoker.dsk", + "basic": "DOS\n" + }, + "c41d215bc457f707782e05e36d96f7ff15637d36": { + "filename": "Strip Tease (Britt Monk, CDP).zip", + "disk0": "STRIP-T.DSK", + "joy-right": "kjoy0", + "basic": "LOADM\"STRIP-T\":EXEC\n" + }, + "d8872712425476a15714ac8f3d6a3dfcb7f8bbce": { + "filename": "Strip Poker (Artworx) (Coco 3).zip", + "machine": "coco3", + "disk0": "STRIP1.DSK", + "basic": "RUN\"POKER\"\n" + }, + "9a79665416a13ff191f86068511958c051018c0f": { + "filename": "Strip Poker (Artworx) (Coco 3).zip", + "machine": "coco3", + "disk0": "STRIP1.DSK", + "basic": "RUN\"POKER\"\n" + }, + "237781c8169b6711a8d79d8e2e96bad01dbe6945": { + "filename": "Strip Poker (Artworx) (Coco 3).zip", + "machine": "coco3", + "disk0": "STRIP1.DSK", + "basic": "RUN\"POKER\"\n" + }, + "319a31b73735091bf8e06783cdfd6018241cd01b": { + "filename": "Super Master Mind (Bill Nobel & Darryl Hildebrandt) (Coco 3).zip", + "machine": "coco3", + "disk0": "SMSTRMND.DSK", + "basic": "RUN\"SMSTRMND\"\n" + }, + "6a6d61ab7efd1c7a87142599795db7d50e84fedb": { + "filename": "Superkid (Quickbeam).zip", + "disk0": "SUPERKID.DSK", + "joy-right": "kjoy0", + "basic": "LOADM\"SUPERKID\":EXEC\n" + }, + "1ceb8e8e8665d29602c75772a1274aae31a7813c": { + "filename": "Sudoku 2.1 (Alex Gayer).zip", + "machine": "coco3", + "disk0": "sudoku.dsk", + "basic": "RUN\"SUDOKU\"\n" + }, + "386c1a2c0d46bcf1c9e7d4ba1e19c18b428d400f": { + "filename": "Super Pitfall (6309) (Tandy) (Coco 3).zip", + "machine": "coco3h", + "disk0": "PITFALL3.DSK", + "joy-right": "kjoy0", + "basic": "LOADM\"PITFALL3\":EXEC\n" + }, + "aab170dcbd418215032495b32b2563662c751396": { + "filename": "Super Poker II (Marc Quirion) (Coco 3).zip", + "machine": "coco3", + "disk0": "POKER2.DSK", + "basic": "RUN\"POKER2\"\n" + }, + "02aaf0ac23c74deccb68f42752c5af85211c0db3": { + "filename": "Super Pitfall (Tandy) (Coco 3).zip", + "machine": "coco3", + "disk0": "PITFALL.DSK", + "joy-right": "kjoy0", + "basic": "RUN\"PITFALL\"\n" + }, + "bfb07670c45929a2dbd07485ed4c3ee7d4bb2320": { + "filename": "Super Rat (L. Becker).zip", + "disk0": "SUPERRAT.DSK", + "joy-right": "kjoy0", + "basic": "LOADM\"SUPERRAT\":EXEC\n" + }, + "f78a36c70ed4f6467c4d595a5ad5db1e85bfd950": { + "filename": "Super Rooter (Mark Nelson).zip", + "disk0": "ROOTER.DSK", + "joy-right": "kjoy0", + "basic": "LOADM\"ROOTER\":EXEC\n" + }, + "cec6b606aa644cba5ca0fdd5656dc8b7c70479e9": { + "filename": "Super Star Trek II (Backyard Software).zip", + "disk0": "STREK-II.DSK", + "basic": "PCLEAR1:RUN\"STREK II\"\n" + }, + "1de7b7925f7b8af08b6e4b836b9d1853493a4e2e": { + "filename": "Super Vaders (T&D Software).zip", + "disk0": "VADERS.DSK", + "basic": "LOADM\"VADERS\":EXEC\n" + }, + "839145183d01eaddbc6450b8dbab5b6806f7cafa": { + "filename": "Swell Foop (Jim Gerrie).zip", + "disk0": "SWELLFOOP.DSK", + "basic": "RUN\"SWELFOOP\"\n" + }, + "1de5d0f996dd8bfa3f683815ba12bf3c14b20cd5": { + "filename": "Surface (The Rainbow).zip", + "disk0": "SURFACE.DSK", + "basic": "RUN\"SURFACE\"\n" + }, + "849459e22102acf64d978c2600cdf7ac1a632a09": { + "filename": "Syzygy v1.0 (Spectral Associates).zip", + "disk0": "SYZYGY.DSK", + "basic": "LOADM\"SYZYGY\":EXEC\n", + "tv-input": "redblue" + }, + "dfa72c12a1cabf0f8b69203b7fc40eb2d62856c3": { + "filename": "Syzygy v3.0 (Spectral Associates) (SSC).zip", + "disk0": "SYZYGY.DSK", + "basic": "LOADM\"SYZYGY\":EXEC\n", + "tv-input": "redblue" + }, + "09fb0d4b57fb3875c89f268a1431719c50a025f8": { + "filename": "Taipan (Megasoft) (English translation).zip", + "disk0": "TAIPAN.DSK", + "basic": "RUN\"TAIPAN\"\n" + }, + "81b39a574b1d94e3a626d8f5b41a3e443f92cb67": { + "filename": "Tales of Suburbia Demo v1.0 (TRS-80 Retro Programing).zip", + "disk0": "SUBURBIA.DSK", + "joy-right": "kjoy0", + "basic": "RUN\"TALES\"\n" + }, + "40510d7bbcf2d54cec5c3515dbd0d74f9e375c0a": { + "filename": "Tanjali (The Software Guild).zip", + "disk0": "TANJALI.DSK", + "basic": "RUN\"TANJALI\"\n" + }, + "7a5ebc3a73d24ae2ed160984632f176a36e815a0": { + "filename": "Tank Out (MatchyGames) (SG-24) (CoCo PSG).zip", + "disk0": "tank_out.dsk", + "basic": "RUN\"R\"\n" + }, + "e911cdf70a76b567003ff7d9a623689a7f1f292e": { + "filename": "Tanglewood (Microdeal).zip", + "disk0": "TANGLE.DSK", + "basic": "LOADM\"TANGLE\":EXEC\n" + }, + "ad48b19e5c4d5472e5f7a4a087ef4fd36c94a388": { + "filename": "Tank (T&D Software).zip", + "disk0": "TANK.DSK", + "joy-right": "kjoy0", + "basic": "RUN\"TANK\"\n" + }, + "eaf52fc49354b467a14668a822797829ea448a91": { + "filename": "Tanks (80 Micros Magazine).zip", + "disk0": "TANKS.DSK", + "joy-right": "kjoy0", + "basic": "RUN\"TANKS\"\n" + }, + "3bed69b256ac0cb80ecf5962048c6b3fe107f6a7": { + "filename": "Tarot Cards (Amir Dimitri).zip", + "disk0": "TAROT.DSK", + "basic": "PCLEAR1:RUN\"TAROT\"\n" + }, + "916bd34a068b6756c3047a7e161733d405f8f371": { + "filename": "T&D Track (T&D Software).zip", + "disk0": "HORSRACE.DSK", + "basic": "RUN\"HORSRACE\"\n" + }, + "331e7335de18c95221346dc5cdc969fd63c0541a": { + "filename": "Teatime (Pocket Money Software).zip", + "disk0": "TEATIME.DSK", + "joy-right": "kjoy0", + "basic": "LOADM\"TEATIME\":EXEC\n" + }, + "8fae83561b084fc9bcc1947726984cc4fb3be844": { + "filename": "Teee-Offf (Prickly-Pear Software).zip", + "disk0": "TEEEOFFF.DSK", + "joy-right": "kjoy0", + "basic": "LOADM\"TEEEOFFF\":EXEC\n" + }, + "460b12d5a640da41fa0b9fcc37e1a0fbd3d39e71": { + "filename": "Telengard (Avalon Hill) (Coco port).zip", + "disk0": "TELNGARD.DSK", + "basic": "RUN\"TELNBOOT\"\n" + }, + "b8ad362eb258e888805a49bd47b939388c3e7ad4": { + "filename": "Temple of ROM (Tandy).zip", + "disk0": "TEMPLE.DSK", + "joy-right": "kjoy0", + "basic": "LOADM\"TEMPLE\":EXEC\n" + }, + "bec060cad30c4d63c1a58e54980bdcd5b84793c9": { + "filename": "Temple of the Lost Ark (SJG Inc.).zip", + "disk0": "The_Ark.dsk", + "basic": "RUN\"THE ARK\"\n" + }, + "4e19d6daa952cb608cb0281f6fad4af0cbb2f681": { + "filename": "Tennis (Tandy).zip", + "disk0": "TENNIS.DSK", + "joy-left": "kjoy0", + "basic": "LOADM\"TENNIS\":EXEC\n" + }, + "71a18be1d6b8afaa2b01cea5f0372b561cd74364": { + "filename": "Term Paper (Charles Farris) (Coco 3).zip", + "machine": "coco3", + "disk0": "TERMPAPR.DSK", + "basic": "RUN\"TITLE\"\n" + }, + "324633533636e4537493297c1b431c5751a806cb": { + "filename": "Terminator 2 (Australian Coco Magazine).zip", + "disk0": "TERMINR2.DSK", + "basic": "RUN\"TERMINR2\"\n" + }, + "6ee4622079f46a12c77c22b8ddcf3d396eabfbbc": { + "filename": "Terror on Floor 51 Demo (JTR Systems) (Coco 3).zip", + "machine": "coco3", + "disk0": "FLOOR51.DSK", + "joy-right": "kjoy0", + "basic": "RUN\"BOOT\"\n" + }, + "99ff1b61a1329cfe7e66dda0efbd0a70c4f7aabb": { + "filename": "Tetra (Brian O'Neill) (Coco 3).zip", + "machine": "coco3", + "disk0": "TETRA.DSK", + "basic": "LOADM\"TETRA\":EXEC\n" + }, + "8e3903a17bf10c19897721a998a8cf86761e71d3": { + "filename": "Tetrapak (T&D Software) (Coco 3).zip", + "machine": "coco3", + "disk0": "TETRAPAK.DSK", + "basic": "LOADM\"TETRAPAK\":EXEC\n" + }, + "76b175beb6939bfa99b9deabe0efb1b2d2abc0fb": { + "filename": "Tetra (Rick's Computer Enterprise) (Coco 3).zip", + "machine": "coco3", + "disk0": "TETRA.DSK", + "basic": "RUN\"TETRA\"\n" + }, + "79ac790428d1b108297b63f14600cfb7d363b00e": { + "filename": "Tetras (Paul Burgin) (Coco port).zip", + "disk0": "TETRAS.DSK", + "joy-left": "kjoy0", + "joy-right": "mjoy0", + "basic": "LOADM\"TETRAS\":EXEC\n" + }, + "ca1f79fba23986d927e3957ca63c260f996c4997": { + "filename": "Tetris (PSE Computers) (Coco Port) (SG-24) (Coco 1-2).zip", + "disk0": "tetris.dsk", + "basic": "RUN\"TETRIS\"\n" + }, + "29ebd4ebf6fc5442ec6c6965e93c1c970592f929": { + "filename": "Tetris (Tandy) (Coco 1-2) (Coco 3).zip", + "machine": "coco3", + "disk0": "TETRIS.DSK", + "basic": "LOADM\"TETRIS\":EXEC\n" + }, + "40a37d6dcb8be6fc0e6537557a743d90f964fb8f": { + "filename": "Thexder (Tandy) (Coco 3).zip", + "machine": "coco3", + "disk0": "THEXDER.DSK", + "basic": "LOADM\"THEXDER\":EXEC\n" + }, + "562a2b3400df13d1da4518762f5ba5dcf524b5de": { + "filename": "Thumb Puzzle (L. Todd Knudsen) (Coco 3).zip", + "machine": "coco3", + "disk0": "THUMBPUZ.DSK", + "joy-left": "mjoy0", + "basic": "LOADM\"TP\"\n" + }, + "8c85f63c315bc5abfa24d3d2fb8c3c87ebf245b6": { + "filename": "Tetris (Mike Sweet & Hug Nguyen) (OS-9) (Coco 3).zip", + "machine": "coco3", + "disk0": "TETRIS.DSK", + "basic": "DOS\n" + }, + "23eab58ce3d467ba0b0b9bebc6c71a179bd23c7c": { + "filename": "Tetris (Mike Sweet & Hug Nguyen) (OS-9) (Coco 3).zip", + "machine": "coco3", + "disk0": "TETRIS.DSK", + "basic": "DOS\n" + }, + "56cf274918290abc2a5d240ce32fed4ddfe0df0f": { + "filename": "Thunder Road Adventure (Programmer's Guild).zip", + "disk0": "THUNDER.DSK", + "basic": "RUN\"THUNDER\"\n" + }, + "66e9719524596912271b1b73cdceb2ef43297476": { + "filename": "Tic-Tac-Toe 3D (Bob Delbourgo).zip", + "disk0": "tictac3d.dsk", + "basic": "RUN\"TICTAC3D\"\n" + }, + "0be4f2fd55eff57fafc78cddb415e4c14e1d5b6f": { + "filename": "Timber Man 2019 Christmas Edition Demo (Coco Brothers Software) (Coco 3).zip", + "machine": "coco3", + "disk0": "TMXMAS.dsk", + "joy-right": "kjoy0", + "basic": "LOADM\"TIMBER19\":EXEC\n" + }, + "3d23d23ce73a7972d97b4f7cd01e15033cf9ea60": { + "filename": "Time Bandit (Computer Shack).zip", + "disk0": "BANDIT.DSK", + "joy-left": "kjoy0", + "basic": "LOADM\"BANDIT\":EXEC\n" + }, + "7fe4121e5ba2bf5c00dcf6466b2a80cd673fd0e4": { + "filename": "Time Fighter (Mark Data Products).zip", + "disk0": "TIMEFGHT.DSK", + "joy-right": "kjoy0", + "basic": "LOADM\"TIMEFGHT\":EXEC\n" + }, + "c09477588ffb81ea6f10c976db04856864f3ec8e": { + "filename": "Time Patrol (Computerware).zip", + "disk0": "TMPATROL.DSK", + "joy-right": "kjoy0", + "basic": "LOADM\"TMPATROL\":EXEC\n" + }, + "b0e27b51144137e92d5322b77dbb98add57f77bb": { + "filename": "Toad in the Hole (John Day).zip", + "disk0": "JUMP-TOA.DSK", + "joy-right": "kjoy0", + "basic": "RUN\"JUMP-TOA\"\n" + }, + "6cb2097295a387072bef1500d2aaf8ac4a3c8d1f": { + "filename": "Tiburon (Shark) (Pere Serrat).zip", + "disk0": "TIBURON.DSK", + "joy-right": "kjoy0", + "basic": "RUN\"BOOT\"\n" + }, + "1d84e4b12f7893395e95087fbd0d1488a5f5bd94": { + "filename": "Tiburon (Shark) (Pere Serrat).zip", + "disk0": "TIBURON.DSK", + "joy-right": "kjoy0", + "basic": "RUN\"BOOT\"\n" + }, + "c65d7274da7c2b90329781137c29e8dccb622ff8": { + "filename": "Tiburon (Shark) (Pere Serrat).zip", + "disk0": "TIBURON.DSK", + "joy-right": "kjoy0", + "basic": "RUN\"BOOT\"\n" + }, + "8185d84a33855a78f6a1f4c09be35bbf68870b48": { + "filename": "Tim Love's Cricket (Peaksoft).zip", + "disk0": "CRICKET.DSK", + "joy-right": "kjoy0", + "basic": "LOADM\"CRICKET\":EXEC\n" + }, + "f21a219944f14bd608a7f9dbd22526092c8405ce": { + "filename": "Tomb of T'ien (Valkyrie Software).zip", + "disk0": "T'ien.dsk", + "joy-right": "kjoy0", + "basic": "LOADM\"T'IEN\":EXEC\n" + }, + "e809238373ad41147f2f793a5b724ef800082fa2": { + "filename": "Touchstone, The (Tom Mix Software).zip", + "disk0": "TOUCH.DSK", + "joy-left": "kjoy0", + "basic": "LOADM\"TOUCH\":EXEC\n" + }, + "692ec6eb9a5c4d3d47daaeea0f42fe597eeffe48": { + "filename": "Tombs of Terror (C & K Springett).zip", + "disk0": "TOMBS.DSK", + "basic": "RUN\"TOMBS\"\n" + }, + "e540133681e822b46917487d4d45a2e63e961bde": { + "filename": "Tower of Fear (Programmer's Guild).zip", + "disk0": "TOWER.DSK", + "basic": "LOADM\"TOWER\":EXEC\n" + }, + "475d15278f0cd9cece44e7f31e6c023a1035b081": { + "filename": "Tour of the Knight, The (Earther Enterprises) (OS-9) (Coco 3).zip", + "machine": "coco3", + "disk0": "knights.dsk", + "joy-right": "mjoy0", + "basic": "DOS\n" + }, + "f82a76a805e9019e4f780ba03e0bdc6236272627": { + "filename": "Town Atak.zip", + "disk0": "CITYBOM.DSK", + "basic": "RUN\"CITY BOM\"\n" + }, + "9ada8d1df5b051888d133d7d45157cf6d1e7a7f2": { + "filename": "Track Events (Baron Products).zip", + "disk0": "track.dsk", + "joy-right": "kjoy0", + "basic": "RUN\"START\"\n" + }, + "40dadd40aced26535c6d68b695da0dafbd797f14": { + "filename": "Tower of Fear (Programmer's Guild) (alt).zip", + "disk0": "TOWERALT.DSK", + "basic": "RUN\"TOWER\"\n" + }, + "e1cbeaf31507a8b898b261ce138845f4257d695d": { + "filename": "Trap 'em (Color Computer Magazine).zip", + "disk0": "TRAPEM.DSK", + "joy-left": "kjoy0", + "basic": "LOADM\"TRAPEM\":EXEC\n" + }, + "d8367ac5f10a6c5e53b4e6d975cfc4a1b74102f4": { + "filename": "Trapfall (Tom Mix Software).zip", + "disk0": "TRAPFALL.DSK", + "joy-right": "kjoy0", + "basic": "LOADM\"TRAPFALL\":EXEC\n" + }, + "720481de8a38034922cf95dde81d5fb61c888fa6": { + "filename": "Trapfall 1.1 (Tom Mix Software).zip", + "disk0": "TRAPFALL.DSK", + "joy-right": "kjoy0", + "basic": "LOADM\"TRAPFALL\":EXEC\n" + }, + "865345e66e8a03b5d3f489c324a33042955820bc": { + "filename": "Treasure Hunt (Color Computer Programmer Monthly).zip", + "disk0": "TREASURE.DSK", + "basic": "RUN\"TREASURE\"\n" + }, + "53182529685f7629d2daffcc2e2a3bbb687944ef": { + "filename": "Treasure Hunt (Saguaro Software).zip", + "disk0": "TRHUNT.DSK", + "basic": "RUN\"TR\"\n" + }, + "6c7540895f812fb04271668404e69ee762abe240": { + "filename": "Treasure Island Defense v1.1 (Sheldon MacDonald) (CoCoVGA) (Coco 3).zip", + "machine": "coco3", + "disk0": "TID.DSK", + "joy-left": "mjoy0", + "basic": "LOADM\"TID\":EXEC\n" + }, + "929812fcd7d6b9ff1003149a81503e4f54e48b62": { + "filename": "Treasure of the Aztec (Computerware) (SSC).zip", + "disk0": "AZTEC-ALT.DSK", + "basic": "LOADM\"AZTECS\":EXEC\n", + "tv-input": "redblue" + }, + "917408da6600384c5509446af51a4480da5e25b3": { + "filename": "Treasure of the Aztec (Computerware) (SSC).zip", + "disk0": "AZTEC-ALT.DSK", + "basic": "LOADM\"AZTECS\":EXEC\n", + "tv-input": "redblue" + }, + "e08f664859148ee237cb69407b5a8de72d85695a": { + "filename": "Trekboer (Mark Data Products).zip", + "disk0": "TREKBOER.DSK", + "basic": "LOADM\"TREKBOER\":EXEC\n", + "tv-input": "redblue" + }, + "65b490ee95149579b2a5eb8b4d4fe771deff58d6": { + "filename": "Treasury Pak (Spectral Associates).zip", + "disk0": "Treasury2.dsk", + "joy-right": "kjoy0", + "basic": "DIR\n" + }, + "be653aaf398b83f5b6146f9b86a45b61235f4f24": { + "filename": "Treasury Pak (Spectral Associates).zip", + "disk0": "Treasury2.dsk", + "joy-right": "kjoy0", + "basic": "DIR\n" + }, + "69ebbd6d10a4a7c0564e76e0f637bd9364715d3d": { + "filename": "Trek Adventure (Aardvark Technical Services).zip", + "disk0": "TREK-ADV.DSK", + "basic": "RUN\"TREK-ADV\"\n" + }, + "dca885576bfec40560676f330055e7e90c594677": { + "filename": "Trubble (John Demchenko) (Coco 3).zip", + "machine": "coco3", + "disk0": "TRUBBLE.DSK", + "basic": "RUN\"TRUBBLE\"\n" + }, + "b26b5354b3ce10237041fff6dcf3cbf3b51f80a8": { + "filename": "Trickashay (AHL Computing).zip", + "disk0": "TRICKASH.DSK", + "joy-left": "mjoy0", + "joy-right": "kjoy0", + "basic": "LOADM\"TRICKASH\":EXEC\n" + }, + "c0dde3d280c5f5fdd9ca1884503ab96420d45072": { + "filename": "Trip, The (The Rainbow).zip", + "disk0": "THETRIP.DSK", + "joy-right": "kjoy0", + "basic": "RUN\"BOOT\"\n" + }, + "754f599470d347013ae763d418b3fad2b95b2763": { + "filename": "Tube Frenzy (Aadvark-80).zip", + "disk0": "TUBFREN.DSK", + "joy-right": "kjoy0", + "basic": "LOADM\"TUBFREN\":EXEC\n" + }, + "d769dc0f2ae5d35673b3cf7a5d8dd26026dba4f4": { + "filename": "Tunnel Racer (T&D Software).zip", + "disk0": "TUNNEL.DSK", + "joy-right": "kjoy0", + "basic": "LOADM\"TUNNEL\":EXEC\n" + }, + "296283e443bfc9528ef585f78289170c9a3deba0": { + "filename": "Tube Way Army (Crystal Computing).zip", + "disk0": "TUBEARMY.DSK", + "joy-left": "kjoy0", + "basic": "LOADM\"TUBEARMY\":EXEC\n" + }, + "6dd7119450e30daea6d0877d7c508c1bcd6d19e6": { + "filename": "Turret (T&D Software).zip", + "disk0": "TURRET.DSK", + "basic": "RUN\"TURRET\"\n" + }, + "840c195caec69357de069aa4eda0c4724868031e": { + "filename": "Tunnel (Unknown author).zip", + "disk0": "TUNNEL.DSK", + "joy-right": "kjoy0", + "basic": "LOADM\"TUNNEL\":EXEC\n" + }, + "f84f9792ea12277762c238b561ef0d6e03db8bac": { + "filename": "Turtle Attack (Triple-C Software).zip", + "disk0": "TURTLE.DSK", + "joy-left": "kjoy0", + "basic": "LOADM\"TURTLE\":EXEC\n" + }, + "050d99cdc02160b5e79257d61356760e5a982aad": { + "filename": "Tut's Tomb (Mark Data Products).zip", + "disk0": "TUTSTOMB.DSK", + "joy-right": "kjoy0", + "basic": "LOADM\"TUTSTOMB\":EXEC\n" + }, + "8b5425e74ac893d8716c1213dddd12174183fa03": { + "filename": "Tutankam (Aadvark-80).zip", + "disk0": "TUTANKAM.DSK", + "joy-right": "kjoy0", + "basic": "LOADM\"TUTANKAM\":EXEC\n" + }, + "771c4c93c087a0ee1c92bcb6e70a887713ca8ca1": { + "filename": "U-Boat (Andrew McLintock).zip", + "disk0": "U-BOAT.DSK", + "basic": "RUN\"U-BOAT\"\n" + }, + "2fd04de98487f4bd00597c355c9b1b37f035244a": { + "filename": "Unatron (K. J. Dowd).zip", + "disk0": "Unatron.dsk", + "joy-right": "kjoy0", + "basic": "LOADM\"UNATRON\":EXEC\n" + }, + "cf16093d218d8599a971ffae99d8a62f7fc47671": { + "filename": "Ugh! (Softek International Limited).zip", + "disk0": "UGH.DSK", + "joy-right": "kjoy0", + "basic": "LOADM\"UGH!\":EXEC\n" + }, + "5f50d8be98c66916f317a941a5dfb83a16a751e7": { + "filename": "Usetown Annex Project (Paul French).zip", + "disk0": "USETOWN.DSK", + "basic": "RUN\"USETOWN\"\n" + }, + "b77715f585ac24934f749f9362dd9d28361a9f70": { + "filename": "Utopian (CLOAD).zip", + "disk0": "UTOPIAN.DSK", + "basic": "RUN\"UTOPIAN\"\n" + }, + "0fccc458d3287cc849abf1ed50fd6f95f2690612": { + "filename": "Vampire Castle Beta 4 (Richard Kelly) (Coco 3).zip", + "machine": "coco3", + "disk0": "VamCasB4.dsk", + "basic": "RUN\"VAMCASB4\"\n" + }, + "96721ea9995614dc7defe2f202ca4a26917cfc5f": { + "filename": "Vampire Castle (Aardvark).zip", + "disk0": "VCASTLE.DSK", + "basic": "RUN\"VCASTLE\"\n" + }, + "227419ab768a93d9bd09a5435f5aa138cb952982": { + "filename": "VC (Microcomputer Games).zip", + "disk0": "VC.DSK", + "joy-right": "kjoy0", + "basic": "LOADM\"VC\":EXEC\n" + }, + "9e9e649b0080a8e35dcc05b16c48d70494002ecb": { + "filename": "Vegas Gamepak (Nelson Software Systems).zip", + "disk0": "VEGAS.DSK", + "joy-right": "kjoy0", + "basic": "LOADM\"VEGAS\":EXEC\n" + }, + "1ab5c7ae70f4562087d12bec1d55305a9623c09b": { + "filename": "Varloc (Tandy).zip", + "disk0": "varloc.dsk", + "joy-right": "mjoy0", + "basic": "DOS\n" + }, + "f5095bd44cb2a4684a7a4f84061ae4437f69f6eb": { + "filename": "Varloc (Tandy).zip", + "disk0": "varloc.dsk", + "joy-right": "mjoy0", + "basic": "DOS\n" + }, + "4014936bd4dff47f6f082c090506edb73a644ba6": { + "filename": "Vegas Game Pak (Tom Mix Software).zip", + "disk0": "VEGAS.DSK", + "basic": "DIR\n" + }, + "b0ce0b1ad734dabcf460481b501ae1ee9aa69874": { + "filename": "Vegas Slots (Tom Mix Software) (Coco 3).zip", + "machine": "coco3", + "disk0": "VEGAS.DSK", + "joy-right": "kjoy0", + "basic": "DIR\n" + }, + "b15b368512c5b23a81b5d8f5717b90e560f3e599": { + "filename": "Venturer (Aadvark-80).zip", + "disk0": "VENTURER.DSK", + "joy-right": "kjoy0", + "basic": "LOADM\"VENTURER\":EXEC\n" + }, + "92defaf2f0bd66bef6be954eb802b1e7e52e45b8": { + "filename": "Video Cards and Keno (Tom Mix Software) (Coco 3).zip", + "machine": "coco3", + "disk0": "CASINO.DSK", + "basic": "RUN\"DOS\"\n" + }, + "10eb1f0bfcad1efac3f9c54433acbb0ee5693e6f": { + "filename": "Vicious Vic (The Rainbow).zip", + "disk0": "VIC.DSK", + "basic": "RUN\"VIC\"\n" + }, + "342bfc659e7aedf10347ed491b8158e7ab83933f": { + "filename": "Video Keno (Tom Mix Software).zip", + "disk0": "KENO.DSK", + "basic": "LOADM\"KENO\":EXEC\n" + }, + "77f8fc221ce97715035c9ed70b5c945226546af6": { + "filename": "Video Pinball (Tandy).zip", + "disk0": "PINBALL.DSK", + "joy-left": "kjoy0", + "joy-right": "mjoy0", + "basic": "LOADM\"PINBALL\":EXEC\n" + }, + "e5a6b41dd08759c74e1b5189127ff9c6c3852e2e": { + "filename": "Video Poker Coco 3 Edition (Paul Shoemaker) (Coco 3).zip", + "machine": "coco3", + "disk0": "VPOKER3.DSK", + "basic": "LOADM\"VPOKER3\":EXEC\n" + }, + "55800515fb9ee0b5fb59c20eded9c0c07481d47f": { + "filename": "Video Poker Standard Edition (Paul Shoemaker).zip", + "disk0": "VPOKER.DSK", + "basic": "LOADM\"VPOKER\":EXEC\n" + }, + "23de6e0d0fd97f0bb63e9cfbc275ad940e382bac": { + "filename": "Viking II (Prickly-Pear Software).zip", + "disk0": "VIKINGII.DSK", + "basic": "RUN\"VIKINGII\"\n" + }, + "e1bf252d3b6d74c4d85f90ecf94db0f6cf0ce855": { + "filename": "Viper Force (Hot Coco).zip", + "disk0": "VIPER.DSK", + "joy-right": "kjoy0", + "basic": "RUN\"VIPER\"\n", + "tv-input": "redblue" + }, + "c2c4ae1240eaf402023ccd553f6677855608b6a6": { + "filename": "Viking (Prickly-Pear Software).zip", + "disk0": "viking.dsk", + "basic": "RUN\"VIKING\"\n" + }, + "adda9deb0dd69cf33e25972da95750b1330923cc": { + "filename": "Vocab (Rick's Computer Enterprise) (Coco 3).zip", + "machine": "coco3", + "disk0": "VOCAB.DSK", + "basic": "RUN\"VOCAB\"\n" + }, + "d83509484531defa68bbe220ed8b0f055731fd12": { + "filename": "Vortex Factor, The (Mark Data Products).zip", + "disk0": "VORTEX.DSK", + "basic": "LOADM\"VORTEX\":EXEC\n", + "tv-input": "redblue" + }, + "8b5fa93fca29c6baa424b1fb90b77cf2b31408bd": { + "filename": "Vox Chess (Computerware).zip", + "disk0": "VOXCHESS.DSK", + "basic": "LOADM\"VOXCHESS\":EXEC\n" + }, + "51693ec622090df925cf7e22b8dc0836fa7fba3f": { + "filename": "Voyager I (Avalon Hill).zip", + "disk0": "VoyagerI.dsk", + "joy-right": "kjoy0", + "basic": "RUN\"VOY-1\"\n" + }, + "903ecbe29b488b2b64914939b5b1256fb5ebd1a1": { + "filename": "Voyagers (Color Horizon Software).zip", + "disk0": "VOYAGERS.DSK", + "joy-right": "kjoy0", + "basic": "LOADM\"VOYAGERS\":EXEC\n" + }, + "a3a92ac820124166945a1dad53739b3cffedf3b1": { + "filename": "Voyage (The Rainbow).zip", + "disk0": "VOYAGE.DSK", + "joy-right": "kjoy0", + "basic": "RUN\"BOOT\"\n" + }, + "ac5475dc69e107d042a2b19c7235474354a6bcb2": { + "filename": "Vultures (J. Morrison Micros) (SG-8 intro).zip", + "disk0": "VULTURES.DSK", + "joy-right": "kjoy0", + "basic": "LOADM\"VULTURES\":EXEC\n" + }, + "4483a835ae76c249550a6218698a0ea86e4a41f3": { + "filename": "Wacky Food (Arcade Animation Inc.).zip", + "disk0": "WCKYFOOD.DSK", + "joy-right": "kjoy0", + "basic": "LOADM\"WCKYFOOD\":EXEC\n" + }, + "8368a772025a57ae8b49b10561fc3f13abaa0fb2": { + "filename": "Wargame (T&D Software).zip", + "disk0": "wargame.dsk", + "basic": "RUN\"WARGAME\"\n" + }, + "06ecf15f1c34c0a22cca30cc1c2200d28ad9c8d1": { + "filename": "Wargame (T&D Software) (alt).zip", + "disk0": "WARGAME.DSK", + "basic": "RUN\"WARGAME\"\n" + }, + "d86d2cc4db309309b6452fffbe9fa08c3581a23e": { + "filename": "Warehouse Mutants (Tom Mix Software).zip", + "disk0": "MUTANT.DSK", + "joy-right": "kjoy0", + "basic": "LOADM\"MUTANT\":EXEC\n" + }, + "14045d85a305642d269799f645a0039f1a55ea8d": { + "filename": "Warkings (Tom Mix Software).zip", + "disk0": "WARKINGS.DSK", + "joy-left": "mjoy0", + "joy-right": "kjoy0", + "basic": "LOADM\"WARKINGS\":EXEC\n" + }, + "0d836bf79f5b656752faf4fb21f7cc30159400be": { + "filename": "War of the Worlds 2 - The Quest (Triad Pictures Corporation).zip", + "disk0": "WOW2.DSK", + "joy-right": "mjoy0", + "basic": "RUN\"WOW2\"\n" + }, + "b52be4382ed0b8018d4de5ffa2bdeedf5003e4e0": { + "filename": "War of the Worlds 1 - The Landing (Triad Pictures Corporation).zip", + "disk0": "WOW1.DSK", + "joy-right": "mjoy0", + "basic": "RUN\"WOW1\"\n" + }, + "9346b372635dda28b4934eaf41afa15590b99f17": { + "filename": "War of the Worlds 3 - The Last Hope (Triad Pictures Corporation).zip", + "disk0": "WOW3.DSK", + "joy-right": "mjoy0", + "basic": "RUN\"WOW3\"\n" + }, + "c3fbd790cc2b373324ab4deffb6cb1b4b51d83c4": { + "filename": "Warp Fighter 3D (SRB Software) (Coco 3).zip", + "machine": "coco3", + "disk0": "WARP3D.DSK", + "joy-right": "kjoy0", + "basic": "LOADM\"WARP3D\":EXEC\n" + }, + "533c2757a76657faeee18f2e261bb1378c6c3c04": { + "filename": "Warp Factor X (Prickly-Pear Software).zip", + "disk0": "WARPX.DSK", + "basic": "RUN\"WARP\"\n" + }, + "57d5799f37b8157c5239e1502fae6c26586d81eb": { + "filename": "Warp (T&D Software).zip", + "disk0": "WARP.DSK", + "basic": "RUN\"WARP\"\n" + }, + "c09a0ac12fa91c7abc33664cd4072419dbfd71f7": { + "filename": "War (Tandy).zip", + "disk0": "WAR.DSK", + "basic": "RUN\"WAR\"\n" + }, + "06329ba1beecee740532554d7ab22864c38aa67a": { + "filename": "Warrior (GOSUB International).zip", + "disk0": "WARRIOR.DSK", + "basic": "RUN\"WARRIOR\"\n" + }, + "934bae26dd3966879050aeb0cbb7c38983c9cb6e": { + "filename": "Waterfall (Nick Marentes) (SSC).zip", + "machine": "coco2bus", + "disk0": "WATRFALL.DSK", + "joy-right": "kjoy0", + "basic": "RUN\"WATRFALL\"\n" + }, + "2fc5e96d827b57f8d6e1f83120bf46373cfe2391": { + "filename": "Waterloo (Ark Royal Games).zip", + "disk0": "WATERLOO.DSK", + "basic": "RUN\"WATERLOO\"\n" + }, + "b2e365140e89345d58dacb5d24e9569b90e62350": { + "filename": "Weirdo (T&D Software).zip", + "disk0": "WEIRDO.DSK", + "joy-right": "kjoy0", + "basic": "LOADM\"WEIRDO\":EXEC\n", + "tv-input": "redblue" + }, + "7c403332eead12225de4a1e7d5cc2cb0f460015a": { + "filename": "Werewolves and Wanderers (Tim Hartnell) (Coco 3).zip", + "machine": "coco3", + "disk0": "WEREWOLF.DSK", + "basic": "RUN\"WEREWOLF\"\n" + }, + "0174f4bf4129f3b60cf6ea5058ad7f8970d56101": { + "filename": "Wet T-Shirt Contest (Softcore Software Company).zip", + "disk0": "WETSHIRT.DSK", + "basic": "LOADM\"WETSHIRT\":EXEC\n" + }, + "21a3b9a98eb75bb6fae77ecf6eed0d983cd54345": { + "filename": "Welcome to the Jungle (Prickly-Pear Software).zip", + "disk0": "JUNGLE.DSK", + "basic": "RUN\"JUNGLE\"\n" + }, + "1e69afa5ecd61ec0347962b460fd5ba68009746f": { + "filename": "Whatzit (Randall A. Smith).zip", + "disk0": "WHATZIT.DSK", + "basic": "RUN\"WHATZIT\"\n" + }, + "1893f68833db59ec26bcd51b02fe1896e4293c1a": { + "filename": "Wheeler Dealer, The (Prototype) (L. Todd Knudsen) (Coco 3).zip", + "machine": "coco3", + "disk0": "WHEELER.DSK", + "basic": "RUN\"BOOT\"\n" + }, + "badd4b66819eb2529163c21994fe032665c63c6f": { + "filename": "Wheel of Fortune (SPORTSware) (Coco 3).zip", + "machine": "coco3", + "disk0": "WHEEL.DSK", + "basic": "RUN\"M\"\n" + }, + "726465107fcf4e06cf320ab39123f513160498b8": { + "filename": "Where's Santa's Landing (Joey Latimer).zip", + "disk0": "SANTA.DSK", + "basic": "RUN\"SANTA\"\n" + }, + "00a898ad72ba6f81a8c28af32edac1fbe4af821a": { + "filename": "Wheel of Money (David Richards) (Coco 3).zip", + "machine": "coco3", + "disk0": "WHEELOF.DSK", + "basic": "RUN\"WHEEL\"\n" + }, + "f74e49d679202560a06853b438723b0b1371e6cf": { + "filename": "Whirlybird Run (Spectral Associates).zip", + "disk0": "WHIRLY.DSK", + "joy-right": "kjoy0", + "basic": "LOADM\"WHIRLY\":EXEC\n" + }, + "34f0c3d22f963e70a311cbb48a66c727dcf0e2dc": { + "filename": "Wiggle Worm (Chromasette).zip", + "disk0": "WIGWORM.DSK", + "joy-right": "mjoy0", + "basic": "LOADM\"WIGWORM\":EXEC\n" + }, + "de65dd60aee6bf58da6300be315bacdec527b6f1": { + "filename": "Wiggle Worm (Chromasette) (Coco 3 fix).zip", + "disk0": "WIGWORM.DSK", + "joy-right": "mjoy0", + "basic": "LOADM\"WIGWORM\":EXEC\n" + }, + "35683d2baa5fa42415885eae4ad6640f622c93c6": { + "filename": "Wildcatting (Tandy).zip", + "disk0": "WILDCAT.DSK", + "joy-left": "mjoy0", + "basic": "LOADM\"WILDCAT\":EXEC\n" + }, + "34900e10479df3fd48d9a1befd3b95459c3a7624": { + "filename": "Willy's Warehouse (Intracolor).zip", + "disk0": "WAREHOUS.DSK", + "joy-left": "kjoy0", + "basic": "LOADM\"WAREHOUS\":EXEC\n", + "tv-input": "redblue" + }, + "f77394ed5446c6e8daf781bb1c1cf54ced403bde": { + "filename": "Wild Party (B&B Software).zip", + "disk0": "WILDPRTY.DSK", + "basic": "RUN\"WILDPRTY\"\n" + }, + "09db973f3c313a03dbf620ca4d0f1ed7d5b65d30": { + "filename": "Wimpy (Gregory Clark) (Coco 3).zip", + "machine": "coco3", + "disk0": "WIMPY.DSK", + "basic": "RUN\"WIMPY\"\n" + }, + "49290708f859baf4fb2ae10bb2794c918d507fd0": { + "filename": "White Fire of Eternity (Saguaro Software).zip", + "disk0": "Whitefire.dsk", + "basic": "LOADM\"BOOT\":EXEC\n" + }, + "82347bd942fc3d8586ed0db12c8fb9731f10d660": { + "filename": "White Fire of Eternity (Saguaro Software).zip", + "disk0": "Whitefire.dsk", + "basic": "LOADM\"BOOT\":EXEC\n" + }, + "d923304aa2c1948eea23511a764310b39971a13a": { + "filename": "White Fire of Eternity (Saguaro Software).zip", + "disk0": "Whitefire.dsk", + "basic": "LOADM\"BOOT\":EXEC\n" + }, + "ef70b91a8f4b0f5138e302f4a9de798ba5ffcc9e": { + "filename": "White Fire of Eternity (Saguaro Software).zip", + "disk0": "Whitefire.dsk", + "basic": "LOADM\"BOOT\":EXEC\n" + }, + "be0f65740a5bbb8510c786d51ca4a0321c626232": { + "filename": "Wizard Adventure (T&D Software).zip", + "disk0": "WIZARD.DSK", + "basic": "RUN\"WIZARD\"\n" + }, + "2711fc712e224b867d9bbcdfdc94bc187c8714c2": { + "filename": "Wizard's Castle (Spectral Associates) (OS-9) (SSC).zip", + "disk0": "WIZCASTL.DSK", + "basic": "DOS\n" + }, + "71c41fa6e9a188cd15d2090cd67b8b936afbc8f0": { + "filename": "Wizard's Castle, The (Public Domain).zip", + "disk0": "WIZCASTL.DSK", + "basic": "RUN\"WIZCASTL\"\n" + }, + "6fe07412cfb52e799eac96d1d1c49b9ae5abb35d": { + "filename": "Wizards II (David Lionell Dawson) (Coco 3).zip", + "machine": "coco3", + "disk0": "wizards.dsk", + "basic": "RUN\"WIZINS\"\n" + }, + "b7570d5349b0144e2577728f2e139f0ddeba65a1": { + "filename": "Wizard's Den (Tom Mix Software).zip", + "disk0": "WIZDEN.DSK", + "joy-right": "kjoy0", + "basic": "RUN\"BOOT\"\n", + "tv-input": "redblue" + }, + "bdd00bf7ee171a57ab33cecaa31677743203bb2d": { + "filename": "Wizard's Quest (Microdeal Cornwall).zip", + "disk0": "WIZQUEST.DSK", + "joy-right": "kjoy0", + "basic": "LOADM\"WIZQUEST\":EXEC\n" + }, + "9d9cce9bf7f507600f3e176338d6beec4f050eb7": { + "filename": "Wizard's Tomb, The (Chris Hawks).zip", + "disk0": "WIZTOMB.DSK", + "basic": "RUN\"WIZDTOMB\"\n" + }, + "46ba87506afb37f8062e05decabfecfe277d6694": { + "filename": "Word Hunt (Jeffery A. Stipes) (Coco 3).zip", + "machine": "coco3", + "disk0": "WORDHU.DSK", + "basic": "RUN\"WORDHU\"\n" + }, + "7f7ff375a9970ffd57aef852a422b077c581c0f9": { + "filename": "Wizard's Tower, The (Aardvark).zip", + "disk0": "wiztower.dsk", + "basic": "RUN\"WIZARDS\"\n" + }, + "1e1a02bdb85fcc4e22ab1d9b54a549496465e515": { + "filename": "World Conquest (T&D Software).zip", + "disk0": "WORLDCON.DSK", + "basic": "RUN\"WORLDCON\"\n" + }, + "3a633ab3520bf1cb37ef3d86d805980cf01a7bf5": { + "filename": "World Class Cribbage (Bob van der Poel) (OS-9) (Coco 3).zip", + "machine": "coco3", + "disk0": "CRIBOS-9.dsk", + "joy-right": "mjoy0", + "basic": "DOS\n" + }, + "c257c6c25dd2b018fbdee44784f8a4f97e717d33": { + "filename": "World Master II (Dough Tuttle) (Coco 3).zip", + "machine": "coco3", + "disk0": "WMASTER.DSK", + "joy-right": "kjoy0", + "basic": "LOADM\"WMASTER\":EXEC\n" + }, + "4730c033a9dec7aa0ed260834c8e20087e94e841": { + "filename": "World Weight Lifting (Australian Coco Magazine) (SSC).zip", + "machine": "coco3", + "disk0": "WWFLIFT.DSK", + "joy-right": "kjoy0", + "basic": "RUN\"WWFLIFT\"\n", + "tv-input": "bluered" + }, + "6c1490b19be94ae4ad665f33858472195b1d46f0": { + "filename": "Worlds of Flight (Tom Mix Software).zip", + "disk0": "WOF.DSK", + "joy-left": "mjoy0", + "basic": "LOADM\"WOF\":EXEC\n" + }, + "bee8c4eb718c25901773b671ea799661903faf2e": { + "filename": "Wormtube (John Bobst) (SG-24) (Coco 1-2).zip", + "disk0": "WORMTUBE.DSK", + "joy-right": "mjoy0", + "basic": "LOADM\"WORMTUBE\":EXEC\n" + }, + "39356118d650af19fdee8329afe9dfcd134f05d2": { + "filename": "Wrestle Maniac (Diecom Products).zip", + "disk0": "Wrestle Maniac.dsk", + "joy-right": "kjoy0", + "basic": "LOADM\"WRESTLE\":EXEC\n", + "tv-input": "redblue" + }, + "040d9df9f0b57aea219f59067da219303f14ba6e": { + "filename": "Wrestle Maniac (Diecom Products).zip", + "disk0": "Wrestle Maniac.dsk", + "joy-right": "kjoy0", + "basic": "LOADM\"WRESTLE\":EXEC\n", + "tv-input": "redblue" + }, + "67cd37e0c5e9d8efe7e6fc8b90d71b1621e9ea87": { + "filename": "Xenocide (Alison deNu).zip", + "disk0": "XENOCIDE.DSK", + "joy-right": "kjoy0", + "basic": "RUN\"XENOCIDE\"\n" + }, + "35b39618f86d0e5e9c0b300b5274ef150a4d3cc0": { + "filename": "Xmas Rush (John Linville).zip", + "disk0": "xmasrush.dsk", + "joy-left": "kjoy0", + "basic": "LOADM\"XMASRUSH\":EXEC\n" + }, + "e00d3f96e292eab1d2f5565d1a655323c9318b70": { + "filename": "X-Rated Adventure Game (Y. K. Supersoft).zip", + "disk0": "X-ADVENT.DSK", + "basic": "RUN\"DR\"\n" + }, + "4a76272435fef9500c471d0c3a80ea2c9b1678ba": { + "filename": "Xygoid (Public Domain).zip", + "disk0": "XYGOID.DSK", + "basic": "LOADM\"XYGOID\":EXEC\n" + }, + "5cc67cf832d31a87f71d4c7608e31cbade15b11c": { + "filename": "YahtCC (Spectral Associates).zip", + "disk0": "YAHTCC.DSK", + "basic": "RUN\"YAHTCC\"\n" + }, + "240866515421aa198dfae9be40ce8a6c54f26094": { + "filename": "Yahtzee 3 (T&D Software) (Coco 3).zip", + "machine": "coco3", + "disk0": "YAHTZEE3.DSK", + "basic": "RUN\"YAHTZEE3\"\n" + }, + "54bea95e59f3e73152c5211ac65fa3c3ddda2ebe": { + "filename": "Yahtzee (GOSUB International).zip", + "disk0": "YAHTZEE.DSK", + "basic": "RUN\"YAHTZEE\"\n" + }, + "b174c19248f1f8bf0253c078d71ee28fbe3cb855": { + "filename": "Yahtzee (Jim Peasley) (Coco 3).zip", + "machine": "coco3", + "disk0": "YAHTZEE.DSK", + "basic": "RUN\"YAHTZEE\"\n" + }, + "d0a6474bc842e50fbba1e67c67586792c94a0f31": { + "filename": "Yahtzee (Kevin Gowan).zip", + "disk0": "YAHTZEE.DSK", + "basic": "RUN\"YAHTZEE\"\n" + }, + "42b55ed7207bf82eb03582b9903a268a7ccdb9b6": { + "filename": "Yahtzee (T&D Software).zip", + "disk0": "YAHTZEE.DSK", + "joy-right": "kjoy0", + "basic": "RUN\"YAHTZEE\"\n" + }, + "27de812de956927fe0ab8a01816e03248d381423": { + "filename": "Yellow Submarine (The Rainbow).zip", + "disk0": "YELLOW.DSK", + "basic": "RUN\"SUBINTRO\"\n" + }, + "35c6c11279750c17c4d77e444fc072fb3d300b97": { + "filename": "Z-89 v1.01 (Game Point Software) (Coco 3).zip", + "machine": "coco3", + "disk0": "Z-89 v1.01 (Game Point Software) (Coco 3)/z89.dsk", + "joy-right": "kjoy0", + "basic": "DOS\n" + }, + "140cbcaad396718789b17c0c51af6ad16a21888b": { + "filename": "Z-89 v1.01 (Game Point Software) (Coco 3).zip", + "machine": "coco3", + "disk0": "Z-89 v1.01 (Game Point Software) (Coco 3)/z89.dsk", + "joy-right": "kjoy0", + "basic": "DOS\n" + }, + "9f8b25499a2d88aca2d000d3f1d9a9aa039e1939": { + "filename": "Z-89 v1.03 (Game Point Software) (Coco 3).zip", + "machine": "coco3", + "disk0": "Z-89 v1.03 (Game Point Software) (Coco 3)/z89.dsk", + "joy-right": "kjoy0", + "basic": "DOS\n" + }, + "cd1cdad3a9e35c7d0d416cf7eeb076a6a52bec49": { + "filename": "Z-89 v1.03 (Game Point Software) (Coco 3).zip", + "machine": "coco3", + "disk0": "Z-89 v1.03 (Game Point Software) (Coco 3)/z89.dsk", + "joy-right": "kjoy0", + "basic": "DOS\n" + }, + "c317a547c21cef19c88aae41143d3eb078eae7df": { + "filename": "Z-89 v1.03 (Game Point Software) (Coco 3).zip", + "machine": "coco3", + "disk0": "Z-89 v1.03 (Game Point Software) (Coco 3)/z89.dsk", + "joy-right": "kjoy0", + "basic": "DOS\n" + }, + "d1a40a69897002e60dc64fd819df3877b7faad30": { + "filename": "Z-89 v1.03 (Game Point Software) (Coco 3).zip", + "machine": "coco3", + "disk0": "Z-89 v1.03 (Game Point Software) (Coco 3)/z89.dsk", + "joy-right": "kjoy0", + "basic": "DOS\n" + }, + "eb95b47f56a850913ba46a133f8dc61bfa762238": { + "filename": "Z-89 v1.03 (Game Point Software) (Coco 3).zip", + "machine": "coco3", + "disk0": "Z-89 v1.03 (Game Point Software) (Coco 3)/z89.dsk", + "joy-right": "kjoy0", + "basic": "DOS\n" + }, + "ac0f8c90bfb83fc4d4aa2e59ab48f4e5deddccc6": { + "filename": "Z-89 v1.03 (Game Point Software) (Coco 3).zip", + "machine": "coco3", + "disk0": "Z-89 v1.03 (Game Point Software) (Coco 3)/z89.dsk", + "joy-right": "kjoy0", + "basic": "DOS\n" + }, + "cc3cf2da6ad9f6d09bbf4c3f9394ee2e6ac79f4e": { + "filename": "Z-89 v1.03 (Game Point Software) (Coco 3).zip", + "machine": "coco3", + "disk0": "Z-89 v1.03 (Game Point Software) (Coco 3)/z89.dsk", + "joy-right": "kjoy0", + "basic": "DOS\n" + }, + "5ae392fc347b6e7a5740543b9d77a9181c88f9dc": { + "filename": "Z-89 v1.03 (Game Point Software) (Coco 3).zip", + "machine": "coco3", + "disk0": "Z-89 v1.03 (Game Point Software) (Coco 3)/z89.dsk", + "joy-right": "kjoy0", + "basic": "DOS\n" + }, + "c9669e2e1dd58f958032193f6312bb3b809e1317": { + "filename": "Z-89 v1.03 (Game Point Software) (Coco 3).zip", + "machine": "coco3", + "disk0": "Z-89 v1.03 (Game Point Software) (Coco 3)/z89.dsk", + "joy-right": "kjoy0", + "basic": "DOS\n" + }, + "6b88c32667f6ea6452a59164f16f0e21a20cac46": { + "filename": "Zaksund (Elite Software).zip", + "disk0": "ZAKSUND.DSK", + "joy-right": "kjoy0", + "basic": "LOADM\"ZAKSUND\":EXEC\n" + }, + "adcead2aa696874b8e43145352ebb2d03d081ab3": { + "filename": "Zandar (K-Soft) (Coco 3).zip", + "machine": "coco3", + "disk0": "ZANDAR.DSK", + "joy-right": "kjoy0", + "basic": "RUN\"ZANDAR\"\n" + }, + "c0822405cc68df8c3f450b778857d2fb857ff448": { + "filename": "Zaxxon (Datasoft).zip", + "disk0": "ZAXXON.DSK", + "joy-right": "kjoy0", + "basic": "LOADM\"ZAXXON\":EXEC\n" + }, + "97f3022735c957f3d12634b3a12645ab0e0a6b30": { + "filename": "Zector Adventure (T&D Software).zip", + "disk0": "zector.dsk", + "basic": "LOADM\"ZECTOR\":EXEC\n" + }, + "971fba728fa3b3cdb6a7e6a797ee873887e18f37": { + "filename": "Zenix Demo (Gosub Software) (Coco 3).zip", + "machine": "coco3", + "disk0": "ZENIXDEM.DSK", + "joy-right": "kjoy0", + "basic": "RUN\"ZDEMO\"\n" + }, + "f6f2b7936c73cb9cfb4d72d59d14ee876b88b846": { + "filename": "Zenix (Gosub Software) (Coco 3).zip", + "machine": "coco3", + "disk0": "ZENIX.DSK", + "joy-right": "kjoy0", + "basic": "LOADM\"ZENIX\":EXEC\n" + }, + "56902e36c2062f691260211386fc0abee6943443": { + "filename": "Zero-G (Chromasette).zip", + "disk0": "ZEROG.DSK", + "basic": "LOADM\"ZEROG\":EXEC\n" + }, + "6a6776666493124901aa0a100efaa7e96ae3e6a3": { + "filename": "Zeus (Aardvark-80).zip", + "disk0": "ZEUS.DSK", + "joy-right": "kjoy0", + "basic": "LOADM\"ZEUS\":EXEC\n" + }, + "c1de292850f7af127fa4de4923f4dd09557645dd": { + "filename": "Zeus (Shattered score fix) (Aardvark-80).zip", + "disk0": "ZEUS_FX.DSK", + "joy-right": "kjoy0", + "basic": "LOADM\"ZEUS\":EXEC\n" + }, + "62072b9785bd8ede8262ecd31c883f2b0bcbf0f9": { + "filename": "Zone 6 (Colorquest).zip", + "disk0": "ZONE6.DSK", + "basic": "LOADM\"ZONE6\":EXEC\n" + }, + "5ab66dea3c13e864a9b211aacba9681a2a404e19": { + "filename": "Zonerunner (Tandy) (OS-9) (Coco 3).zip", + "machine": "coco3", + "disk0": "ZONERUN.DSK", + "joy-right": "mjoy0", + "basic": "DOS\n" + }, + "fdd3ef04a6e2fcc854a467ae735f2af43c838af2": { + "filename": "Zonx (The Rainbow).zip", + "disk0": "ZONX.DSK", + "joy-right": "kjoy0", + "basic": "LOADM\"ZONX\":EXEC\n" + }, + "331e80d26885097388283f9534a9969bba929d68": { + "filename": "Action Réaction (French) (Coco 3).zip", + "machine": "coco3", + "disk0": "REACTION.DSK", + "basic": "LOADM\"ACTION\":EXEC\n" + }, + "801f57fae7799d191811b17419478dbb29ef4fa3": { + "filename": "Canyon Climber (Tandy) (French translation).zip", + "disk0": "CANYONF.DSK", + "joy-right": "kjoy0", + "basic": "LOADM\"CANYONF\":EXEC\n" + }, + "8460d2f0671b4109c8c0c940a6779f603d7d3bd2": { + "filename": "Chateau (French).zip", + "disk0": "CHATEAU.DSK", + "basic": "RUN\"CHATEAU\"\n" + }, + "3f2372d97990f3d85714736e72da3e09b452972f": { + "filename": "Chickens (French translation).zip", + "disk0": "POULETS.DSK", + "joy-right": "kjoy0", + "basic": "RUN\"POULETS\"\n" + }, + "f30ba9537af91215bd9466f5ef1961b494f636dd": { + "filename": "Clowns & Balloons (Tandy) (French translation).zip", + "disk0": "CLOWNSF.DSK", + "joy-right": "mjoy0", + "basic": "LOADM\"CLOWNSF\":EXEC\n" + }, + "db983568f52832f9cf8b686730886fb798cff54a": { + "filename": "Diggem (Chromasette) (French translation).zip", + "disk0": "DIGGEMF.DSK", + "joy-right": "kjoy0", + "basic": "RUN\"DIGGEMF\"\n" + }, + "90eaf697b3300c4356137e521ebfa16ff89261e1": { + "filename": "East-World Karate (The Saint John Gallery) (French translation).zip", + "disk0": "Karate Special.dsk", + "joy-left": "kjoy0", + "basic": "RUN\"KARATSPE\"\n" + }, + "3e65cb3261b52c5e8bc4fbf26546b13e128d6199": { + "filename": "Echecs (CSS) (French).zip", + "disk0": "echecs.dsk", + "basic": "RUN\"ECHECS\"\n" + }, + "47452f732665c0c4b4eb4aa0cc3a45d0991b64e3": { + "filename": "Find That Capital Letter (French translation).zip", + "disk0": "FINDLETF.DSK", + "basic": "RUN\"FINDLETF\"\n" + }, + "a1f272238e0b039d4d7858b327187278e7cad0be": { + "filename": "Find That Number (French translation).zip", + "disk0": "FINDNUMF.DSK", + "basic": "RUN\"FINDNUMF\"\n" + }, + "110b9668e7bc186a0b18226cfcbdbe194cc3770f": { + "filename": "Fléau (Lomiq) (French).zip", + "disk0": "FLEAU.DSK", + "basic": "RUN\"*\n", + "tv-input": "redblue" + }, + "88422dfc5c39fff0af83a86b10f56e84fb41ec37": { + "filename": "Gold Runner II Level Editor (French).zip", + "disk0": "Gold Runner II.dsk", + "joy-right": "kjoy0", + "basic": "RUN\"EDITRUN\"\n" + }, + "e37de61c9e60292cc1299eec2c127408f5eca1ef": { + "filename": "Gold Runner II Level Editor (French).zip", + "disk0": "Gold Runner II.dsk", + "joy-right": "kjoy0", + "basic": "RUN\"EDITRUN\"\n" + }, + "b2f42525f53405e0869f823207111b39272b9922": { + "filename": "Kiosque à limonade (Bill Batcher) (French).zip", + "disk0": "KIOSQUE2.DSK", + "basic": "RUN\"KIOSQUE2\"\n" + }, + "531a3d623bbe93c0250ae215bd416b8d2bc7bfc6": { + "filename": "Leo (Lomiq) (French).zip", + "disk0": "LEO.DSK", + "joy-right": "kjoy0", + "basic": "LOADM\"LEO\":EXEC\n" + }, + "315406060a401b78e4d5129bc2c2b8e30f260b5c": { + "filename": "Marble Maze (Diecom Products) (French translation).zip", + "disk0": "MARBLEF.DSK", + "joy-left": "kjoy0", + "basic": "LOADM\"MARBLEF\":EXEC\n" + }, + "e891ee9fe6a2abb7fbbfe471f147a76f9bcd9503": { + "filename": "Mastermind (Tom O'Brian) (French translation) (Coco 3).zip", + "machine": "coco3", + "disk0": "MASTERMF.DSK", + "basic": "RUN\"MASTERMF\"\n" + }, + "f3656717343ebbb9b2ce2993e0d155491ac00b77": { + "filename": "Miss Gobbler (Spectral Associates) (French translation).zip", + "disk0": "MISSGOBF.DSK", + "joy-right": "kjoy0", + "basic": "LOADM\"MISSGOBF\":EXEC\n" + }, + "2109806767360b49765ab7ec524309d50ebb8020": { + "filename": "Moneyopoly (Tom Mix Software) (French Translation).zip", + "disk0": "MONOF.DSK", + "joy-left": "mjoy0", + "joy-right": "mjoy0", + "basic": "LOADM\"MONOF\":EXEC\n" + }, + "0072f25b5c1c6dfc352521a817d027de759dd825": { + "filename": "Mudpies (Computer Shack) (French translation).zip", + "disk0": "MUDPIEF.DSK", + "joy-right": "kjoy0", + "basic": "LOADM\"MUDPIEF\":EXEC\n", + "tv-input": "redblue" + }, + "e98463183f48483a560a5d63c52be8c93262eaa5": { + "filename": "Nerble Force (Computerware) (French translation).zip", + "disk0": "NERBLEF.DSK", + "joy-right": "kjoy0", + "basic": "LOADM\"NERBLEF\":EXEC\n" + }, + "ecb36cb6ae22119749c4369f454885198cdfe9d5": { + "filename": "Pooyan (Tandy) (French translation).zip", + "disk0": "POOYANF.DSK", + "joy-right": "kjoy0", + "basic": "LOADM\"POOYANF\":EXEC\n" + }, + "e3c370c4afec501314f7baa880e28232fce889b8": { + "filename": "Quilles (Tropical Lanes) (T&D Software) (French translation).zip", + "disk0": "QUILLES.DSK", + "joy-right": "kjoy0", + "basic": "RUN\"QUILLES\"\n" + }, + "b52d6e8d237caaf8e2e61885047cb523217d062b": { + "filename": "Rainbow Book of Adventures, The (French translation).zip", + "disk0": "RAINADVF.DSK", + "joy-right": "kjoy0", + "basic": "DIR\n" + }, + "84d82f4277cb5d07522be8fead0ba9ffece7e30b": { + "filename": "Route des pionniers (French).zip", + "disk0": "PIONIER2.DSK", + "basic": "RUN\"PIONIER2\"\n" + }, + "7456b1c003d63f9ab15e3157026aece3aae254bf": { + "filename": "Sailor Man (Tom Mix Software) (French translation).zip", + "disk0": "SAILORF.DSK", + "joy-right": "kjoy0", + "basic": "LOADM\"SAILORF\":EXEC\n", + "tv-input": "redblue" + }, + "a3fa6aba4e2696e75143905ed914c5f1b543774b": { + "filename": "Shenanigans (Mark Data Products) (French translation).zip", + "disk0": "SHENAN-F.DSK", + "basic": "LOADM\"SHENAN-F\":EXEC\n" + }, + "cc359c5d52a779c906db7dca22153aa147462561": { + "filename": "Time Bandit (Computer Shack) (French translation).zip", + "disk0": "BANDITF.DSK", + "joy-left": "kjoy0", + "basic": "LOADM\"BANDITF\":EXEC\n" + }, + "b02d0acdc716f971827b34af3d56cecf8cce4454": { + "filename": "Annexion (Ediciel) (French).zip", + "disk0": "annexion.dsk", + "basic": "RUN\"ANNEXION\"\n" + }, + "922c3cab43c53d2da672bd0564338f3d6ae2ddc2": { + "filename": "Jeu de dames (Ediciel) (French).zip", + "disk0": "dame.dsk", + "basic": "RUN\"DAME\"\n" + }, + "1ad0e1e56149aa8295486deb727fa2af5378f3a6": { + "filename": "Télérama No. 1 (Vifi Nathan) (French).zip", + "disk0": "teleram1.dsk", + "basic": "DIR\n" + }, + "c61846225ed5b1d2e36d0c5bb5fe9f1e0830589b": { + "filename": "Télérama No. 2 (Vifi Nathan) (French).zip", + "disk0": "teleram2.dsk", + "basic": "DIR\n" + }, + "b03554a212e6f67bfcd6c9565cb6329b8a54aeb6": { + "filename": "Ballyhoo (Infocom).zip", + "disk0": "BALLYHOO.DSK", + "basic": "DOS\n" + }, + "298317131b2f7270ec66786d7436adb8a21e3d66": { + "filename": "Cutthroats (Infocom).zip", + "disk0": "CUTHROTS.DSK", + "basic": "DOS\n" + }, + "945242ebe1d1f9c7ccd022569a2055b8a4f527ec": { + "filename": "Deadline (Infocom).zip", + "disk0": "DEADLINE.DSK", + "basic": "DOS\n" + }, + "58c7f959305b138ff4529382822f7310b717c6c5": { + "filename": "Enchanter, The (Infocom).zip", + "disk0": "ENCHANTR.DSK", + "basic": "DOS\n" + }, + "9e654ecc42377d3954ea92c1063393d96c2360d3": { + "filename": "Four-In-One Sampler (Infocom).zip", + "disk0": "4-IN-1.DSK", + "basic": "DOS\n" + }, + "29644708ac3ff7966467207a43a616d83a0f0c84": { + "filename": "Hitchhiker's Guide to the Galaxy (Infocom).zip", + "disk0": "HTCHHKER.DSK", + "basic": "DOS\n" + }, + "4d8278d41685cd67e8ab3065eb9edba6d7a250a3": { + "filename": "Hollywood Hijinx (Infocom).zip", + "disk0": "HOLLYWD.DSK", + "basic": "DOS\n" + }, + "9705bc2eab35f108276fd75d8a39ad3231706c00": { + "filename": "Infidel (Infocom).zip", + "disk0": "INFIDEL.DSK", + "basic": "DOS\n" + }, + "a063da3197392b7f75ba47e05216f6dece63e428": { + "filename": "Leather Goddesses of Phobos (Infocom).zip", + "disk0": "LEATHER.DSK", + "basic": "DOS\n" + }, + "f49a5e4b1ce8d078c806ff891c06b5556e2d3e8e": { + "filename": "Lurking Horror, The (Infocom).zip", + "disk0": "LURKINGH.DSK", + "basic": "DOS\n" + }, + "86d10517c389021f5e5488cb6937251b64a66993": { + "filename": "Mini-ZORK (Infocom).zip", + "disk0": "MINIZORK.DSK", + "basic": "DOS\n" + }, + "10632e66cba333918e0e8577f5ecfab9a07fa010": { + "filename": "Moonmist (Infocom).zip", + "disk0": "MOONMIST.DSK", + "basic": "DOS\n" + }, + "d02ce3a20ef62cc8b79219038ddd12d1a93bf205": { + "filename": "Planetfall (Infocom).zip", + "disk0": "PLANETFL.DSK", + "basic": "DOS\n" + }, + "da704e6665875343c814ce820edc42c38bd20ce3": { + "filename": "Plundered Hearts (Infocom).zip", + "disk0": "HEARTS.DSK", + "basic": "DOS\n" + }, + "a2f101de9c6eb42fd86ee690a4afcdf61919b13b": { + "filename": "Sea Stalker (Infocom).zip", + "disk0": "SEASTALK.DSK", + "basic": "DOS\n" + }, + "3248bb2be5a898e29c584c6497c010ddb088a8b5": { + "filename": "Sorcerer (Infocom).zip", + "disk0": "SORCERER.DSK", + "basic": "DOS\n" + }, + "cedf073a1345f3b43312cb65b8799bab59772fc4": { + "filename": "Spellbreaker (Infocom).zip", + "disk0": "SPELLBRK.DSK", + "basic": "DOS\n" + }, + "b40a45d1029be6f1d14b321e0b76efa2487e3881": { + "filename": "StarCross (Infocom).zip", + "disk0": "STARCRSS.DSK", + "basic": "DOS\n" + }, + "1aa389f2b0f3530f531a4d5254ddc9b78b69c9b1": { + "filename": "Stationfall (Infocom).zip", + "disk0": "STATIONF.DSK", + "basic": "DOS\n" + }, + "4a9dfdc4832fa7f61af087f2cec6a25df3b43c63": { + "filename": "Suspect (Infocom).zip", + "disk0": "SUSPECT.DSK", + "basic": "DOS\n" + }, + "00258936e130efb6438044a6f2c0b01e5d250e39": { + "filename": "Suspended (Infocom).zip", + "disk0": "SUSPEND.DSK", + "basic": "DOS\n" + }, + "e42fae7c6fb0593f2d4ef67fcd617777e53a62ca": { + "filename": "Wishbringer (Infocom).zip", + "disk0": "WISHBRNG.DSK", + "basic": "DOS\n" + }, + "d3e02103050e7fd9b9a79f571da150c0d6d86449": { + "filename": "Witness, The (Infocom).zip", + "disk0": "WITNESS.DSK", + "basic": "DOS\n" + }, + "ad720c1402a53a846717b6aff65b1a165acd375d": { + "filename": "Zork III (Infocom).zip", + "disk0": "ZORK_III.DSK", + "basic": "DOS\n" + }, + "12f443fad8ccb7d232e6bc048e0a499d46c1d7e3": { + "filename": "Zork II (Infocom).zip", + "disk0": "ZORK_II.DSK", + "basic": "DOS\n" + }, + "1005612f871bcae5a5691da46c6657b922db390d": { + "filename": "Zork I (Infocom).zip", + "disk0": "ZORK_I.DSK", + "basic": "DOS\n" + }, + "8c563ae72fc182845b6d65065ac8ccd5f7439a56": { + "filename": "Ballyhoo (Infocom) (64 column) (Coco 3).zip", + "machine": "coco3", + "disk0": "BALLYHOO.DSK", + "basic": "DOS\n" + }, + "2214597507d267e7b5cd7449d5465aec97bec77b": { + "filename": "Cutthroats (Infocom) (64 column) (Coco 3).zip", + "machine": "coco3", + "disk0": "CUTHROTS.DSK", + "basic": "DOS\n" + }, + "13dcd1de5cdc801929cecbcafcb3f09ce677e3ff": { + "filename": "Deadline (Infocom) (64 column) (Coco 3).zip", + "machine": "coco3", + "disk0": "DEADLINE.DSK", + "basic": "DOS\n" + }, + "9d2e4083b356a422bdd66b177bf7d74d96140384": { + "filename": "Enchanter, The (Infocom) (64 column) (Coco 3).zip", + "machine": "coco3", + "disk0": "ENCHANTR.DSK", + "basic": "DOS\n" + }, + "d756f710cfcc1fec7d8470ff1a7825800434d99d": { + "filename": "Four-In-One Sampler (Infocom) (64 column) (Coco 3).zip", + "machine": "coco3", + "disk0": "4-IN-1.DSK", + "basic": "DOS\n" + }, + "3d630f54f4bf6b91ae627d9cfd22a338770cfc01": { + "filename": "Hitchhiker's Guide to the Galaxy (Infocom) (64 column) (Coco 3).zip", + "machine": "coco3", + "disk0": "HTCHHKER.DSK", + "basic": "DOS\n" + }, + "96235d1d1e18b674793c2f6a68ee300ade840f3e": { + "filename": "Hollywood Hijinx (Infocom) (64 column) (Coco 3).zip", + "machine": "coco3", + "disk0": "HOLLYWD.DSK", + "basic": "DOS\n" + }, + "e37fa206eb81dc115cfa6de99c2d523f7beaf5cd": { + "filename": "Infidel (Infocom) (64 column) (Coco 3).zip", + "machine": "coco3", + "disk0": "INFIDEL.DSK", + "basic": "DOS\n" + }, + "fad611814c9341cc4e4f2e28b58e129f1a634f0d": { + "filename": "Leather Goddesses of Phobos (Infocom) (64 column) (Coco 3).zip", + "machine": "coco3", + "disk0": "LEATHER.DSK", + "basic": "DOS\n" + }, + "1c4266439dcb399fa3754ddb402c95f8497780a7": { + "filename": "Lurking Horror, The (Infocom) (64 column) (Coco 3).zip", + "machine": "coco3", + "disk0": "LURKINGH.DSK", + "basic": "DOS\n" + }, + "b2daf01e2d1e4fb52b1b3c4a25e12aadef7bcd25": { + "filename": "Mini-ZORK (Infocom) (64 column) (Coco 3).zip", + "machine": "coco3", + "disk0": "MINIZORK.DSK", + "basic": "DOS\n" + }, + "9155b700f7281e59c1bc7c4683bce55ab4d28476": { + "filename": "Moonmist (Infocom) (64 column) (Coco 3).zip", + "machine": "coco3", + "disk0": "MOONMIST.DSK", + "basic": "DOS\n" + }, + "3ca1020e83bcb616634a8b63b958e1e18f8a3a1d": { + "filename": "Planetfall (Infocom) (64 column) (Coco 3).zip", + "machine": "coco3", + "disk0": "PLANETFL.DSK", + "basic": "DOS\n" + }, + "5171e71f23516f3957aa508e4a57fb8868da8346": { + "filename": "Plundered Hearts (Infocom) (64 column) (Coco 3).zip", + "machine": "coco3", + "disk0": "HEARTS.DSK", + "basic": "DOS\n" + }, + "e90242fac742b49b33384283017e0aa7e1534539": { + "filename": "Sea Stalker (Infocom) (64 column) (Coco 3).zip", + "machine": "coco3", + "disk0": "SEASTALK.DSK", + "basic": "DOS\n" + }, + "918e6fc6d4e9a9da0e4c82cd6d6852eec76978a0": { + "filename": "Sorcerer (Infocom) (64 column) (Coco 3).zip", + "machine": "coco3", + "disk0": "SORCERER.DSK", + "basic": "DOS\n" + }, + "1b9ed229e183dfe2490c4d9c951d5571f70166ba": { + "filename": "Spellbreaker (Infocom) (64 column) (Coco 3).zip", + "machine": "coco3", + "disk0": "SPELLBRK.DSK", + "basic": "DOS\n" + }, + "26787d71caac9bf11550ba726c10e56ac51da511": { + "filename": "StarCross (Infocom) (64 column) (Coco 3).zip", + "machine": "coco3", + "disk0": "STARCRSS.DSK", + "basic": "DOS\n" + }, + "968ad229d742ee45f0dfa96a76db3073a0c58fdc": { + "filename": "Stationfall (Infocom) (64 column) (Coco 3).zip", + "machine": "coco3", + "disk0": "STATIONF.DSK", + "basic": "DOS\n" + }, + "0f8d60e11773120a200745dae6ce5dbe802305b9": { + "filename": "Suspect (Infocom) (64 column) (Coco 3).zip", + "machine": "coco3", + "disk0": "SUSPECT.DSK", + "basic": "DOS\n" + }, + "b8ab0b44d989b1e9ca3e53d483fe866c467abd18": { + "filename": "Suspended (Infocom) (64 column) (Coco 3).zip", + "machine": "coco3", + "disk0": "SUSPEND.DSK", + "basic": "DOS\n" + }, + "3edce92cbf117327b0f99875421c56cea2baa460": { + "filename": "Wishbringer (Infocom) (64 column) (Coco 3).zip", + "machine": "coco3", + "disk0": "WISHBRNG.DSK", + "basic": "DOS\n" + }, + "8516dea9fa90e4ef999bd5041ee5346b1ef6261b": { + "filename": "Witness, The (Infocom) (64 column) (Coco 3).zip", + "machine": "coco3", + "disk0": "WITNESS.DSK", + "basic": "DOS\n" + }, + "b3f2fd85e36b6031d999dbf68979c14506ce4312": { + "filename": "Zork III (Infocom) (64 column) (Coco 3).zip", + "machine": "coco3", + "disk0": "ZORK_III.DSK", + "basic": "DOS\n" + }, + "f33b664a3e09fa2d67e00e45219433a63a3bb0d8": { + "filename": "Zork II (Infocom) (64 column) (Coco 3).zip", + "machine": "coco3", + "disk0": "ZORK_II.DSK", + "basic": "DOS\n" + }, + "b7ec43e4a4f7ea23371bd50f3bf41470fcbcd8d0": { + "filename": "Zork I (Infocom) (64 column) (Coco 3).zip", + "machine": "coco3", + "disk0": "ZORK_I.DSK", + "basic": "DOS\n" + }, + "521b5b9a0ecd44754529eeee9bb8eddb8869738b": { + "filename": "Aventura em San Quentin (Portuguese).zip", + "disk0": "SAN.DSK", + "basic": "RUN\"SAN\"\n" + }, + "65cc3d273eb090824d135ff9c4ca59ee608d4022": { + "filename": "Birds (Portuguese translation).zip", + "disk0": "PHOENIX.DSK", + "joy-right": "kjoy0", + "basic": "LOADM\"PHOENIX\":EXEC\n" + }, + "82806c2f21788ef86a1396d565a8dcbf6a93312e": { + "filename": "Blackjack (Tandy) (Portuguese Translation).zip", + "disk0": "21.DSK", + "basic": "RUN\"21\"\n" + }, + "c17320d08d0be5adaf81f5d6f96e532da5e69a7a": { + "filename": "Black Sanctum, The (Mark Data Products) (Portuguese translation).zip", + "disk0": "SANCTUMP.DSK", + "joy-right": "kjoy0", + "basic": "LOADM\"SANCTUMP\":EXEC\n", + "tv-input": "redblue" + }, + "3590fb2567cc05325ed0f80443a92b0019ab3c41": { + "filename": "Blackbeard's Island (Novasoft) (Portuguese translation).zip", + "disk0": "BLACKBPT.DSK", + "basic": "LOADM\"BLACKBPT\":EXEC\n" + }, + "316c9bab1addcfc55d457e100e9f375c0f45715b": { + "filename": "Bomba (Henio Bazerra) (Portuguese).zip", + "disk0": "BRITANIA.DSK", + "basic": "RUN\"BRITANIA\"\n" + }, + "a695fb1f855578d16e236410e16180e713516a8f": { + "filename": "Brew Master (Novasoft) (Portuguese translation).zip", + "disk0": "BREWP.DSK", + "joy-right": "kjoy0", + "basic": "LOADM\"BREWP\":EXEC\n" + }, + "aa2e70cf55d8789eb65213bcf0916a19cec3fe33": { + "filename": "Caladuril (Diecom Products) (Portuguese translation).zip", + "disk0": "CALP.DSK", + "basic": "LOADM\"CALP\":EXEC\n" + }, + "90518e4388a85bc932b618aa655be22b3d42a583": { + "filename": "Calixto Island (Mark Data Products) (Portuguese translation).zip", + "disk0": "CALIXTOP.DSK", + "basic": "LOADM\"CALIXTOP\":EXEC\n", + "tv-input": "redblue" + }, + "b2da60420194d3025152de5cadaa36aecc5a0f5d": { + "filename": "Canyon Climber (Tandy) (Portuguese Translation).zip", + "disk0": "CANYONP.DSK", + "joy-right": "kjoy0", + "basic": "LOADM\"CANYONP\":EXEC\n" + }, + "c4f468daed4acb894ec574d614acba9a7631f235": { + "filename": "Cashman (Computer Shack) (Portuguese Translation).zip", + "disk0": "CASHMANP.DSK", + "joy-left": "kjoy0", + "basic": "LOADM\"CASHMANP\":EXEC\n", + "tv-input": "redblue" + }, + "a2128fc68729cf2c5cdb9ea4ee24d9ffffab8ea3": { + "filename": "Castelo (Portuguese).zip", + "disk0": "CASTELO.DSK", + "joy-right": "kjoy0", + "basic": "RUN\"CASTELO\"\n" + }, + "d0230716c1d51ee87136ca2ed9d80b5ecfc3fcb6": { + "filename": "Color Blackjack (The Rainbow) (Portuguese translation).zip", + "disk0": "BLKJACK.DSK", + "basic": "RUN\"BLK JACK\"\n" + }, + "b98d6dbe657fc5f269e7ad0ad9e614cd5bb5297f": { + "filename": "Color Car Action (Novasoft) (Portuguese translation).zip", + "disk0": "COLORCAR.DSK", + "joy-left": "kjoy0", + "basic": "LOADM\"COLORCAR\":EXEC\n" + }, + "646fe8963cc9c021208964e52591d95150ed4f25": { + "filename": "Color Golf (Plansoft) (Portuguese).zip", + "disk0": "CLRGOLF.DSK", + "basic": "RUN\"CLRGOLF\"\n" + }, + "f4950106845592ad9e2a7310835472a14c504cc3": { + "filename": "Color Meteroids (Portuguese translation).zip", + "disk0": "COLORMET.DSK", + "joy-left": "mjoy0", + "joy-right": "kjoy0", + "basic": "LOADM\"COLORMET\":EXEC\n" + }, + "f91fe6620b8413047b1a26eee83694d8cf1d1b7f": { + "filename": "Color Space Invaders (Portuguese translation) (Spectral Associates).zip", + "disk0": "INVADER.DSK", + "basic": "LOADM\"INVADER\":EXEC\n" + }, + "c39b9329b0ba742206f4de0028dcfab850f02dd3": { + "filename": "Cuber (Tom Mix Software) (Portuguese translation).zip", + "disk0": "CUBER.DSK", + "joy-right": "kjoy0", + "basic": "LOADM\"CUBER\":EXEC\n" + }, + "c3cc5f3866adb7d5e2077e65b5029a6ee44709a5": { + "filename": "Demon Seed (Portuguese translation).zip", + "disk0": "DEMON.DSK", + "joy-right": "kjoy0", + "basic": "LOADM\"DEMON\":EXEC\n" + }, + "23094f22986ffb9e4bc7dd5aeff3a151b8aff6ba": { + "filename": "Desert Patrol (Arcade Animation Inc.) (Portuguese translation).zip", + "disk0": "PATROLP.DSK", + "joy-right": "kjoy0", + "basic": "LOADM\"PATROLP\":EXEC\n" + }, + "309430dfac94d0389bc98481931a9c02aae75cf1": { + "filename": "Desert Rider (Tandy) (Portuguese translation).zip", + "disk0": "DESERT2P.DSK", + "joy-right": "kjoy0", + "basic": "LOADM\"DESERT2P\":EXEC\n" + }, + "1a8c70fe3f3caa9681fc8f34c5cbade35a4c3404": { + "filename": "Desert Rider (Tandy) (Portuguese translation).zip", + "disk0": "DESERT2P.DSK", + "joy-right": "kjoy0", + "basic": "LOADM\"DESERT2P\":EXEC\n" + }, + "d238e6bd8903c6a628b60ffddd57f75674d88cfb": { + "filename": "Devious (Spectral Associates) (Portuguese translation).zip", + "disk0": "DEVIOUSP.DSK", + "joy-right": "kjoy0", + "basic": "LOADM\"DEVIOUSP\":EXEC\n" + }, + "cee36e649f5a5bebf7cdacd7cc3e77b4b35a7ad8": { + "filename": "Downland (Tandy) (Portuguese translation).zip", + "disk0": "DOWNLAND.DSK", + "joy-right": "kjoy0", + "basic": "LOADM\"DOWNLAND\":EXEC\n" + }, + "4244c573d87d4699fa19c84d91fa7c12747b6274": { + "filename": "Dunkey Monkey (Intellectronics) (Portuguese Translation).zip", + "disk0": "DONKEYM.DSK", + "joy-right": "kjoy0", + "basic": "LOADM\"DUNKEYM\":EXEC\n" + }, + "da63342d3fe6a32d34dfc05f0ad7acc04fe03256": { + "filename": "Excalibur (Portuguese).zip", + "disk0": "EXCALIBU.DSK", + "basic": "RUN\"EXCALIBU\"\n" + }, + "4e595f2aa08ffd035593306e52886d09eefe3e35": { + "filename": "F-16 Assault (Diecom Products) (SSC) (Portuguese translation).zip", + "disk0": "F16P.DSK", + "joy-right": "kjoy0", + "basic": "LOADM\"F16 P\":EXEC\n", + "tv-input": "redblue" + }, + "7451dcbe44366f95660afeb0217f1679572ffcdb": { + "filename": "F-16 Assault (Diecom Products) (SSC) (Portuguese translation).zip", + "disk0": "F16P.DSK", + "joy-right": "kjoy0", + "basic": "LOADM\"F16 P\":EXEC\n", + "tv-input": "redblue" + }, + "4270fbcc6fb55c80f6469d7aa88d4dfcde9f8179": { + "filename": "Froggie (Spectral Associates) (SSC) (Portuguese translation).zip", + "disk0": "FROGGIEP.DSK", + "joy-right": "kjoy0", + "basic": "LOADM\"FROGGIEP\":EXEC\n" + }, + "4ce47ce369a6a17ba92f7421f403a9dff6ff57b9": { + "filename": "Fury (Computer Shack) (Portuguese translation).zip", + "disk0": "FURYP.DSK", + "joy-right": "kjoy0", + "basic": "LOADM\"FURYP\":EXEC\n" + }, + "56f3425dd11595bcd5a7cf23b13c7bbdbdfc76ca": { + "filename": "Galactic Attack (Tandy) (Portuguese translation).zip", + "disk0": "GALACTIP.DSK", + "joy-left": "kjoy0", + "basic": "LOADM\"GALACTIP\":EXEC\n" + }, + "50a03f4a05fc5269647353b196cb3abc2df067c4": { + "filename": "Galactic Fighter (Four Star Software) (Portuguese translation).zip", + "disk0": "GFIGHTEP.DSK", + "joy-right": "kjoy0", + "basic": "LOADM\"GFIGHTEP\":EXEC\n" + }, + "571c29eb28985c312e9052d31071614c0ab02bdd": { + "filename": "Galatic Wars (Lynn Davis) (Portuguese).zip", + "disk0": "GALATIC.DSK", + "joy-right": "kjoy0", + "basic": "RUN\"GALATIC\"\n" + }, + "9fd14d8b98114fab587b0b56721ea9e92b763bc2": { + "filename": "Ghana Bwana (Tandy) (SSC) (Portuguese translation).zip", + "disk0": "GHANAP.DSK", + "joy-right": "kjoy0", + "basic": "RUN\"GHANAP\"\n" + }, + "0ae2325a559e4025261f29246b1638b0890ebe3c": { + "filename": "Guardian (Portuguese translation).zip", + "disk0": "GUARDIAN.DSK", + "joy-right": "kjoy0", + "basic": "LOADM\"GUARDIAN\":EXEC\n" + }, + "5d362f578de64255a884135e4af1d90ee5312671": { + "filename": "Juno (Federal Hill) (Portuguese translation).zip", + "disk0": "JUNOP.DSK", + "joy-right": "kjoy0", + "basic": "LOADM\"JUNOP\":EXEC\n" + }, + "05f7cdf880960d5f9130fc79e2e88a70d111f282": { + "filename": "Jupiter Patrol (Aardvark-80) (Portuguese translation).zip", + "disk0": "JUPATROP.DSK", + "joy-right": "kjoy0", + "basic": "LOADM\"JUPATROP\":EXEC\n" + }, + "5f8259194ab18ba0bec1b6f64aa05869ad797444": { + "filename": "Labirinto (Portuguese).zip", + "disk0": "LABIRINT.DSK", + "basic": "RUN\"LABIRINT\"\n" + }, + "1fbe0a0a2b576ba8cb4fb18431bd101391e58fcc": { + "filename": "Lunchtime (Tom Mix Software) (Portuguese translation).zip", + "disk0": "LUNCHP.DSK", + "joy-left": "kjoy0", + "basic": "LOADM\"LUNCHP\":EXEC\n" + }, + "d04a1060a97a275f78df84999ea158f671726afb": { + "filename": "Mansion Adventure (Portuguese translation).zip", + "disk0": "MANSAO.DSK", + "basic": "RUN\"MANSAO\"\n" + }, + "e790050caaa005e89c60715ac649c01c590c5de4": { + "filename": "Major Istar (Computerware) (Portuguese translation).zip", + "disk0": "MISTARPT.DSK", + "basic": "LOADM\"MISTARPT\":EXEC\n" + }, + "b3d876d4e60a719a5c78df547204244345436cc6": { + "filename": "Marooned (Saguaro Sotware) (Portuguese translation).zip", + "disk0": "MAROONEP.DSK", + "basic": "RUN\"MAROONEP\"\n", + "tv-input": "redblue" + }, + "a6b650885153759a5c31242b945171dab9ef3948": { + "filename": "Martian Crypt, The (Tom Mix Software) (Portuguese Translation).zip", + "disk0": "CRIPTAPT.DSK", + "basic": "LOADM\"CRIPTAPT\":EXEC\n", + "tv-input": "redblue" + }, + "de695334032be072a1f63b5d769fa919e8f24017": { + "filename": "Megamunk (Color Connection Software) (Portuguese).zip", + "disk0": "MEGAMUNK.DSK", + "joy-right": "kjoy0", + "basic": "LOADM\"MEGAMUNK\":EXEC\n" + }, + "29f92d723802b039b7ce57982f2844216fcc6792": { + "filename": "Module Man (Spectral Associates) (SSC) (Portuguese translation).zip", + "disk0": "MODULEP.DSK", + "joy-right": "kjoy0", + "basic": "LOADM\"MODULEP\":EXEC\n" + }, + "fdd3ba0aebd651f9b0cfc5dd48bb52176b2999a3": { + "filename": "Moon Cresta (Incentive Software) (Coco port) (Portuguese translation).zip", + "disk0": "CRESTAP.DSK", + "joy-right": "kjoy0", + "basic": "LOADM\"CRESTAP\":EXEC\n" + }, + "d19ffa332ada1254ee2b4e8b53edfa5ed3e3db33": { + "filename": "Monopoly (Portuguese).zip", + "disk0": "MONOPOLY.DSK", + "basic": "RUN\"MONOPOLY\"\n" + }, + "3b4635fc4053b77378ada10e5e626110622f1f22": { + "filename": "Mudpies (Computer Shack-Michtron) (Portuguese translation).zip", + "disk0": "MUDPIESP.DSK", + "joy-right": "kjoy0", + "basic": "LOADM\"MUDPIESP\":EXEC\n", + "tv-input": "redblue" + }, + "9f55345da6a3ba5f69c217485e7aae817830ea40": { + "filename": "Olho (Portuguese).zip", + "disk0": "OLHO.DSK", + "basic": "RUN\"OLHO\"\n" + }, + "b847447a3da4bf46e70e3522b91a96527781511f": { + "filename": "Outhouse (Computer Shack) (Portuguese translation).zip", + "disk0": "OUTHOUSP.DSK", + "joy-right": "kjoy0", + "basic": "LOADM\"OUTHOUSP\":EXEC\n" + }, + "7c6f865914459d36a5069bb169ab6c51b3901a20": { + "filename": "O Resgate (Daniel Scherer) (Portuguese).zip", + "disk0": "RESGATE.DSK", + "basic": "RUN\"BOOT\"\n" + }, + "cf9e629dc1ee0af3a65ca146edfe59d0a7699f57": { + "filename": "Panic Button (Tandy) (Portuguese translation).zip", + "disk0": "PANICP.DSK", + "joy-left": "kjoy0", + "basic": "LOADM\"PANICP\":EXEC\n" + }, + "48bd11345201533d7604cf43bf4a5b805ddb80d0": { + "filename": "Paper Route (Diecom Products) (Portuguese translation).zip", + "disk0": "PAPERP.DSK", + "joy-left": "kjoy0", + "basic": "LOADM\"PAPERP\":EXEC\n" + }, + "5d7dfe1db00da0a7d03973a362bafa69ca07e541": { + "filename": "Pengon (Spectral Associates) (Portuguese translation).zip", + "disk0": "PENGONP.DSK", + "joy-right": "kjoy0", + "basic": "LOADM\"PENGONP\":EXEC\n", + "tv-input": "redblue" + }, + "56efe14542db2fab36c09d59ab8bd07909a46ab4": { + "filename": "Pooyan (Tandy) (Portuguese Translation).zip", + "disk0": "POOYANP.DSK", + "joy-right": "kjoy0", + "basic": "LOADM\"POOYANP\":EXEC\n" + }, + "e917ff168c311760f5e223e856df23f9be963a1b": { + "filename": "Pouso Lunar (Portuguese).zip", + "disk0": "LANDER.DSK", + "basic": "RUN\"LANDER\"\n" + }, + "7bfa3712a5255272114579f23c4f53bbc45b8b81": { + "filename": "Ralli (Portuguese).zip", + "disk0": "RALLI.DSK", + "basic": "RUN\"RALLI\"\n" + }, + "24599ebc78737244aff2cf18c4547397834c87da": { + "filename": "Sailor Man (Tom Mix Software) (Portuguese translation).zip", + "disk0": "SAILORP.DSK", + "joy-right": "kjoy0", + "basic": "RUN\"SAILORP\"\n", + "tv-input": "redblue" + }, + "98fd3eb07152a2f811784765b6b4ea19a92c64ff": { + "filename": "Sam Sleuth (Computerware) (Portuguese translation).zip", + "disk0": "SLEUTHP.DSK", + "basic": "LOADM\"SLEUTH1P\":EXEC\n" + }, + "22ea520d5ab521ef8e2f0cb9654880abafee5dff": { + "filename": "Sea Dragon (Adventure International) (Portuguese Translation).zip", + "disk0": "SEADRAGP.DSK", + "joy-right": "kjoy0", + "basic": "LOADM\"SEADRAGP\":EXEC\n" + }, + "122bc689c5fae874207cf2bbe1bb63a29dcbea6d": { + "filename": "Shenanigans (Mark Data Products) (Portuguese translation).zip", + "disk0": "SHENPORT.DSK", + "joy-right": "kjoy0", + "basic": "LOADM\"SHENPORT\":EXEC\n", + "tv-input": "redblue" + }, + "13af879d87ab5dab9c42a13799f53de154f011c8": { + "filename": "Sea Quest (Mark Data Products) (Portuguese translation).zip", + "disk0": "SEAQPORT.DSK", + "basic": "LOADM\"SEAQPORT\":EXEC\n", + "tv-input": "redblue" + }, + "f8fe238265822cb75034746421bdb1cddd918a7a": { + "filename": "Skyway (Tom Mix Software) (Portuguese translation).zip", + "disk0": "SKYWAYP.DSK", + "joy-right": "kjoy0", + "basic": "LOADM\"SKYWAYP\":EXEC\n", + "tv-input": "redblue" + }, + "7dd41b63b9c5de71d36379ec2a9ffceb6181fb11": { + "filename": "Stone Raider II (Microdeal Cornwall) (Portuguese translation).zip", + "disk0": "STONRAIP.DSK", + "joy-right": "kjoy0", + "basic": "LOADM\"STONRAIP\":EXEC\n" + }, + "2be0d8b984241670fa0e41a89c72241ae8df41f2": { + "filename": "Super Startrek (KJM) (Portuguese).zip", + "disk0": "SUPETREK.DSK", + "basic": "RUN\"SUPETREK\"\n" + }, + "8c4e752c916fa73e4b577c1d4fd2b722b1bba8b1": { + "filename": "Taipan (Megasoft) (Portuguese).zip", + "disk0": "TAIPAN.DSK", + "basic": "RUN\"TAIPAN\"\n" + }, + "726a184df92117f713537939676ccfbaf204a734": { + "filename": "The Frog (Portuguese translation).zip", + "disk0": "FROG.DSK", + "basic": "LOADM\"FROG\":EXEC\n" + }, + "1b5e2b437498b29fa79983099a252a4f13338056": { + "filename": "Time Bandit (Computer Shack) (Portuguese translation).zip", + "disk0": "BANDITP.DSK", + "joy-left": "kjoy0", + "basic": "LOADM\"BANDITP\":EXEC\n" + }, + "e8392c2f68cc6bf0c949227d1343355f83918890": { + "filename": "Trekboer (Mark Data Products) (Portuguese Translation).zip", + "disk0": "TREKPORT.DSK", + "joy-right": "kjoy0", + "basic": "LOADM\"TREKPORT\":EXEC\n", + "tv-input": "redblue" + }, + "727293ce95679c16054751eeb1d0d2549aebc861": { + "filename": "Torres (Tower of Hanoi) (Portuguese).zip", + "disk0": "TORRES.DSK", + "basic": "RUN\"TORRES\"\n" + }, + "8e6c00f8f5dcc8a7d39df45a6918ae8293bb2a55": { + "filename": "Vortex Factor, The (Mark Data Products) (Portuguese Translation).zip", + "disk0": "VORTEXPT.DSK", + "basic": "LOADM\"VORTEXPT\":EXEC\n", + "tv-input": "redblue" + }, + "225ee1ac0f5e280fcd77a149ddcc32ce63b89801": { + "filename": "Zaxxon (Datasoft) (Portuguse translation).zip", + "disk0": "ZAXXON.DSK", + "joy-right": "kjoy0", + "basic": "LOADM\"ZAXXON\":EXEC\n" + }, + "2e8aed5e19469433ec105a1b7b7061b6ced02631": { + "filename": "Adventure #10 - Savage Island Part 1 (Adventure International) (6847T1).zip", + "machine": "coco2bus", + "disk0": "SAVAGE1.DSK", + "basic": "LOADM\"SAVAGE1\":EXEC\n" + }, + "ee9a1b762f757b45bc7006ff8bc4a08c0372ed2a": { + "filename": "Adventure #12 - The Golden Voyage (Adventure International) (6847T1).zip", + "machine": "coco2bus", + "disk0": "VOYAGE.DSK", + "basic": "LOADM\"VOYAGE\":EXEC\n" + }, + "b455e8e4d2067394c6c0447fba2077b4aa8e5eb7": { + "filename": "Adventure #11 - Savage Island Part 2 (Adventure International) (6847T1).zip", + "machine": "coco2bus", + "disk0": "SAVAGE2.DSK", + "basic": "LOADM\"SAVAGE2\":EXEC\n" + }, + "b889ddf333a64f687575fcb6c0969a7e95be9e25": { + "filename": "Adventure #13 - Sorcerer of Claymorgue Castle (Adventure International) (6847T1).zip", + "machine": "coco2bus", + "disk0": "SORCERER.DSK", + "basic": "LOADM\"SORCERER\":EXEC\n" + }, + "b5ddfdc65e0fb946451bb8ba858bf3d1694fab9d": { + "filename": "Adventure #1 - Adventureland (Adventure International) (6847T1).zip", + "machine": "coco2bus", + "disk0": "ADVENTUR.DSK", + "basic": "LOADM\"ADVENTUR\":EXEC\n" + }, + "9be0df3281023bf8fcccc51e8277c0a0bd3d4b7f": { + "filename": "Adventure #2 - Pirate Adventure (Adventure International) (6847T1).zip", + "machine": "coco2bus", + "disk0": "PIRATE.DSK", + "basic": "LOADM\"PIRATE\":EXEC\n" + }, + "6e19aab5547b08eae81c204ac76c5674bff82beb": { + "filename": "Adventure #3 - Secret Mission (Adventure International) (6847T1).zip", + "machine": "coco2bus", + "disk0": "MISSION.DSK", + "basic": "LOADM\"MISSION\":EXEC\n" + }, + "c2f7586573004bdb54c008fd2fdac6b86d4561da": { + "filename": "Adventure #4 - Voodoo Castle (Adventure International) (6847T1).zip", + "machine": "coco2bus", + "disk0": "VOODOO.DSK", + "basic": "LOADM\"VOODOO\":EXEC\n" + }, + "671d9147d24e12e6f64ab65f0350c0deae35ce5c": { + "filename": "Adventure #6 - Strange Odyssey (Adventure International) (6847T1).zip", + "machine": "coco2bus", + "disk0": "ODYSSEY.DSK", + "basic": "LOADM\"ODYSSEY\":EXEC\n" + }, + "e5cd09ed68cc4d772d3849c8bb5658147753e5dd": { + "filename": "Adventure #7 - Mystery Fun House (Adventure International) (6847T1).zip", + "machine": "coco2bus", + "disk0": "FUNHOUSE.DSK", + "basic": "LOADM\"FUNHOUSE\":EXEC\n" + }, + "3165b173d7d2ae7645f1a7515eab378d3ffac668": { + "filename": "Adventure #8 - Pyramid of Doom (Adventure International) (6847T1).zip", + "machine": "coco2bus", + "disk0": "PYRAMID.DSK", + "basic": "LOADM\"PYRAMID\":EXEC\n" + }, + "5a93c4ecce176b71bfe9305962f62e7e1fac7a3e": { + "filename": "Adventure #9 - Ghost Town (Adventure International) (6847T1).zip", + "machine": "coco2bus", + "disk0": "GHOST.DSK", + "basic": "LOADM\"GHOST\":EXEC\n" + }, + "5dd92035d5a83539907c99c56338a7d8efdf7ede": { + "filename": "QuestProbe #1 - The Hulk (Adventure International) (6847T1).zip", + "machine": "coco2bus", + "disk0": "HULK.DSK", + "basic": "LOADM\"HULK\":EXEC\n" + }, + "ada9fa117aba3db231dc2063b0d5c9691b2a6aef": { + "filename": "AGI Quest I (Juha Terho) (OS-9) (Coco 3).zip", + "machine": "coco3", + "disk0": "Floppy 360K/agiquest.dsk", + "joy-right": "kjoy0", + "basic": "DOS\n" + }, + "d85ff0b2917be41d830c01221825f88bd0a8147d": { + "filename": "AGI Quest I (Juha Terho) (OS-9) (Coco 3).zip", + "machine": "coco3", + "disk0": "Floppy 360K/agiquest.dsk", + "joy-right": "kjoy0", + "basic": "DOS\n" + }, + "7b549d53b0ae85ae9c44efa245dc2a4469468071": { + "filename": "AGI Quest I (Juha Terho) (OS-9) (Coco 3).zip", + "machine": "coco3", + "disk0": "Floppy 360K/agiquest.dsk", + "joy-right": "kjoy0", + "basic": "DOS\n" + }, + "e888b2862573ee06f192569b7ed05a7e739b8f9e": { + "filename": "AGI Quest I (Juha Terho) (OS-9) (Coco 3).zip", + "machine": "coco3", + "disk0": "Floppy 360K/agiquest.dsk", + "joy-right": "kjoy0", + "basic": "DOS\n" + }, + "33496337cb30fa0315a47690255b0303b7ca4358": { + "filename": "Black Cauldron, The (Sierra On-Line) (OS-9) (Coco 3).zip", + "machine": "coco3", + "disk0": "Floppy 360K/bc-1.dsk", + "joy-right": "kjoy0", + "basic": "DOS\n" + }, + "d548e8726f097e1425d2491dc23eb5e0a25d125c": { + "filename": "Black Cauldron, The (Sierra On-Line) (OS-9) (Coco 3).zip", + "machine": "coco3", + "disk0": "Floppy 360K/bc-1.dsk", + "joy-right": "kjoy0", + "basic": "DOS\n" + }, + "700f67b561442900239a00d1baedecb0672071f5": { + "filename": "Black Cauldron, The (Sierra On-Line) (OS-9) (Coco 3).zip", + "machine": "coco3", + "disk0": "Floppy 360K/bc-1.dsk", + "joy-right": "kjoy0", + "basic": "DOS\n" + }, + "a5152ec2f2c972cc8b6efb96636553a79958d1f0": { + "filename": "Black Cauldron, The (Sierra On-Line) (OS-9) (Coco 3).zip", + "machine": "coco3", + "disk0": "Floppy 360K/bc-1.dsk", + "joy-right": "kjoy0", + "basic": "DOS\n" + }, + "2d68d55a43d52168016c672428fea04473b8bc8b": { + "filename": "Black Cauldron, The (Sierra On-Line) (OS-9) (Coco 3).zip", + "machine": "coco3", + "disk0": "Floppy 360K/bc-1.dsk", + "joy-right": "kjoy0", + "basic": "DOS\n" + }, + "6e32cb9df7022f8b8459181f87722f05607d3a8d": { + "filename": "Black Cauldron, The (Sierra On-Line) (OS-9) (Coco 3).zip", + "machine": "coco3", + "disk0": "Floppy 360K/bc-1.dsk", + "joy-right": "kjoy0", + "basic": "DOS\n" + }, + "774f5a75834954558d11ecf54f6422e477d837fa": { + "filename": "Caitlyn's Destiny (Corby Lacroix) (OS-9) (Coco 3).zip", + "machine": "coco3", + "disk0": "Floppy 720K/caitlyn-1.dsk", + "joy-right": "kjoy0", + "basic": "DOS\n" + }, + "632723a5df5fe2e0401b0348de8a5779f5635e91": { + "filename": "Caitlyn's Destiny (Corby Lacroix) (OS-9) (Coco 3).zip", + "machine": "coco3", + "disk0": "Floppy 720K/caitlyn-1.dsk", + "joy-right": "kjoy0", + "basic": "DOS\n" + }, + "583eb95ab4e0303811f762a49b86e162dd16f670": { + "filename": "Caitlyn's Destiny (Corby Lacroix) (OS-9) (Coco 3).zip", + "machine": "coco3", + "disk0": "Floppy 720K/caitlyn-1.dsk", + "joy-right": "kjoy0", + "basic": "DOS\n" + }, + "407c3baaff2a1d75fd55a9c1ada3a3f26fe81972": { + "filename": "Caitlyn's Destiny (Corby Lacroix) (OS-9) (Coco 3).zip", + "machine": "coco3", + "disk0": "Floppy 720K/caitlyn-1.dsk", + "joy-right": "kjoy0", + "basic": "DOS\n" + }, + "4bfa7cb1b341e2ac952194b88b66bfdb3bd8555b": { + "filename": "Caitlyn's Destiny (Corby Lacroix) (OS-9) (Coco 3).zip", + "machine": "coco3", + "disk0": "Floppy 720K/caitlyn-1.dsk", + "joy-right": "kjoy0", + "basic": "DOS\n" + }, + "3bb89a7a2d2ec1022bf724f701f95554784b7557": { + "filename": "Caitlyn's Destiny (Corby Lacroix) (OS-9) (Coco 3).zip", + "machine": "coco3", + "disk0": "Floppy 720K/caitlyn-1.dsk", + "joy-right": "kjoy0", + "basic": "DOS\n" + }, + "1049237115e7ae0faaae2e605bdf0e4a25286a34": { + "filename": "Caitlyn's Destiny (Corby Lacroix) (OS-9) (Coco 3).zip", + "machine": "coco3", + "disk0": "Floppy 720K/caitlyn-1.dsk", + "joy-right": "kjoy0", + "basic": "DOS\n" + }, + "d28cf636e396421f605b1eb9bedcb72526f7b90b": { + "filename": "Caitlyn's Destiny (Corby Lacroix) (OS-9) (Coco 3).zip", + "machine": "coco3", + "disk0": "Floppy 720K/caitlyn-1.dsk", + "joy-right": "kjoy0", + "basic": "DOS\n" + }, + "56ea1e75978c1942a70d13ed3738c3565be6f6cc": { + "filename": "Caitlyn's Destiny (Corby Lacroix) (OS-9) (Coco 3).zip", + "machine": "coco3", + "disk0": "Floppy 720K/caitlyn-1.dsk", + "joy-right": "kjoy0", + "basic": "DOS\n" + }, + "b8e998b349e7c4248b3dc58559985fed0d94e08b": { + "filename": "Caitlyn's Destiny (Corby Lacroix) (OS-9) (Coco 3).zip", + "machine": "coco3", + "disk0": "Floppy 720K/caitlyn-1.dsk", + "joy-right": "kjoy0", + "basic": "DOS\n" + }, + "8d0301c76d92daa136596d3918a39937cc6c0d40": { + "filename": "Caitlyn's Destiny (Corby Lacroix) (OS-9) (Coco 3).zip", + "machine": "coco3", + "disk0": "Floppy 720K/caitlyn-1.dsk", + "joy-right": "kjoy0", + "basic": "DOS\n" + }, + "a5abf7e2533bc081de7ba2634ab565102c4c2216": { + "filename": "Caitlyn's Destiny (Corby Lacroix) (OS-9) (Coco 3).zip", + "machine": "coco3", + "disk0": "Floppy 720K/caitlyn-1.dsk", + "joy-right": "kjoy0", + "basic": "DOS\n" + }, + "40a787ec873a9f0d22418059624c55be721a0c3c": { + "filename": "Caitlyn's Destiny (Corby Lacroix) (OS-9) (Coco 3).zip", + "machine": "coco3", + "disk0": "Floppy 720K/caitlyn-1.dsk", + "joy-right": "kjoy0", + "basic": "DOS\n" + }, + "93ba947c9a9ffecc635481efd6434da586f704de": { + "filename": "Enclosure (Femo Duo Entertainment) (OS-9) (Coco 3).zip", + "machine": "coco3", + "disk0": "Floppy 360K/enclos-1.dsk", + "joy-right": "kjoy0", + "basic": "DOS\n" + }, + "26fbf4025e0b2cc3f17335bf2a2aa90350647e1b": { + "filename": "Enclosure (Femo Duo Entertainment) (OS-9) (Coco 3).zip", + "machine": "coco3", + "disk0": "Floppy 360K/enclos-1.dsk", + "joy-right": "kjoy0", + "basic": "DOS\n" + }, + "2367fe31180da3e67d87fc9d907ffe67afb8c97a": { + "filename": "Enclosure (Femo Duo Entertainment) (OS-9) (Coco 3).zip", + "machine": "coco3", + "disk0": "Floppy 360K/enclos-1.dsk", + "joy-right": "kjoy0", + "basic": "DOS\n" + }, + "10bb515a813a7d2655ab2e92ebb1c2229eb40ea0": { + "filename": "Enclosure (Femo Duo Entertainment) (OS-9) (Coco 3).zip", + "machine": "coco3", + "disk0": "Floppy 360K/enclos-1.dsk", + "joy-right": "kjoy0", + "basic": "DOS\n" + }, + "399b2ab1fe580e29271c230b0d84ddc8ed497c13": { + "filename": "Enclosure (Femo Duo Entertainment) (OS-9) (Coco 3).zip", + "machine": "coco3", + "disk0": "Floppy 360K/enclos-1.dsk", + "joy-right": "kjoy0", + "basic": "DOS\n" + }, + "6f2f052ab128e658f60d46f807ca3ac731dbedd0": { + "filename": "Enclosure (Femo Duo Entertainment) (OS-9) (Coco 3).zip", + "machine": "coco3", + "disk0": "Floppy 360K/enclos-1.dsk", + "joy-right": "kjoy0", + "basic": "DOS\n" + }, + "24da338bd476545698faa72555a2a2bcffa9afbc": { + "filename": "Enclosure (Femo Duo Entertainment) (OS-9) (Coco 3).zip", + "machine": "coco3", + "disk0": "Floppy 360K/enclos-1.dsk", + "joy-right": "kjoy0", + "basic": "DOS\n" + }, + "9dee02ecf3040d375ffdc6327ec5804982f63514": { + "filename": "Enclosure (Femo Duo Entertainment) (OS-9) (Coco 3).zip", + "machine": "coco3", + "disk0": "Floppy 360K/enclos-1.dsk", + "joy-right": "kjoy0", + "basic": "DOS\n" + }, + "ce63fa6c922656d0ff674f8a458f0e33de8ed2e1": { + "filename": "Gold Rush! (Sierra On-Line) (OS-9) (Coco 3).zip", + "machine": "coco3", + "disk0": "Floppy 720K/gr-1.dsk", + "joy-right": "kjoy0", + "basic": "DOS\n" + }, + "d94a706e677ddc60bfc9b75dc9ce683e76416e51": { + "filename": "Gold Rush! (Sierra On-Line) (OS-9) (Coco 3).zip", + "machine": "coco3", + "disk0": "Floppy 720K/gr-1.dsk", + "joy-right": "kjoy0", + "basic": "DOS\n" + }, + "711c6ff48db181ffd5c54cefbc2267fe300e2855": { + "filename": "Gold Rush! (Sierra On-Line) (OS-9) (Coco 3).zip", + "machine": "coco3", + "disk0": "Floppy 720K/gr-1.dsk", + "joy-right": "kjoy0", + "basic": "DOS\n" + }, + "9c9d688bd3a2acd5240d9464f6c289d40370105e": { + "filename": "Gold Rush! (Sierra On-Line) (OS-9) (Coco 3).zip", + "machine": "coco3", + "disk0": "Floppy 720K/gr-1.dsk", + "joy-right": "kjoy0", + "basic": "DOS\n" + }, + "e4fbf921bb656c4fbd6a59ac910d55958748e2bd": { + "filename": "Gold Rush! (Sierra On-Line) (OS-9) (Coco 3).zip", + "machine": "coco3", + "disk0": "Floppy 720K/gr-1.dsk", + "joy-right": "kjoy0", + "basic": "DOS\n" + }, + "3ee17510edbcb7724bc73500c87a9a5c8651586b": { + "filename": "Gold Rush! (Sierra On-Line) (OS-9) (Coco 3).zip", + "machine": "coco3", + "disk0": "Floppy 720K/gr-1.dsk", + "joy-right": "kjoy0", + "basic": "DOS\n" + }, + "75f83d2d55e98dbd956417295925bc528e33bfee": { + "filename": "Gold Rush! (Sierra On-Line) (OS-9) (Coco 3).zip", + "machine": "coco3", + "disk0": "Floppy 720K/gr-1.dsk", + "joy-right": "kjoy0", + "basic": "DOS\n" + }, + "e6e0784c75b4ca61170fdac7f9f6b0e3e366246f": { + "filename": "Gold Rush! (Sierra On-Line) (OS-9) (Coco 3).zip", + "machine": "coco3", + "disk0": "Floppy 720K/gr-1.dsk", + "joy-right": "kjoy0", + "basic": "DOS\n" + }, + "6ddb18b62f9a894dceae7ec9a1e83931dce9e892": { + "filename": "Gold Rush! (Sierra On-Line) (OS-9) (Coco 3).zip", + "machine": "coco3", + "disk0": "Floppy 720K/gr-1.dsk", + "joy-right": "kjoy0", + "basic": "DOS\n" + }, + "74bd13b5f2fc306923e5caac280cea923fb0f536": { + "filename": "Gold Rush! (Sierra On-Line) (OS-9) (Coco 3).zip", + "machine": "coco3", + "disk0": "Floppy 720K/gr-1.dsk", + "joy-right": "kjoy0", + "basic": "DOS\n" + }, + "815eaa09d8bf66f9b144241b7b8b221b47dcc651": { + "filename": "Gold Rush! (Sierra On-Line) (OS-9) (Coco 3).zip", + "machine": "coco3", + "disk0": "Floppy 720K/gr-1.dsk", + "joy-right": "kjoy0", + "basic": "DOS\n" + }, + "77a57fb3012962d3437dc1a7db533772192226ed": { + "filename": "Gold Rush! (Sierra On-Line) (OS-9) (Coco 3).zip", + "machine": "coco3", + "disk0": "Floppy 720K/gr-1.dsk", + "joy-right": "kjoy0", + "basic": "DOS\n" + }, + "4b7477b0b88668f42530c954d316f80126fda0f6": { + "filename": "Gold Rush! (Sierra On-Line) (OS-9) (Coco 3).zip", + "machine": "coco3", + "disk0": "Floppy 720K/gr-1.dsk", + "joy-right": "kjoy0", + "basic": "DOS\n" + }, + "2148be2ecd559d797d1347f69523202f01940377": { + "filename": "King's Quest III (Sierra On-Line) (OS-9) (Coco 3).zip", + "machine": "coco3", + "disk0": "Floppy 360K/kq3-1.dsk", + "joy-right": "kjoy0", + "basic": "DOS\n" + }, + "997a4b51fd36da36da9a1209f1207bce46ce6516": { + "filename": "King's Quest III (Sierra On-Line) (OS-9) (Coco 3).zip", + "machine": "coco3", + "disk0": "Floppy 360K/kq3-1.dsk", + "joy-right": "kjoy0", + "basic": "DOS\n" + }, + "23f076f81804fc52f6277eda014ef23281e9ca91": { + "filename": "King's Quest III (Sierra On-Line) (OS-9) (Coco 3).zip", + "machine": "coco3", + "disk0": "Floppy 360K/kq3-1.dsk", + "joy-right": "kjoy0", + "basic": "DOS\n" + }, + "4277ec4ea0d57d66505fc370701793515c2b7cb6": { + "filename": "King's Quest III (Sierra On-Line) (OS-9) (Coco 3).zip", + "machine": "coco3", + "disk0": "Floppy 360K/kq3-1.dsk", + "joy-right": "kjoy0", + "basic": "DOS\n" + }, + "341f9f69e3bfcc539e44665ce44f1ccd37191e8b": { + "filename": "King's Quest III (Sierra On-Line) (OS-9) (Coco 3).zip", + "machine": "coco3", + "disk0": "Floppy 360K/kq3-1.dsk", + "joy-right": "kjoy0", + "basic": "DOS\n" + }, + "99d981902a8025068316020dd1e0aad5a5453ea2": { + "filename": "King's Quest III (Sierra On-Line) (OS-9) (Coco 3).zip", + "machine": "coco3", + "disk0": "Floppy 360K/kq3-1.dsk", + "joy-right": "kjoy0", + "basic": "DOS\n" + }, + "869e9875949325968423da32f55f9633adfbc5a9": { + "filename": "King's Quest III (Sierra On-Line) (OS-9) (Coco 3).zip", + "machine": "coco3", + "disk0": "Floppy 360K/kq3-1.dsk", + "joy-right": "kjoy0", + "basic": "DOS\n" + }, + "118ec75c1c42abe787f7dd43211dfdf3d67bde03": { + "filename": "King's Quest III (Sierra On-Line) (OS-9) (Coco 3).zip", + "machine": "coco3", + "disk0": "Floppy 360K/kq3-1.dsk", + "joy-right": "kjoy0", + "basic": "DOS\n" + }, + "3564eb0b175764db1d415abf47e709ec1c4f5138": { + "filename": "King's Quest III (Sierra On-Line) (OS-9) (Coco 3).zip", + "machine": "coco3", + "disk0": "Floppy 360K/kq3-1.dsk", + "joy-right": "kjoy0", + "basic": "DOS\n" + }, + "42f1813d00f3284b43356dc229a409a8073a1839": { + "filename": "King's Quest III (Sierra On-Line) (OS-9) (Coco 3).zip", + "machine": "coco3", + "disk0": "Floppy 360K/kq3-1.dsk", + "joy-right": "kjoy0", + "basic": "DOS\n" + }, + "4b1e13beb0418a2c38f55ff51aed584cecb6cc60": { + "filename": "King's Quest III (Sierra On-Line) (OS-9) (Coco 3).zip", + "machine": "coco3", + "disk0": "Floppy 360K/kq3-1.dsk", + "joy-right": "kjoy0", + "basic": "DOS\n" + }, + "8e6599e1444aea19b0c47fcd74da91c0b6cd42dd": { + "filename": "King's Quest III (Sierra On-Line) (OS-9) (Coco 3).zip", + "machine": "coco3", + "disk0": "Floppy 360K/kq3-1.dsk", + "joy-right": "kjoy0", + "basic": "DOS\n" + }, + "4009eebe8b9d878bac23391812d53c5debd943ee": { + "filename": "King's Quest III (Sierra On-Line) (OS-9) (Coco 3).zip", + "machine": "coco3", + "disk0": "Floppy 360K/kq3-1.dsk", + "joy-right": "kjoy0", + "basic": "DOS\n" + }, + "80f9621f748aaab8bb7208b59d179e9291b3ca4a": { + "filename": "King's Quest III (Sierra On-Line) (OS-9) (Coco 3).zip", + "machine": "coco3", + "disk0": "Floppy 360K/kq3-1.dsk", + "joy-right": "kjoy0", + "basic": "DOS\n" + }, + "eca05ca67d9aef88668b001b304a8ba7d2cd3dd8": { + "filename": "King's Quest III (Sierra On-Line) (OS-9) (Coco 3).zip", + "machine": "coco3", + "disk0": "Floppy 360K/kq3-1.dsk", + "joy-right": "kjoy0", + "basic": "DOS\n" + }, + "d837a7e1255f7c848d807175f44153a1688f2e37": { + "filename": "King's Quest III (Sierra On-Line) (OS-9) (Coco 3).zip", + "machine": "coco3", + "disk0": "Floppy 360K/kq3-1.dsk", + "joy-right": "kjoy0", + "basic": "DOS\n" + }, + "07fcbe1fc986155bb2e7cda8bf902e387b5a1d9e": { + "filename": "King's Quest II (Sierra On-Line) (OS-9) (Coco 3).zip", + "machine": "coco3", + "disk0": "Floppy 720K/kq2.dsk", + "joy-right": "kjoy0", + "basic": "DOS\n" + }, + "855886c906e7b43912c6761e50ffa25dc6108dc9": { + "filename": "King's Quest II (Sierra On-Line) (OS-9) (Coco 3).zip", + "machine": "coco3", + "disk0": "Floppy 720K/kq2.dsk", + "joy-right": "kjoy0", + "basic": "DOS\n" + }, + "cb9d3663fe5062b40708bbee35ebbb6300c5f243": { + "filename": "King's Quest II (Sierra On-Line) (OS-9) (Coco 3).zip", + "machine": "coco3", + "disk0": "Floppy 720K/kq2.dsk", + "joy-right": "kjoy0", + "basic": "DOS\n" + }, + "3b65d9121e8ba10c51235ff6ede350c4f40b172c": { + "filename": "King's Quest II (Sierra On-Line) (OS-9) (Coco 3).zip", + "machine": "coco3", + "disk0": "Floppy 720K/kq2.dsk", + "joy-right": "kjoy0", + "basic": "DOS\n" + }, + "119faa489726e48cae7300b025effcf2432b7214": { + "filename": "King's Quest II (Sierra On-Line) (OS-9) (Coco 3).zip", + "machine": "coco3", + "disk0": "Floppy 720K/kq2.dsk", + "joy-right": "kjoy0", + "basic": "DOS\n" + }, + "7a7cf4090b950b10e52ea42a0fd4655d327d76af": { + "filename": "King's Quest II (Sierra On-Line) (OS-9) (Coco 3).zip", + "machine": "coco3", + "disk0": "Floppy 720K/kq2.dsk", + "joy-right": "kjoy0", + "basic": "DOS\n" + }, + "006611b160a8df0d16478acc7d9b5ca8efc296ec": { + "filename": "King's Quest II (Sierra On-Line) (OS-9) (Coco 3).zip", + "machine": "coco3", + "disk0": "Floppy 720K/kq2.dsk", + "joy-right": "kjoy0", + "basic": "DOS\n" + }, + "ff833548208bc60da639353d4fe2b32ecb2e55e5": { + "filename": "King's Quest I (Sierra On-Line) (OS-9) (Coco 3).zip", + "machine": "coco3", + "disk0": "Floppy 360K/kq1.dsk", + "joy-right": "kjoy0", + "basic": "DOS\n" + }, + "797d880d1466ad61da783445a3d4dc8998204848": { + "filename": "King's Quest I (Sierra On-Line) (OS-9) (Coco 3).zip", + "machine": "coco3", + "disk0": "Floppy 360K/kq1.dsk", + "joy-right": "kjoy0", + "basic": "DOS\n" + }, + "e4f57283332c1036887a3bb37233d259f414d6fb": { + "filename": "King's Quest I (Sierra On-Line) (OS-9) (Coco 3).zip", + "machine": "coco3", + "disk0": "Floppy 360K/kq1.dsk", + "joy-right": "kjoy0", + "basic": "DOS\n" + }, + "bb77881216c85e423007a83473c66d79ad5d8469": { + "filename": "King's Quest I (Sierra On-Line) (OS-9) (Coco 3).zip", + "machine": "coco3", + "disk0": "Floppy 360K/kq1.dsk", + "joy-right": "kjoy0", + "basic": "DOS\n" + }, + "ef9db5601a137d70df81e38e1e4bd6cec0fab235": { + "filename": "King's Quest I (Sierra On-Line) (OS-9) (Coco 3).zip", + "machine": "coco3", + "disk0": "Floppy 360K/kq1.dsk", + "joy-right": "kjoy0", + "basic": "DOS\n" + }, + "ba96f2825c42751d3046af719a38f9eb5620c5b4": { + "filename": "King's Quest IV (Sierra On-Line) (OS-9) (Coco 3).zip", + "machine": "coco3", + "disk0": "Floppy 720K/kq4-1.dsk", + "joy-right": "kjoy0", + "basic": "DOS\n" + }, + "8fb58da00ce6f9e4938d1a5db63bb5a085237c76": { + "filename": "King's Quest IV (Sierra On-Line) (OS-9) (Coco 3).zip", + "machine": "coco3", + "disk0": "Floppy 720K/kq4-1.dsk", + "joy-right": "kjoy0", + "basic": "DOS\n" + }, + "93417304046a0792e33fa7ef4403e933c785825a": { + "filename": "King's Quest IV (Sierra On-Line) (OS-9) (Coco 3).zip", + "machine": "coco3", + "disk0": "Floppy 720K/kq4-1.dsk", + "joy-right": "kjoy0", + "basic": "DOS\n" + }, + "68a44e79de9fd8eac6797122e862801c0f28484b": { + "filename": "King's Quest IV (Sierra On-Line) (OS-9) (Coco 3).zip", + "machine": "coco3", + "disk0": "Floppy 720K/kq4-1.dsk", + "joy-right": "kjoy0", + "basic": "DOS\n" + }, + "bd9d16b65ca7682efffff29ad24d95e51fc3ca19": { + "filename": "King's Quest IV (Sierra On-Line) (OS-9) (Coco 3).zip", + "machine": "coco3", + "disk0": "Floppy 720K/kq4-1.dsk", + "joy-right": "kjoy0", + "basic": "DOS\n" + }, + "5e39a37d78a356930fc9eefb662f3c95002bc495": { + "filename": "King's Quest IV (Sierra On-Line) (OS-9) (Coco 3).zip", + "machine": "coco3", + "disk0": "Floppy 720K/kq4-1.dsk", + "joy-right": "kjoy0", + "basic": "DOS\n" + }, + "c2c5d93695aad49ec6507bf9ae381e29ff9f6e75": { + "filename": "King's Quest IV (Sierra On-Line) (OS-9) (Coco 3).zip", + "machine": "coco3", + "disk0": "Floppy 720K/kq4-1.dsk", + "joy-right": "kjoy0", + "basic": "DOS\n" + }, + "3be477db3bf4ef2abdeb61d86169abc01ced6de2": { + "filename": "King's Quest IV (Sierra On-Line) (OS-9) (Coco 3).zip", + "machine": "coco3", + "disk0": "Floppy 720K/kq4-1.dsk", + "joy-right": "kjoy0", + "basic": "DOS\n" + }, + "3553c1f139b0c6b9b6b933835de19d53c8330640": { + "filename": "King's Quest IV (Sierra On-Line) (OS-9) (Coco 3).zip", + "machine": "coco3", + "disk0": "Floppy 720K/kq4-1.dsk", + "joy-right": "kjoy0", + "basic": "DOS\n" + }, + "679a69d87c00ddd49fccb9bfde91dda2c9c2f025": { + "filename": "King's Quest IV (Sierra On-Line) (OS-9) (Coco 3).zip", + "machine": "coco3", + "disk0": "Floppy 720K/kq4-1.dsk", + "joy-right": "kjoy0", + "basic": "DOS\n" + }, + "78232ef8e8d5200c74c598d97fa6791774fb906d": { + "filename": "King's Quest IV (Sierra On-Line) (OS-9) (Coco 3).zip", + "machine": "coco3", + "disk0": "Floppy 720K/kq4-1.dsk", + "joy-right": "kjoy0", + "basic": "DOS\n" + }, + "8434f89d2c1d6038a447fa1f0676131635e5a548": { + "filename": "King's Quest IV (Sierra On-Line) (OS-9) (Coco 3).zip", + "machine": "coco3", + "disk0": "Floppy 720K/kq4-1.dsk", + "joy-right": "kjoy0", + "basic": "DOS\n" + }, + "f59a555e8032b078aa84ce419e4c3ae833c35268": { + "filename": "King's Quest IV (Sierra On-Line) (OS-9) (Coco 3).zip", + "machine": "coco3", + "disk0": "Floppy 720K/kq4-1.dsk", + "joy-right": "kjoy0", + "basic": "DOS\n" + }, + "b5526bceffdebc56002c93fe34f42e8c9c9ac5a0": { + "filename": "King's Quest IV (Sierra On-Line) (OS-9) (Coco 3).zip", + "machine": "coco3", + "disk0": "Floppy 720K/kq4-1.dsk", + "joy-right": "kjoy0", + "basic": "DOS\n" + }, + "1906d2a8cf3446a08dfefbe2c91cbe780aa7426c": { + "filename": "Leisure Suit Larry (Sierra On-Line) (OS-9) (Coco 3).zip", + "machine": "coco3", + "disk0": "Floppy 360K/lsl-1.dsk", + "joy-right": "kjoy0", + "basic": "DOS\n" + }, + "085877afb36803b58b73fcf46ff4366f8120dd68": { + "filename": "Leisure Suit Larry (Sierra On-Line) (OS-9) (Coco 3).zip", + "machine": "coco3", + "disk0": "Floppy 360K/lsl-1.dsk", + "joy-right": "kjoy0", + "basic": "DOS\n" + }, + "51253bb098220c6b3319cf43058dab419cfad636": { + "filename": "Leisure Suit Larry (Sierra On-Line) (OS-9) (Coco 3).zip", + "machine": "coco3", + "disk0": "Floppy 360K/lsl-1.dsk", + "joy-right": "kjoy0", + "basic": "DOS\n" + }, + "d8302af015fcace30766e4d96e2f15c9d9072bd1": { + "filename": "Leisure Suit Larry (Sierra On-Line) (OS-9) (Coco 3).zip", + "machine": "coco3", + "disk0": "Floppy 360K/lsl-1.dsk", + "joy-right": "kjoy0", + "basic": "DOS\n" + }, + "71f1d6aaec42fbe81bdf45debdbac3c08f11c2f6": { + "filename": "Leisure Suit Larry (Sierra On-Line) (OS-9) (Coco 3).zip", + "machine": "coco3", + "disk0": "Floppy 360K/lsl-1.dsk", + "joy-right": "kjoy0", + "basic": "DOS\n" + }, + "0f19035455ab6eca902c12ccc79f4cc13fbfcf36": { + "filename": "Leisure Suit Larry (Sierra On-Line) (OS-9) (Coco 3).zip", + "machine": "coco3", + "disk0": "Floppy 360K/lsl-1.dsk", + "joy-right": "kjoy0", + "basic": "DOS\n" + }, + "0e2dc0ba79971ab8545d49a7da54cbf7fe3c3c7c": { + "filename": "Leisure Suit Larry (Sierra On-Line) (OS-9) (Coco 3).zip", + "machine": "coco3", + "disk0": "Floppy 360K/lsl-1.dsk", + "joy-right": "kjoy0", + "basic": "DOS\n" + }, + "5e4444df89883eefaf078b30a75f322204d0ce57": { + "filename": "Leisure Suit Larry (Sierra On-Line) (OS-9) (Coco 3).zip", + "machine": "coco3", + "disk0": "Floppy 360K/lsl-1.dsk", + "joy-right": "kjoy0", + "basic": "DOS\n" + }, + "8bea7bca0424f7e3c5edf1fe19a7ca1570c7c27e": { + "filename": "Manhunter 1 - New York (Sierra On-Line) (OS-9) (Coco 3).zip", + "machine": "coco3", + "disk0": "Floppy 720K/mh1-1.dsk", + "joy-right": "kjoy0", + "basic": "DOS\n" + }, + "370d0f63d71f168102de441a7732d48d2d833045": { + "filename": "Manhunter 1 - New York (Sierra On-Line) (OS-9) (Coco 3).zip", + "machine": "coco3", + "disk0": "Floppy 720K/mh1-1.dsk", + "joy-right": "kjoy0", + "basic": "DOS\n" + }, + "216a158a17fd2816ef532831b0f57b8d746b5650": { + "filename": "Manhunter 1 - New York (Sierra On-Line) (OS-9) (Coco 3).zip", + "machine": "coco3", + "disk0": "Floppy 720K/mh1-1.dsk", + "joy-right": "kjoy0", + "basic": "DOS\n" + }, + "3b32772fe3a9cec9a471cda25505dee5484786df": { + "filename": "Manhunter 1 - New York (Sierra On-Line) (OS-9) (Coco 3).zip", + "machine": "coco3", + "disk0": "Floppy 720K/mh1-1.dsk", + "joy-right": "kjoy0", + "basic": "DOS\n" + }, + "dce1f53d81c3cbfea9389b23aa84eac6154a8986": { + "filename": "Manhunter 1 - New York (Sierra On-Line) (OS-9) (Coco 3).zip", + "machine": "coco3", + "disk0": "Floppy 720K/mh1-1.dsk", + "joy-right": "kjoy0", + "basic": "DOS\n" + }, + "139179d53ae0f581b5f4f493d3ac24a0e1977105": { + "filename": "Manhunter 1 - New York (Sierra On-Line) (OS-9) (Coco 3).zip", + "machine": "coco3", + "disk0": "Floppy 720K/mh1-1.dsk", + "joy-right": "kjoy0", + "basic": "DOS\n" + }, + "099c37715a16cc16046658c8e4e197cd076104fa": { + "filename": "Manhunter 1 - New York (Sierra On-Line) (OS-9) (Coco 3).zip", + "machine": "coco3", + "disk0": "Floppy 720K/mh1-1.dsk", + "joy-right": "kjoy0", + "basic": "DOS\n" + }, + "950eb55dba751e7040e1d3e7c3d4f7cea97da8a8": { + "filename": "Manhunter 1 - New York (Sierra On-Line) (OS-9) (Coco 3).zip", + "machine": "coco3", + "disk0": "Floppy 720K/mh1-1.dsk", + "joy-right": "kjoy0", + "basic": "DOS\n" + }, + "30b2a45621d55713ae86060acc72b207e5c0ca1d": { + "filename": "Manhunter 1 - New York (Sierra On-Line) (OS-9) (Coco 3).zip", + "machine": "coco3", + "disk0": "Floppy 720K/mh1-1.dsk", + "joy-right": "kjoy0", + "basic": "DOS\n" + }, + "1f2b30bb53e4a2eebdc7a58e2565179d3582fbb0": { + "filename": "Manhunter 1 - New York (Sierra On-Line) (OS-9) (Coco 3).zip", + "machine": "coco3", + "disk0": "Floppy 720K/mh1-1.dsk", + "joy-right": "kjoy0", + "basic": "DOS\n" + }, + "f2dd73402f82248eeadd664d41f7686880c716a9": { + "filename": "Manhunter 1 - New York (Sierra On-Line) (OS-9) (Coco 3).zip", + "machine": "coco3", + "disk0": "Floppy 720K/mh1-1.dsk", + "joy-right": "kjoy0", + "basic": "DOS\n" + }, + "cea1ce98cd1373c012cebadb674f20e96ce19364": { + "filename": "Manhunter 1 - New York (Sierra On-Line) (OS-9) (Coco 3).zip", + "machine": "coco3", + "disk0": "Floppy 720K/mh1-1.dsk", + "joy-right": "kjoy0", + "basic": "DOS\n" + }, + "9ffff1e29a9814bf09146636d937092f5fec0a7c": { + "filename": "Manhunter 1 - New York (Sierra On-Line) (OS-9) (Coco 3).zip", + "machine": "coco3", + "disk0": "Floppy 720K/mh1-1.dsk", + "joy-right": "kjoy0", + "basic": "DOS\n" + }, + "6412716d1107fc512a19fc6249278105120e85aa": { + "filename": "Manhunter 1 - New York (Sierra On-Line) (OS-9) (Coco 3).zip", + "machine": "coco3", + "disk0": "Floppy 720K/mh1-1.dsk", + "joy-right": "kjoy0", + "basic": "DOS\n" + }, + "9544cad56a66449c5f68d0e0bf68c0060c73cf13": { + "filename": "Operation Recon Demo (Dykstra-Budin Production) (OS-9) (Coco 3).zip", + "machine": "coco3", + "disk0": "Floppy 360K/recon.dsk", + "joy-right": "kjoy0", + "basic": "DOS\n" + }, + "55fc9043ac97b802e48923c972d097b7e15f29d8": { + "filename": "Operation Recon Demo (Dykstra-Budin Production) (OS-9) (Coco 3).zip", + "machine": "coco3", + "disk0": "Floppy 360K/recon.dsk", + "joy-right": "kjoy0", + "basic": "DOS\n" + }, + "f1a7494a7bd066e4c10e3ce8ddf30f5f12249c4d": { + "filename": "Operation Recon Demo (Dykstra-Budin Production) (OS-9) (Coco 3).zip", + "machine": "coco3", + "disk0": "Floppy 360K/recon.dsk", + "joy-right": "kjoy0", + "basic": "DOS\n" + }, + "9d6010242db6d194cbd149689e65b843fb08274d": { + "filename": "Operation Recon Demo (Dykstra-Budin Production) (OS-9) (Coco 3).zip", + "machine": "coco3", + "disk0": "Floppy 360K/recon.dsk", + "joy-right": "kjoy0", + "basic": "DOS\n" + }, + "ff59539ce83b0ab9b28ea02947ded41f83db976c": { + "filename": "Mixed-Up Mother Goose (Sierra On-Line) (OS-9) (Coco 3).zip", + "machine": "coco3", + "disk0": "Floppy 360K/mg-1.dsk", + "joy-right": "kjoy0", + "basic": "DOS\n" + }, + "5cd883074d81bf6b184a35212442135d46e8af33": { + "filename": "Mixed-Up Mother Goose (Sierra On-Line) (OS-9) (Coco 3).zip", + "machine": "coco3", + "disk0": "Floppy 360K/mg-1.dsk", + "joy-right": "kjoy0", + "basic": "DOS\n" + }, + "84676c117fad060458b5344253a785a53d6af4f9": { + "filename": "Mixed-Up Mother Goose (Sierra On-Line) (OS-9) (Coco 3).zip", + "machine": "coco3", + "disk0": "Floppy 360K/mg-1.dsk", + "joy-right": "kjoy0", + "basic": "DOS\n" + }, + "fcf63719a9f425cac935e6a4074481b96b360dcf": { + "filename": "Mixed-Up Mother Goose (Sierra On-Line) (OS-9) (Coco 3).zip", + "machine": "coco3", + "disk0": "Floppy 360K/mg-1.dsk", + "joy-right": "kjoy0", + "basic": "DOS\n" + }, + "94d1524dd58bdf8baad247d32f77961729f04b06": { + "filename": "Mixed-Up Mother Goose (Sierra On-Line) (OS-9) (Coco 3).zip", + "machine": "coco3", + "disk0": "Floppy 360K/mg-1.dsk", + "joy-right": "kjoy0", + "basic": "DOS\n" + }, + "a4ccaa60a6e36e57afe5fb1415b5d5896bb78962": { + "filename": "Space Quest 1 (Sierra On-Line) (OS-9) (Coco 3).zip", + "machine": "coco3", + "disk0": "Floppy 360K/sq1-1.dsk", + "joy-right": "kjoy0", + "basic": "DOS\n" + }, + "44a3c6431126062ba845f42fbb149f161644559c": { + "filename": "Space Quest 1 (Sierra On-Line) (OS-9) (Coco 3).zip", + "machine": "coco3", + "disk0": "Floppy 360K/sq1-1.dsk", + "joy-right": "kjoy0", + "basic": "DOS\n" + }, + "e4d18e70219f59b973dc5e7f7fdd3d4ddcc138de": { + "filename": "Space Quest 1 (Sierra On-Line) (OS-9) (Coco 3).zip", + "machine": "coco3", + "disk0": "Floppy 360K/sq1-1.dsk", + "joy-right": "kjoy0", + "basic": "DOS\n" + }, + "b45460cc10cb9859c75ad0798f88f75b991b733a": { + "filename": "Space Quest 1 (Sierra On-Line) (OS-9) (Coco 3).zip", + "machine": "coco3", + "disk0": "Floppy 360K/sq1-1.dsk", + "joy-right": "kjoy0", + "basic": "DOS\n" + }, + "b998f7e275bf9c055f96fd914f55d7cbb92fd123": { + "filename": "Space Quest 1 (Sierra On-Line) (OS-9) (Coco 3).zip", + "machine": "coco3", + "disk0": "Floppy 360K/sq1-1.dsk", + "joy-right": "kjoy0", + "basic": "DOS\n" + }, + "5762dfebe2adb7cfea689397de1c86c087ea1fde": { + "filename": "Space Quest 1 (Sierra On-Line) (OS-9) (Coco 3).zip", + "machine": "coco3", + "disk0": "Floppy 360K/sq1-1.dsk", + "joy-right": "kjoy0", + "basic": "DOS\n" + }, + "52988e993ca0b650765a4c949ada2d62368f20aa": { + "filename": "Police Quest 1 (Sierra On-Line) (OS-9) (Coco 3).zip", + "machine": "coco3", + "disk0": "Floppy 360K/pq1-1.dsk", + "joy-right": "kjoy0", + "basic": "DOS\n" + }, + "c812d2dd739b7c00be9966fbde026b2743b8a6f6": { + "filename": "Police Quest 1 (Sierra On-Line) (OS-9) (Coco 3).zip", + "machine": "coco3", + "disk0": "Floppy 360K/pq1-1.dsk", + "joy-right": "kjoy0", + "basic": "DOS\n" + }, + "b35a4a2ca1b4d5e8c160ba46980cdc4f02fa95bb": { + "filename": "Police Quest 1 (Sierra On-Line) (OS-9) (Coco 3).zip", + "machine": "coco3", + "disk0": "Floppy 360K/pq1-1.dsk", + "joy-right": "kjoy0", + "basic": "DOS\n" + }, + "d075fad395f7d74a73167127076f36a7fefedfe7": { + "filename": "Police Quest 1 (Sierra On-Line) (OS-9) (Coco 3).zip", + "machine": "coco3", + "disk0": "Floppy 360K/pq1-1.dsk", + "joy-right": "kjoy0", + "basic": "DOS\n" + }, + "ebdfe6246cfb49d492b362ba06cb983498e57b6c": { + "filename": "Police Quest 1 (Sierra On-Line) (OS-9) (Coco 3).zip", + "machine": "coco3", + "disk0": "Floppy 360K/pq1-1.dsk", + "joy-right": "kjoy0", + "basic": "DOS\n" + }, + "9b7f0c9515f9609452385086e598c8f49b3a718b": { + "filename": "Police Quest 1 (Sierra On-Line) (OS-9) (Coco 3).zip", + "machine": "coco3", + "disk0": "Floppy 360K/pq1-1.dsk", + "joy-right": "kjoy0", + "basic": "DOS\n" + }, + "e48342c953a9fb505dbd24461c0ee2a834b59bf9": { + "filename": "Police Quest 1 (Sierra On-Line) (OS-9) (Coco 3).zip", + "machine": "coco3", + "disk0": "Floppy 360K/pq1-1.dsk", + "joy-right": "kjoy0", + "basic": "DOS\n" + }, + "16d7f9dae8107ba6c47e8b155886ba48e65262ec": { + "filename": "Police Quest 1 (Sierra On-Line) (OS-9) (Coco 3).zip", + "machine": "coco3", + "disk0": "Floppy 360K/pq1-1.dsk", + "joy-right": "kjoy0", + "basic": "DOS\n" + }, + "5ecc60e1d2909510f2ce599d25f35e5afd4e99ee": { + "filename": "Police Quest 1 (Sierra On-Line) (OS-9) (Coco 3).zip", + "machine": "coco3", + "disk0": "Floppy 360K/pq1-1.dsk", + "joy-right": "kjoy0", + "basic": "DOS\n" + }, + "e69f191b6cd27ce6afc97a5b1c812e5d0dd065ea": { + "filename": "Space Quest 0 (Jefferson Stewart) (OS-9) (Coco 3).zip", + "machine": "coco3", + "disk0": "Floppy 360K/sq0-1.dsk", + "joy-right": "kjoy0", + "basic": "DOS\n" + }, + "5bb6a11c7d7758e1ac38a99c47d7bbd972c97797": { + "filename": "Space Quest 0 (Jefferson Stewart) (OS-9) (Coco 3).zip", + "machine": "coco3", + "disk0": "Floppy 360K/sq0-1.dsk", + "joy-right": "kjoy0", + "basic": "DOS\n" + }, + "abc4f1aee4804b7462b6a4dc71d20d6a11b34a85": { + "filename": "Space Quest 0 (Jefferson Stewart) (OS-9) (Coco 3).zip", + "machine": "coco3", + "disk0": "Floppy 360K/sq0-1.dsk", + "joy-right": "kjoy0", + "basic": "DOS\n" + }, + "17ae36dcfdae43694b9ab5671ade303292666db2": { + "filename": "Space Quest 0 (Jefferson Stewart) (OS-9) (Coco 3).zip", + "machine": "coco3", + "disk0": "Floppy 360K/sq0-1.dsk", + "joy-right": "kjoy0", + "basic": "DOS\n" + }, + "a783468d637e37370111ec33f7b6d5ee5faf8e57": { + "filename": "Space Quest 0 (Jefferson Stewart) (OS-9) (Coco 3).zip", + "machine": "coco3", + "disk0": "Floppy 360K/sq0-1.dsk", + "joy-right": "kjoy0", + "basic": "DOS\n" + }, + "8f6cc23a3109a68a44ea9ca5627cb364b6babc0b": { + "filename": "Space Quest 0 (Jefferson Stewart) (OS-9) (Coco 3).zip", + "machine": "coco3", + "disk0": "Floppy 360K/sq0-1.dsk", + "joy-right": "kjoy0", + "basic": "DOS\n" + }, + "c2e6fe70d3504686285bfcccd7316bec4670571e": { + "filename": "Space Quest 0 (Jefferson Stewart) (OS-9) (Coco 3).zip", + "machine": "coco3", + "disk0": "Floppy 360K/sq0-1.dsk", + "joy-right": "kjoy0", + "basic": "DOS\n" + }, + "4616ab407d8ba62f84fa96b27dff7db9a09b8738": { + "filename": "Space Quest 0 (Jefferson Stewart) (OS-9) (Coco 3).zip", + "machine": "coco3", + "disk0": "Floppy 360K/sq0-1.dsk", + "joy-right": "kjoy0", + "basic": "DOS\n" + }, + "6171d73e6c9f69bd334a4cb8e85c64a715e2b540": { + "filename": "Space Quest 2 (Sierra On-Line) (OS-9) (Coco 3).zip", + "machine": "coco3", + "disk0": "Floppy 360K/sq2-1.dsk", + "joy-right": "kjoy0", + "basic": "DOS\n" + }, + "6823d5b818222acc99723fde56004217bc1c81a1": { + "filename": "Space Quest 2 (Sierra On-Line) (OS-9) (Coco 3).zip", + "machine": "coco3", + "disk0": "Floppy 360K/sq2-1.dsk", + "joy-right": "kjoy0", + "basic": "DOS\n" + }, + "6013aaefc96a6330e652146187b1e9fb1c26f5c0": { + "filename": "Space Quest 2 (Sierra On-Line) (OS-9) (Coco 3).zip", + "machine": "coco3", + "disk0": "Floppy 360K/sq2-1.dsk", + "joy-right": "kjoy0", + "basic": "DOS\n" + }, + "c9b3b77655d0640296235ba43d3d271fd7d6a8e1": { + "filename": "Space Quest 2 (Sierra On-Line) (OS-9) (Coco 3).zip", + "machine": "coco3", + "disk0": "Floppy 360K/sq2-1.dsk", + "joy-right": "kjoy0", + "basic": "DOS\n" + }, + "ddf8749eb96f2a2f840c602843c78592dd70acfb": { + "filename": "Space Quest 2 (Sierra On-Line) (OS-9) (Coco 3).zip", + "machine": "coco3", + "disk0": "Floppy 360K/sq2-1.dsk", + "joy-right": "kjoy0", + "basic": "DOS\n" + }, + "d4c8910b5720e4e50955b4c147b7be7744b9716f": { + "filename": "Space Quest 2 (Sierra On-Line) (OS-9) (Coco 3).zip", + "machine": "coco3", + "disk0": "Floppy 360K/sq2-1.dsk", + "joy-right": "kjoy0", + "basic": "DOS\n" + }, + "4e3c10425ca878dbc85666084cd738b7bffa4115": { + "filename": "Space Quest 2 (Sierra On-Line) (OS-9) (Coco 3).zip", + "machine": "coco3", + "disk0": "Floppy 360K/sq2-1.dsk", + "joy-right": "kjoy0", + "basic": "DOS\n" + }, + "ff6ec8c208feedefabe0a54e98c1b1c4927a6354": { + "filename": "Time Quest (Chad Goulding) (OS-9) (Coco 3).zip", + "machine": "coco3", + "disk0": "Floppy 360K/tq.dsk", + "joy-right": "kjoy0", + "basic": "DOS\n" + }, + "e2f933a2de507955a27a360f9ee18b1d398f03a0": { + "filename": "Time Quest (Chad Goulding) (OS-9) (Coco 3).zip", + "machine": "coco3", + "disk0": "Floppy 360K/tq.dsk", + "joy-right": "kjoy0", + "basic": "DOS\n" + }, + "41f63d67c7860182b70ff8b0b10eea0837a6d25e": { + "filename": "Time Quest (Chad Goulding) (OS-9) (Coco 3).zip", + "machine": "coco3", + "disk0": "Floppy 360K/tq.dsk", + "joy-right": "kjoy0", + "basic": "DOS\n" + }, + "b8eed0817069b8d23f4a109078d821bd90654668": { + "filename": "Time Quest (Chad Goulding) (OS-9) (Coco 3).zip", + "machine": "coco3", + "disk0": "Floppy 360K/tq.dsk", + "joy-right": "kjoy0", + "basic": "DOS\n" + }, + "c15251df18ce63eebebd58490380a7fd094e25d8": { + "filename": "Time Quest (Chad Goulding) (OS-9) (Coco 3).zip", + "machine": "coco3", + "disk0": "Floppy 360K/tq.dsk", + "joy-right": "kjoy0", + "basic": "DOS\n" + }, + "17a561c3b552ebe2ed2ed7850448382429d01797": { + "filename": "Voodoo Girl - Queen of the Darned (Andrew Baker) (OS-9) (Coco 3).zip", + "machine": "coco3", + "disk0": "Floppy 360K/vg.dsk", + "joy-right": "kjoy0", + "basic": "DOS\n" + }, + "42c6909d276676bcb885fab339b177d7dbd549a9": { + "filename": "Voodoo Girl - Queen of the Darned (Andrew Baker) (OS-9) (Coco 3).zip", + "machine": "coco3", + "disk0": "Floppy 360K/vg.dsk", + "joy-right": "kjoy0", + "basic": "DOS\n" + }, + "1f46e1ea1e6b941bc335abaf7b62c4303e8fdc79": { + "filename": "Voodoo Girl - Queen of the Darned (Andrew Baker) (OS-9) (Coco 3).zip", + "machine": "coco3", + "disk0": "Floppy 360K/vg.dsk", + "joy-right": "kjoy0", + "basic": "DOS\n" + }, + "f747ba781e94926a893351d876def491e44798f1": { + "filename": "Voodoo Girl - Queen of the Darned (Andrew Baker) (OS-9) (Coco 3).zip", + "machine": "coco3", + "disk0": "Floppy 360K/vg.dsk", + "joy-right": "kjoy0", + "basic": "DOS\n" + }, + "cc02f1d070bad3260352e946fa4a5f04ff04acd8": { + "filename": "Voodoo Girl - Queen of the Darned (Andrew Baker) (OS-9) (Coco 3).zip", + "machine": "coco3", + "disk0": "Floppy 360K/vg.dsk", + "joy-right": "kjoy0", + "basic": "DOS\n" + }, + "af0d91d8ce5a433ae5a6aa75bb0c4ea86b51b385": { + "filename": "Voodoo Girl - Queen of the Darned (Andrew Baker) (OS-9) (Coco 3).zip", + "machine": "coco3", + "disk0": "Floppy 360K/vg.dsk", + "joy-right": "kjoy0", + "basic": "DOS\n" + }, + "61804f84e3cfd617f1e89297512c3a257d958052": { + "filename": "Manhunter 2 - San Francisco (Sierra On-Line) (OS-9) (Coco 3).zip", + "machine": "coco3", + "disk0": "Floppy 720K/mh2-1.dsk", + "joy-right": "kjoy0", + "basic": "DOS\n" + }, + "a1c1358c3c0ecc3321528eebd992c2980ed050f7": { + "filename": "Manhunter 2 - San Francisco (Sierra On-Line) (OS-9) (Coco 3).zip", + "machine": "coco3", + "disk0": "Floppy 720K/mh2-1.dsk", + "joy-right": "kjoy0", + "basic": "DOS\n" + }, + "ea508324b3468ca6ea7d6616eaf98d7e7bd29f06": { + "filename": "Manhunter 2 - San Francisco (Sierra On-Line) (OS-9) (Coco 3).zip", + "machine": "coco3", + "disk0": "Floppy 720K/mh2-1.dsk", + "joy-right": "kjoy0", + "basic": "DOS\n" + }, + "d443af95755811f88f0760ca1c832c86929a6ed6": { + "filename": "Manhunter 2 - San Francisco (Sierra On-Line) (OS-9) (Coco 3).zip", + "machine": "coco3", + "disk0": "Floppy 720K/mh2-1.dsk", + "joy-right": "kjoy0", + "basic": "DOS\n" + }, + "8d431ec39e626f0bd0494b3ffbf53a443e614bf8": { + "filename": "Manhunter 2 - San Francisco (Sierra On-Line) (OS-9) (Coco 3).zip", + "machine": "coco3", + "disk0": "Floppy 720K/mh2-1.dsk", + "joy-right": "kjoy0", + "basic": "DOS\n" + }, + "0c15700aa053dc61e7806c904935b03ea2c05f22": { + "filename": "Manhunter 2 - San Francisco (Sierra On-Line) (OS-9) (Coco 3).zip", + "machine": "coco3", + "disk0": "Floppy 720K/mh2-1.dsk", + "joy-right": "kjoy0", + "basic": "DOS\n" + }, + "2e75bdce7ba055a7e9ef6f33718fe618ebdf37ed": { + "filename": "Manhunter 2 - San Francisco (Sierra On-Line) (OS-9) (Coco 3).zip", + "machine": "coco3", + "disk0": "Floppy 720K/mh2-1.dsk", + "joy-right": "kjoy0", + "basic": "DOS\n" + }, + "fb30a44a57397e28e1d3072eb7ef205d647c7e2d": { + "filename": "Manhunter 2 - San Francisco (Sierra On-Line) (OS-9) (Coco 3).zip", + "machine": "coco3", + "disk0": "Floppy 720K/mh2-1.dsk", + "joy-right": "kjoy0", + "basic": "DOS\n" + }, + "c6aebaf58ef7b437b8e79f58ad129e79e32f96ff": { + "filename": "Manhunter 2 - San Francisco (Sierra On-Line) (OS-9) (Coco 3).zip", + "machine": "coco3", + "disk0": "Floppy 720K/mh2-1.dsk", + "joy-right": "kjoy0", + "basic": "DOS\n" + }, + "14ef39ebabdcad78241f9e155de1ab06dc4b202b": { + "filename": "Manhunter 2 - San Francisco (Sierra On-Line) (OS-9) (Coco 3).zip", + "machine": "coco3", + "disk0": "Floppy 720K/mh2-1.dsk", + "joy-right": "kjoy0", + "basic": "DOS\n" + }, + "0adf1d233f7c353afafdc90fcf50247f2fe5c756": { + "filename": "Manhunter 2 - San Francisco (Sierra On-Line) (OS-9) (Coco 3).zip", + "machine": "coco3", + "disk0": "Floppy 720K/mh2-1.dsk", + "joy-right": "kjoy0", + "basic": "DOS\n" + }, + "df33a1cfb794a85fe0ed27a5bd4fc8d5459b60ad": { + "filename": "Manhunter 2 - San Francisco (Sierra On-Line) (OS-9) (Coco 3).zip", + "machine": "coco3", + "disk0": "Floppy 720K/mh2-1.dsk", + "joy-right": "kjoy0", + "basic": "DOS\n" + }, + "c516c314e3d6d397a0db665e9d979d7ebe6a8e21": { + "filename": "Manhunter 2 - San Francisco (Sierra On-Line) (OS-9) (Coco 3).zip", + "machine": "coco3", + "disk0": "Floppy 720K/mh2-1.dsk", + "joy-right": "kjoy0", + "basic": "DOS\n" + }, + "a0a2f1ced65c34f6d1ff7702f466cf33c37e8f88": { + "filename": "Manhunter 2 - San Francisco (Sierra On-Line) (OS-9) (Coco 3).zip", + "machine": "coco3", + "disk0": "Floppy 720K/mh2-1.dsk", + "joy-right": "kjoy0", + "basic": "DOS\n" + }, + "d24955b9b51d18354eb9145c57b9d1a0645c63f1": { + "filename": "Manhunter 2 - San Francisco (Sierra On-Line) (OS-9) (Coco 3).zip", + "machine": "coco3", + "disk0": "Floppy 720K/mh2-1.dsk", + "joy-right": "kjoy0", + "basic": "DOS\n" + }, + "fc3f7b64a178c578197eedc9feefeebf9749cc5f": { + "filename": "Manhunter 2 - San Francisco (Sierra On-Line) (OS-9) (Coco 3).zip", + "machine": "coco3", + "disk0": "Floppy 720K/mh2-1.dsk", + "joy-right": "kjoy0", + "basic": "DOS\n" + }, + "7d88d516ea0b9bf637ea478a31f31b50de059b3d": { + "filename": "Manhunter 2 - San Francisco (Sierra On-Line) (OS-9) (Coco 3).zip", + "machine": "coco3", + "disk0": "Floppy 720K/mh2-1.dsk", + "joy-right": "kjoy0", + "basic": "DOS\n" + }, + "ce0e25c7b51c749da9e9af9ea914c14264120509": { + "filename": "Manhunter 2 - San Francisco (Sierra On-Line) (OS-9) (Coco 3).zip", + "machine": "coco3", + "disk0": "Floppy 720K/mh2-1.dsk", + "joy-right": "kjoy0", + "basic": "DOS\n" + }, + "1ee3908aeb5dcdad2f9956ffd7c243b11db46f9e": { + "filename": "Star Commander I (Coyle Home Studios) (OS-9) (Coco 3).zip", + "machine": "coco3", + "disk0": "Floppy 720K/starcom.dsk", + "joy-right": "kjoy0", + "basic": "DOS\n" + }, + "5f4cf5a7af4c113d1fd42617041482954de73c40": { + "filename": "Star Commander I (Coyle Home Studios) (OS-9) (Coco 3).zip", + "machine": "coco3", + "disk0": "Floppy 720K/starcom.dsk", + "joy-right": "kjoy0", + "basic": "DOS\n" + }, + "21037feb4adf0a5a7848653f7a44ca993841615d": { + "filename": "Star Commander I (Coyle Home Studios) (OS-9) (Coco 3).zip", + "machine": "coco3", + "disk0": "Floppy 720K/starcom.dsk", + "joy-right": "kjoy0", + "basic": "DOS\n" + }, + "fb2aad6cb8c82dc1ef1bcd6e15e0561e90ea69a8": { + "filename": "Star Commander I (Coyle Home Studios) (OS-9) (Coco 3).zip", + "machine": "coco3", + "disk0": "Floppy 720K/starcom.dsk", + "joy-right": "kjoy0", + "basic": "DOS\n" + }, + "b7e4c4574e125b4d4868a43355d6f00d34c52374": { + "filename": "Star Commander I (Coyle Home Studios) (OS-9) (Coco 3).zip", + "machine": "coco3", + "disk0": "Floppy 720K/starcom.dsk", + "joy-right": "kjoy0", + "basic": "DOS\n" + }, + "ede7abb9640c037e742ba00dd81d87c14602c630": { + "filename": "Star Commander I (Coyle Home Studios) (OS-9) (Coco 3).zip", + "machine": "coco3", + "disk0": "Floppy 720K/starcom.dsk", + "joy-right": "kjoy0", + "basic": "DOS\n" + }, + "8698bc14cf9dfb05f40aab2b4ca15c461de118c1": { + "filename": "Star Commander I (Coyle Home Studios) (OS-9) (Coco 3).zip", + "machine": "coco3", + "disk0": "Floppy 720K/starcom.dsk", + "joy-right": "kjoy0", + "basic": "DOS\n" + }, + "221d40b8844c501ba33e3d0d882cde0d58b498aa": { + "filename": "Star Commander I (Coyle Home Studios) (OS-9) (Coco 3).zip", + "machine": "coco3", + "disk0": "Floppy 720K/starcom.dsk", + "joy-right": "kjoy0", + "basic": "DOS\n" + }, + "6f235f9f92ee88b7cba9a252dd609872979b53ad": { + "filename": "Star Commander I (Coyle Home Studios) (OS-9) (Coco 3).zip", + "machine": "coco3", + "disk0": "Floppy 720K/starcom.dsk", + "joy-right": "kjoy0", + "basic": "DOS\n" + }, + "a183810bbe406349c249d42b0e651d383bbc8a0b": { + "filename": "V - The Graphic Adventure (Brainburst Interactive) (OS-9) (Coco 3).zip", + "machine": "coco3", + "disk0": "Floppy 360K/v-1.dsk", + "joy-right": "kjoy0", + "basic": "DOS\n" + }, + "971ca3da3bcdd052489a079a28e29227d5e1ded9": { + "filename": "V - The Graphic Adventure (Brainburst Interactive) (OS-9) (Coco 3).zip", + "machine": "coco3", + "disk0": "Floppy 360K/v-1.dsk", + "joy-right": "kjoy0", + "basic": "DOS\n" + }, + "dd97148cf63eef85c5876dee0ebfcc077eac8095": { + "filename": "V - The Graphic Adventure (Brainburst Interactive) (OS-9) (Coco 3).zip", + "machine": "coco3", + "disk0": "Floppy 360K/v-1.dsk", + "joy-right": "kjoy0", + "basic": "DOS\n" + }, + "12367fbacdfe3043a0e57e0d52fec820ae3f6d73": { + "filename": "V - The Graphic Adventure (Brainburst Interactive) (OS-9) (Coco 3).zip", + "machine": "coco3", + "disk0": "Floppy 360K/v-1.dsk", + "joy-right": "kjoy0", + "basic": "DOS\n" + }, + "ba4df95fb2e344ae24203a74a2c192eb7fad5481": { + "filename": "V - The Graphic Adventure (Brainburst Interactive) (OS-9) (Coco 3).zip", + "machine": "coco3", + "disk0": "Floppy 360K/v-1.dsk", + "joy-right": "kjoy0", + "basic": "DOS\n" + }, + "5dae9d559624f72052537d4f3b2e6d4956a4c337": { + "filename": "V - The Graphic Adventure (Brainburst Interactive) (OS-9) (Coco 3).zip", + "machine": "coco3", + "disk0": "Floppy 360K/v-1.dsk", + "joy-right": "kjoy0", + "basic": "DOS\n" + }, + "154e633b221f3d4dcf651d20ba65b8db95ed5253": { + "filename": "V - The Graphic Adventure (Brainburst Interactive) (OS-9) (Coco 3).zip", + "machine": "coco3", + "disk0": "Floppy 360K/v-1.dsk", + "joy-right": "kjoy0", + "basic": "DOS\n" + }, + "68ed2c834078eca5b7dc38bfb29019f927581b2f": { + "filename": "Space Quest - The Lost Chapter (Vonster) (OS-9) (Coco 3).zip", + "machine": "coco3", + "disk0": "Floppy 720K/sqtlc-1.dsk", + "joy-right": "kjoy0", + "basic": "DOS\n" + }, + "15bffe703db7aed0300a815283508f8b52c6394b": { + "filename": "Space Quest - The Lost Chapter (Vonster) (OS-9) (Coco 3).zip", + "machine": "coco3", + "disk0": "Floppy 720K/sqtlc-1.dsk", + "joy-right": "kjoy0", + "basic": "DOS\n" + }, + "ba8e8bc3a7dc4b50b3c4d942cbbd2d7d2c0b0b85": { + "filename": "Space Quest - The Lost Chapter (Vonster) (OS-9) (Coco 3).zip", + "machine": "coco3", + "disk0": "Floppy 720K/sqtlc-1.dsk", + "joy-right": "kjoy0", + "basic": "DOS\n" + }, + "53b87a186db8925169c69e554fb3cf902b6f66b1": { + "filename": "Space Quest - The Lost Chapter (Vonster) (OS-9) (Coco 3).zip", + "machine": "coco3", + "disk0": "Floppy 720K/sqtlc-1.dsk", + "joy-right": "kjoy0", + "basic": "DOS\n" + }, + "a91f32270b2043d557837373ce7148d123522648": { + "filename": "Space Quest - The Lost Chapter (Vonster) (OS-9) (Coco 3).zip", + "machine": "coco3", + "disk0": "Floppy 720K/sqtlc-1.dsk", + "joy-right": "kjoy0", + "basic": "DOS\n" + }, + "e8f88230bd7eaef808b76649110764348d6c658c": { + "filename": "Space Quest - The Lost Chapter (Vonster) (OS-9) (Coco 3).zip", + "machine": "coco3", + "disk0": "Floppy 720K/sqtlc-1.dsk", + "joy-right": "kjoy0", + "basic": "DOS\n" + }, + "02d799ab9ac39cecf55006ccd5e4cde7078369c0": { + "filename": "Space Quest - The Lost Chapter (Vonster) (OS-9) (Coco 3).zip", + "machine": "coco3", + "disk0": "Floppy 720K/sqtlc-1.dsk", + "joy-right": "kjoy0", + "basic": "DOS\n" + }, + "a2692b901d3c931e9c9b14215e5ac90cb7b8384b": { + "filename": "Space Quest - The Lost Chapter (Vonster) (OS-9) (Coco 3).zip", + "machine": "coco3", + "disk0": "Floppy 720K/sqtlc-1.dsk", + "joy-right": "kjoy0", + "basic": "DOS\n" + }, + "ce50144db793454fafc9deb3c0d7f1c128bd62b6": { + "filename": "Space Quest - The Lost Chapter (Vonster) (OS-9) (Coco 3).zip", + "machine": "coco3", + "disk0": "Floppy 720K/sqtlc-1.dsk", + "joy-right": "kjoy0", + "basic": "DOS\n" + }, + "e6d79b376f20f8cafd2844571393ac5a2773d329": { + "filename": "Space Quest - The Lost Chapter (Vonster) (OS-9) (Coco 3).zip", + "machine": "coco3", + "disk0": "Floppy 720K/sqtlc-1.dsk", + "joy-right": "kjoy0", + "basic": "DOS\n" + }, + "2325140c8f6e377227a8d1e74a586b7fa54a582f": { + "filename": "Space Quest - The Lost Chapter (Vonster) (OS-9) (Coco 3).zip", + "machine": "coco3", + "disk0": "Floppy 720K/sqtlc-1.dsk", + "joy-right": "kjoy0", + "basic": "DOS\n" + }, + "4dd4d0fc73e789df85a76d22ab0e02243ba56052": { + "filename": "Space Quest - The Lost Chapter (Vonster) (OS-9) (Coco 3).zip", + "machine": "coco3", + "disk0": "Floppy 720K/sqtlc-1.dsk", + "joy-right": "kjoy0", + "basic": "DOS\n" + } +} \ No newline at end of file diff --git a/package/batocera/core/batocera-configgen/data/mame/autoboot_scripts/cpc_cass.lua b/package/batocera/core/batocera-configgen/data/mame/autoboot_scripts/cpc_cass.lua new file mode 100644 index 00000000000..9a8119018b4 --- /dev/null +++ b/package/batocera/core/batocera-configgen/data/mame/autoboot_scripts/cpc_cass.lua @@ -0,0 +1,78 @@ +-- cpc_cass.lua + +-- Load common autoboot functions +local common_autoboot = dofile("/usr/share/batocera/configgen/data/mame/autoboot_scripts/autoboot_common.lua") + +-- Script-wide variables +local button = {} +local frame_num = 0 + +-- --- Cassette Loading Handler Setup --- +local CASSETTE_SLOT = 3 +local CASSETTE_DEVICE_TAG = manager.machine.images:at(CASSETTE_SLOT).device.tag +local cassette_handler = common_autoboot.create_cassette_handler(CASSETTE_DEVICE_TAG) + +common_autoboot.populate_buttons(button) + +common_autoboot.print_image_info() + +-- --- Constants for this specific script --- +local BUTTON_PRESS_DURATION = common_autoboot.DEFAULT_BUTTON_PRESS_DURATION +local CASSETTE_MOTOR_OFF_DELAY_FRAMES = common_autoboot.DEFAULT_CASSETTE_MOTOR_OFF_DELAY_FRAMES + +local function boot_default(cassette_handler) + common_autoboot.type_at_frame(frame_num, "RUN\"\n", 100) + common_autoboot.play_cassette_at_frame(frame_num, CASSETTE_DEVICE_TAG, 200) + common_autoboot.type_at_frame(frame_num, "\n", 250) + + local cassette_load_done = cassette_handler(frame_num, CASSETTE_MOTOR_OFF_DELAY_FRAMES) + + if cassette_load_done then + -- emu.keypost('RUN\n') + end +end + +-- --- Main Script Logic --- + +-- Determine the currently loaded software name +local current_software_name = manager.machine.images:at(CASSETTE_SLOT).filename + +-- Map software names to their respective boot functions +local boot_sequences = { + ['default'] = boot_default, +} + +-- MAME machine frame notification callback +local function process_frame() + frame_num = frame_num + 1 + + -- Initial setup/info display at frame 1 + if frame_num == 1 then + emu.print_info("System Driver: " .. emu.romname()) + emu.print_info("Loaded Software Name (from image filename): " .. current_software_name) + + -- Determine which profile will be used + if boot_sequences[current_software_name] then + emu.print_info("--- Booting using game specific profile: " .. current_software_name .. " ---") + else + emu.print_info("--- Booting using default profile ---") + end + end + + --- DEBUG: Print current frame number every 100 frames --- + common_autoboot.debug_frame_num(frame_num) + + -- Execute the relevant boot sequence based on the loaded software + local boot_function = boot_sequences[current_software_name] + + if not boot_function then -- If specific function not found + boot_function = boot_sequences["default"] -- Assign the default function + end + + if boot_function then + boot_function(cassette_handler) + end +end + +-- Subscribe to machine frame notifications +subscription = emu.add_machine_frame_notifier(process_frame) \ No newline at end of file diff --git a/package/batocera/core/batocera-configgen/data/mame/autoboot_scripts/cpc_flop.lua b/package/batocera/core/batocera-configgen/data/mame/autoboot_scripts/cpc_flop.lua new file mode 100644 index 00000000000..dfd24739d92 --- /dev/null +++ b/package/batocera/core/batocera-configgen/data/mame/autoboot_scripts/cpc_flop.lua @@ -0,0 +1,122 @@ +cpu = manager.machine.devices[":maincpu"] +mem = cpu.spaces["program"] +base_addr = 0x9E7D +base_file_name_size = 8 +ext_file_name_size = 3 +next_file = 14 +file_index = 0 +cat_frame = 0 +try_run = 0 +type_buffer = {} +type_qty = 0 +memory_clean_frame = 0 + +local function stack_char(char) + table.insert(type_buffer, 1, char) + type_qty = type_qty + 1 +end + +local function cat(frame) + stack_char("c") + stack_char("a") + stack_char("t") + stack_char("\n") + cat_frame = frame +end + +local function is_extension_allowed() + addr = base_addr + file_index * next_file + base_file_name_size + + ext = {} + for c = 0, 2 do + ext[c+1] = mem:read_u8(addr + c) & 0x7F + end + + forbidden_ext = { string.byte('T'), string.byte('X'), string.byte('T')} + + allowed = 0 + + for c = 1, 3 do + if forbidden_ext[c] ~= ext[c] then + allowed = 1 + break + end + end + + return allowed +end + + +local function run_file() + if is_extension_allowed() == 0 then + file_index = file_index + 1 + return + end + + stack_char("r") + stack_char("u") + stack_char("n") + stack_char("\"") + for c = 0, base_file_name_size-1 do + addr = base_addr + file_index * next_file + c + char = mem:read_u8(addr) + if char ~= 0x20 then + char = char & 0x7F + stack_char(string.char(char)) + end + end + + stack_char("\n") + try_run = 1 +end + +local function wait_for_clean_memory(frame) + if memory_clean_frame ~= 0 then + if frame > memory_clean_frame + 25 then + try_run = 0 + cat_frame = 0 + memory_clean_frame = 0 + file_index = file_index + 1 + end + else + start_file = mem:read_u8(base_addr + file_index * next_file) + if start_file == 0 then + memory_clean_frame = frame + end + end +end + +local function unstack_char() + if type_qty > 0 then + char = table.remove(type_buffer) + type_qty = type_qty - 1 + emu.keypost(char) + end +end + +frame_num = 0 +local function process_frame() + frame_num = frame_num + 1 + + if cat_frame == 0 then + if frame_num > 250 then + cat(frame_num) + end + else + if try_run == 0 then + if frame_num > cat_frame + 250 then + run_file() + end + else + wait_for_clean_memory(frame_num) + end + end + + if frame_num % 5 == 0 then + unstack_char() + end +end + +emu.keypost("|disc\n") +emu.keypost("run\"disc\n") +subscription=emu.add_machine_frame_notifier(process_frame) diff --git a/package/batocera/core/batocera-configgen/data/mame/autoboot_scripts/crvision.lua b/package/batocera/core/batocera-configgen/data/mame/autoboot_scripts/crvision.lua new file mode 100644 index 00000000000..0fb78679f32 --- /dev/null +++ b/package/batocera/core/batocera-configgen/data/mame/autoboot_scripts/crvision.lua @@ -0,0 +1,69 @@ +-- _cass.lua + +-- Load common autoboot functions +local common_autoboot = dofile("/usr/share/batocera/configgen/data/mame/autoboot_scripts/autoboot_common.lua") + +-- Script-wide variables +local button = {} +local frame_num = 0 + +-- --- Map all buttons --- +common_autoboot.populate_buttons(button) + +-- --- Print Info --- +common_autoboot.print_image_info() +-- common_autoboot.print_buttons(button) + +local CARTRIDGE_SLOT = 3 +local current_software_name = manager.machine.images:at(CARTRIDGE_SLOT).filename + +-- --- Constants for this specific script --- +local BUTTON_PRESS_DURATION = common_autoboot.DEFAULT_BUTTON_PRESS_DURATION + +-- Press F10, then R +local function boot_default() + common_autoboot.press_and_release(button, frame_num, "Reset", 200, 10) + common_autoboot.type_at_frame(frame_num, "R", 300) +end + +-- --- Main Script Logic --- + +-- Map software names to their respective boot functions +boot_sequences = { + ['default'] = boot_default, +} + +-- MAME machine frame notification callback +local function process_frame() + frame_num = frame_num + 1 + + -- Initial setup/info display at frame 1 + if frame_num == 1 then + emu.print_info("System Driver: " .. emu.romname()) + emu.print_info("Loaded Software Name (from image filename): " .. current_software_name) + + -- Determine which profile will be used + if boot_sequences[current_software_name] then + emu.print_info("--- Booting using game specific profile: " .. current_software_name .. " ---") + else + emu.print_info("--- Booting using default profile ---") + end + end + + --- DEBUG: Print current frame number every 100 frames --- + common_autoboot.debug_frame_num(frame_num) + + -- Execute the relevant boot sequence based on the loaded software + local boot_function = boot_sequences[current_software_name] + + if not boot_function then -- If specific function not found + boot_function = boot_sequences["default"] -- Assign the default function + end + + if boot_function then + boot_function() + end +end + +-- Subscribe to machine frame notifications +subscription = emu.add_machine_frame_notifier(process_frame) \ No newline at end of file diff --git a/package/batocera/core/batocera-configgen/data/mame/autoboot_scripts/dai_cass.lua b/package/batocera/core/batocera-configgen/data/mame/autoboot_scripts/dai_cass.lua new file mode 100644 index 00000000000..f89d319d833 --- /dev/null +++ b/package/batocera/core/batocera-configgen/data/mame/autoboot_scripts/dai_cass.lua @@ -0,0 +1,97 @@ +-- dai_cass.lua + +-- Load common autoboot functions +local common_autoboot = dofile("/usr/share/batocera/configgen/data/mame/autoboot_scripts/autoboot_common.lua") + +-- Script-wide variables +local button = {} +local frame_num = 0 + +-- --- Map all buttons --- +common_autoboot.populate_buttons(button) + +-- --- Print Info --- +common_autoboot.print_image_info() +-- common_autoboot.print_buttons(button) + +-- --- Cassette Loading Handler Setup --- +local CASSETTE_SLOT = 1 +local CASSETTE_DEVICE_TAG = manager.machine.images:at(CASSETTE_SLOT).device.tag +local cassette_handler = common_autoboot.create_cassette_handler(CASSETTE_DEVICE_TAG) + + +-- --- Constants for this specific script --- +local BUTTON_PRESS_DURATION = common_autoboot.DEFAULT_BUTTON_PRESS_DURATION +local CASSETTE_MOTOR_OFF_DELAY_FRAMES = common_autoboot.DEFAULT_CASSETTE_MOTOR_OFF_DELAY_FRAMES + +local function boot_default(cassette_handler) + common_autoboot.type_at_frame(frame_num, "\n", 100) + common_autoboot.type_at_frame(frame_num, "LOAD\n", 200) + common_autoboot.play_cassette_at_frame(frame_num, CASSETTE_DEVICE_TAG, 300) + + local cassette_load_done = cassette_handler(frame_num, CASSETTE_MOTOR_OFF_DELAY_FRAMES) + + if cassette_load_done then + emu.keypost('RUN\n') + end +end + +local function boot_acrobat(cassette_handler) + common_autoboot.type_at_frame(frame_num, "\n", 100) + common_autoboot.type_at_frame(frame_num, "UT\n", 200) + common_autoboot.type_at_frame(frame_num, "Z3\n", 300) + common_autoboot.type_at_frame(frame_num, "R\n", 400) + common_autoboot.play_cassette_at_frame(frame_num, CASSETTE_DEVICE_TAG, 500) + + local cassette_load_done = cassette_handler(frame_num, CASSETTE_MOTOR_OFF_DELAY_FRAMES) + + if cassette_load_done then + emu.keypost('G400\n') + end +end + +-- --- Main Script Logic --- + +-- Determine the currently loaded software name +local current_software_name = manager.machine.images:at(CASSETTE_SLOT).filename + +-- Map software names to their respective boot functions +local boot_sequences = { + ['default'] = boot_default, + acrobat = boot_acrobat, +} + +-- MAME machine frame notification callback +local function process_frame() + frame_num = frame_num + 1 + + -- Initial setup/info display at frame 1 + if frame_num == 1 then + emu.print_info("System Driver: " .. emu.romname()) + emu.print_info("Loaded Software Name (from image filename): " .. current_software_name) + + -- Determine which profile will be used + if boot_sequences[current_software_name] then + emu.print_info("--- Booting using game specific profile: " .. current_software_name .. " ---") + else + emu.print_info("--- Booting using default profile ---") + end + end + + --- DEBUG: Print current frame number every 100 frames --- + common_autoboot.debug_frame_num(frame_num) + + -- Execute the relevant boot sequence based on the loaded software + local boot_function = boot_sequences[current_software_name] + + if not boot_function then -- If specific function not found + boot_function = boot_sequences["default"] -- Assign the default function + end + + if boot_function then + boot_function(cassette_handler) + end +end + +-- Subscribe to machine frame notifications +subscription = emu.add_machine_frame_notifier(process_frame) \ No newline at end of file diff --git a/package/batocera/core/batocera-configgen/data/mame/autoboot_scripts/dragon_cass.lua b/package/batocera/core/batocera-configgen/data/mame/autoboot_scripts/dragon_cass.lua new file mode 100644 index 00000000000..84e1db4a7cf --- /dev/null +++ b/package/batocera/core/batocera-configgen/data/mame/autoboot_scripts/dragon_cass.lua @@ -0,0 +1,330 @@ +-- dragon_cass.lua +-- +-- Dragon cassette autoboot with CoCo/Dragon CAS header parsing. +-- This is a safer default than blind CLOAD/RUN: +-- type 0 (BASIC) -> CLOAD, then RUN after load completes +-- type 2 (machine) -> CLOADM:EXEC when load >= $01A9, else CLOADM +-- +-- RUN is sent via whichever fires first: +-- a) Hardware cassette motor turns off (works for CAS with EOF block + raw audio) +-- b) Dragon BASIC "OK" prompt detected on screen (fallback for CAS without EOF block) + +local common_autoboot = dofile("/usr/share/batocera/configgen/data/mame/autoboot_scripts/autoboot_common.lua") +local zip_util = dofile("/usr/share/batocera/configgen/data/mame/autoboot_scripts/autoboot_zip.lua") + +local CAS_SCAN_BYTES = 4096 + +local TAPE_TYPE_BASIC = 0 +local TAPE_TYPE_DATA = 1 +local TAPE_TYPE_MACHINE = 2 + +local BUTTON_PRESS_DURATION = common_autoboot.DEFAULT_BUTTON_PRESS_DURATION +local CASSETTE_MOTOR_OFF_DELAY_FRAMES = common_autoboot.DEFAULT_CASSETTE_MOTOR_OFF_DELAY_FRAMES + +-- Dragon video RAM: 0x0400-0x05FF, 32 cols x 16 rows +local DRAGON_SCREEN_RAM = 0x0400 +local DRAGON_SCREEN_COLS = 32 +local DRAGON_SCREEN_ROWS = 16 + +local button = {} +local frame_num = 0 + +common_autoboot.populate_buttons(button) + +local function be16(data, pos) + local hi = data:byte(pos) or 0 + local lo = data:byte(pos + 1) or 0 + return (hi << 8) | lo +end + +local function find_cassette_path() + for _, cass in pairs(manager.machine.cassettes) do + if cass.exists and cass.filename and #cass.filename > 0 then + print("[Dragon Autoboot] Cassette device: " .. cass.filename) + return cass.filename + end + end + + for _, img in pairs(manager.machine.images) do + if img.exists and img.filename and #img.filename > 0 then + local fn = img.filename:lower() + if fn:find("%.cas$") or fn:find("%.wav$") then + print("[Dragon Autoboot] Image device cassette: " .. img.filename) + return img.filename + end + end + end + + return nil +end + +local function parse_cas_header(data) + if not data or #data < 20 then + return nil + end + + local i = 1 + local saw_leader = false + + while i <= #data do + local b = data:byte(i) + if b == 0x55 then + saw_leader = true + i = i + 1 + elseif b == 0x3C and saw_leader then + local base = i + 1 + if base + 16 > #data then + return nil + end + + local block_type = data:byte(base) + local block_size = data:byte(base + 1) + + if block_type == 0x00 and block_size >= 15 then + local dstart = base + 2 + local file_type = data:byte(dstart + 8) + local start_address = be16(data, dstart + 11) + local load_address = be16(data, dstart + 13) + local name = {} + + for fi = 0, 7 do + local c = data:byte(dstart + fi) + if c and c ~= 0x20 and c ~= 0x00 then + table.insert(name, string.char(c)) + end + end + + print(string.format( + "[Dragon Autoboot] CAS header: file='%s' type=%d load=0x%04X exec=0x%04X", + table.concat(name), file_type or -1, load_address, start_address)) + + return { + file_type = file_type, + load_address = load_address, + start_address = start_address, + } + end + + saw_leader = false + i = i + 1 + else + saw_leader = false + i = i + 1 + end + end + + return nil +end + +local function detect_tape_info() + print("[Dragon Autoboot] --- Tape type detection ---") + + local path = find_cassette_path() + if not path then + print("[Dragon Autoboot] No cassette image found.") + return { file_type = TAPE_TYPE_BASIC, load_address = 0 } + end + + local data = zip_util.read_bytes(path, CAS_SCAN_BYTES) + if not data then + print("[Dragon Autoboot] Could not read cassette data, defaulting to BASIC.") + return { file_type = TAPE_TYPE_BASIC, load_address = 0 } + end + + local info = parse_cas_header(data) + if info then + return info + end + + print("[Dragon Autoboot] No valid CAS header found, defaulting to BASIC.") + return { file_type = TAPE_TYPE_BASIC, load_address = 0 } +end + +-- ── Low-level helpers ───────────────────────────────────────────────────────── + +local function vram_read(addr) + local cpu = manager.machine.devices[":maincpu"] + if not cpu then return nil end + local mem = cpu.spaces["program"] + if not mem then return nil end + return mem:read_u8(addr) +end + +-- Returns true if bytes b1,b2 look like Dragon BASIC's "OK" string. +-- Dragon MC6847 alphanumeric: 'A'=0x01…'Z'=0x1A (ASCII minus 0x40). +-- 'O'=0x0F, 'K'=0x0B +-- Some Dragon 64 ROMs may write raw ASCII values instead: +-- 'O'=0x4F, 'K'=0x4B +local function is_ok_bytes(b1, b2) + return (b1 == 0x0F and b2 == 0x0B) or -- MC6847 native + (b1 == 0x4F and b2 == 0x4B) -- raw ASCII +end + +-- Scan entire video RAM; return a table of {addr=true} for every "OK" found. +local function collect_ok_addrs() + local found = {} + for row = 0, DRAGON_SCREEN_ROWS - 1 do + for col = 0, DRAGON_SCREEN_COLS - 2 do + local addr = DRAGON_SCREEN_RAM + row * DRAGON_SCREEN_COLS + col + local b1 = vram_read(addr) + local b2 = vram_read(addr + 1) + if b1 and b2 and is_ok_bytes(b1, b2) then + found[addr] = true + end + end + end + return found +end + +-- Dump a single row of video RAM as hex for diagnostics. +local function dump_vram_row(row) + local bytes = {} + for col = 0, DRAGON_SCREEN_COLS - 1 do + local b = vram_read(DRAGON_SCREEN_RAM + row * DRAGON_SCREEN_COLS + col) + table.insert(bytes, b and string.format("%02X", b) or "??") + end + print(string.format("[Dragon Autoboot] VRAM row %2d: %s", row, table.concat(bytes, " "))) +end + +-- ── Boot state ──────────────────────────────────────────────────────────────── + +local ok_baseline = {} -- OK addresses present before tape plays +local ok_baseline_done = false +local ok_seen_frame = 0 +local motor_was_on = false +local motor_off_start = 0 +local run_sent = false +local cassette_handler = common_autoboot.create_cassette_handler(":cassette") +local tape_info = nil + +local function send_run() + print("[Dragon Autoboot] Sending RUN at frame " .. frame_num) + manager.machine.video.throttled = true + manager.machine.video.frameskip = 0 + emu.keypost("RUN\n") + run_sent = true +end + +-- ── boot_basic ──────────────────────────────────────────────────────────────── + +local function boot_basic() + common_autoboot.type_at_frame(frame_num, "CLOAD\n", 200, BUTTON_PRESS_DURATION) + common_autoboot.play_cassette_at_frame(frame_num, ":cassette", 300) + + if run_sent then return end + + -- Hold throttle OFF from the moment the tape starts until completion. + -- We manage this directly rather than relying on cassette_handler so that + -- brief motor toggles between blocks don't stutter back to normal speed. + if frame_num >= 300 then + manager.machine.video.throttled = false + manager.machine.video.frameskip = 12 + end + + -- ── Baseline capture ─────────────────────────────────────────────────── + -- Snapshot any "OK" already on screen (from the Dragon BASIC startup banner) + -- just after CLOAD is typed but well before the tape data arrives. + if frame_num == 350 then + ok_baseline = collect_ok_addrs() + ok_baseline_done = true + local n = 0 + for _ in pairs(ok_baseline) do n = n + 1 end + print(string.format("[Dragon Autoboot] Baseline: %d OK addr(s) in VRAM", n)) + -- Dump first 4 rows for diagnosis + for r = 0, 3 do dump_vram_row(r) end + end + + -- ── Periodic diagnostics ────────────────────────────────────────────── + if frame_num % 600 == 0 and frame_num > 300 then + local cass = manager.machine.cassettes[":cassette"] + local ms = cass and tostring(cass.motor_state) or "n/a" + local ist = cass and tostring(cass.is_stopped) or "n/a" + local b0 = vram_read(DRAGON_SCREEN_RAM) + print(string.format("[Dragon Autoboot] frame=%d motor=%s is_stopped=%s vram[0]=0x%s", + frame_num, ms, ist, b0 and string.format("%02X", b0) or "??")) + end + + -- ── Method 1: hardware motor turns off ──────────────────────────────── + -- Track when motor was on, then detect it going off with a short debounce. + if frame_num > 360 then + local cass = manager.machine.cassettes[":cassette"] + local motor = cass and cass.motor_state or nil + if motor == true then + motor_was_on = true + motor_off_start = 0 + elseif motor == false and motor_was_on then + if motor_off_start == 0 then + motor_off_start = frame_num + print("[Dragon Autoboot] Motor OFF at frame " .. frame_num) + elseif frame_num >= motor_off_start + CASSETTE_MOTOR_OFF_DELAY_FRAMES then + print("[Dragon Autoboot] Motor off confirmed → RUN") + send_run() + return + end + end + end + + -- ── Method 2: "OK" prompt on screen ─────────────────────────────────── + -- Dragon BASIC always prints "OK" when returning to the command prompt. + -- Check every 30 frames to keep overhead low. + if ok_baseline_done and frame_num >= 400 and frame_num % 30 == 0 then + local new_ok = false + local current = collect_ok_addrs() + for addr in pairs(current) do + if not ok_baseline[addr] then + local row = math.floor((addr - DRAGON_SCREEN_RAM) / DRAGON_SCREEN_COLS) + local col = (addr - DRAGON_SCREEN_RAM) % DRAGON_SCREEN_COLS + local b1 = vram_read(addr) + print(string.format("[Dragon Autoboot] New OK row=%d col=%d addr=0x%04X b1=0x%02X frame=%d", + row, col, addr, b1 or 0, frame_num)) + new_ok = true + break + end + end + + if new_ok then + if ok_seen_frame == 0 then + ok_seen_frame = frame_num + -- Dump last rows when we first see it, to verify encoding + for r = 12, 15 do dump_vram_row(r) end + elseif frame_num >= ok_seen_frame + 30 then + print("[Dragon Autoboot] Screen OK confirmed → RUN") + send_run() + end + else + ok_seen_frame = 0 + end + end +end + +local function boot_machine() + local need_exec = (tape_info.load_address or 0) >= 0x01A9 + local cmd = need_exec and "CLOADM:EXEC\n" or "CLOADM\n" + + common_autoboot.type_at_frame(frame_num, cmd, 200, BUTTON_PRESS_DURATION) + common_autoboot.play_cassette_at_frame(frame_num, ":cassette", 300) + + cassette_handler(frame_num, CASSETTE_MOTOR_OFF_DELAY_FRAMES) +end + +local function process_frame() + frame_num = frame_num + 1 + + if frame_num == 1 then + emu.print_info("System Driver: " .. emu.romname()) + common_autoboot.print_image_info() + tape_info = detect_tape_info() + end + + common_autoboot.debug_frame_num(frame_num) + + if not tape_info or tape_info.file_type == TAPE_TYPE_DATA then + boot_basic() + elseif tape_info.file_type == TAPE_TYPE_MACHINE then + boot_machine() + else + boot_basic() + end +end + +subscription = emu.add_machine_frame_notifier(process_frame) diff --git a/package/batocera/core/batocera-configgen/data/mame/autoboot_scripts/einstein.lua b/package/batocera/core/batocera-configgen/data/mame/autoboot_scripts/einstein.lua new file mode 100644 index 00000000000..14980cf4d6e --- /dev/null +++ b/package/batocera/core/batocera-configgen/data/mame/autoboot_scripts/einstein.lua @@ -0,0 +1,76 @@ +-- einstein.lua +-- Most titles require DIR then run the .COM. Some titles are automatically run, esp. those that boot using XTALDOS 2.0 +-- WIP: need to go through each games and see the .COM file + +-- Load common autoboot functions +local common_autoboot = dofile("/usr/share/batocera/configgen/data/mame/autoboot_scripts/autoboot_common.lua") + +-- Script-wide variables +local button = {} +local frame_num = 0 + +-- --- Map all buttons --- +common_autoboot.populate_buttons(button) + +-- --- Print Info --- +common_autoboot.print_image_info() +-- common_autoboot.print_buttons(button) + +local FLOPPY_SLOT = 2 + +-- --- Constants for this specific script --- +local BUTTON_PRESS_DURATION = common_autoboot.DEFAULT_BUTTON_PRESS_DURATION +local CASSETTE_MOTOR_OFF_DELAY_FRAMES = common_autoboot.DEFAULT_CASSETTE_MOTOR_OFF_DELAY_FRAMES + +local function boot_default(title_to_type) + return function() + common_autoboot.type_at_frame(frame_num, title_to_type .. "\n", 300) + end +end + +-- --- Main Script Logic --- + +-- Determine the currently loaded software name +local current_software_name = manager.machine.images:at(FLOPPY_SLOT).filename + +-- Map software names to their respective boot functions +local boot_sequences = { + alice = boot_default("ALICE.COM"), + chuckie = boot_default("CHUCKIE.COM"), + pakman = boot_default("PAKMAN.COM"), +} + +-- MAME machine frame notification callback +local function process_frame() + frame_num = frame_num + 1 + + -- Initial setup/info display at frame 1 + if frame_num == 1 then + emu.print_info("System Driver: " .. emu.romname()) + emu.print_info("Loaded Software Name (from image filename): " .. current_software_name) + + -- Determine which profile will be used + if boot_sequences[current_software_name] then + emu.print_info("--- Booting using game specific profile: " .. current_software_name .. " ---") + else + emu.print_info("--- Booting using default profile ---") + end + end + + --- DEBUG: Print current frame number every 100 frames --- + common_autoboot.debug_frame_num(frame_num) + + -- Execute the relevant boot sequence based on the loaded software + local boot_function = boot_sequences[current_software_name] + + if not boot_function then -- If specific function not found + boot_function = boot_sequences["default"] -- Assign the default function + end + + if boot_function then + boot_function(cassette_handler) + end +end + +-- Subscribe to machine frame notifications +subscription = emu.add_machine_frame_notifier(process_frame) \ No newline at end of file diff --git a/package/batocera/core/batocera-configgen/data/mame/autoboot_scripts/electron_cass.lua b/package/batocera/core/batocera-configgen/data/mame/autoboot_scripts/electron_cass.lua new file mode 100644 index 00000000000..645d88c0f01 --- /dev/null +++ b/package/batocera/core/batocera-configgen/data/mame/autoboot_scripts/electron_cass.lua @@ -0,0 +1,79 @@ +-- electron_cass.lua + +-- Load common autoboot functions +local common_autoboot = dofile("/usr/share/batocera/configgen/data/mame/autoboot_scripts/autoboot_common.lua") + +-- Script-wide variables +local button = {} +local frame_num = 0 + +common_autoboot.populate_buttons(button) + +-- Print the button table contents for debugging +-- common_autoboot.print_buttons(button) + +-- --- Constants for this specific script --- +local BUTTON_PRESS_DURATION = common_autoboot.DEFAULT_BUTTON_PRESS_DURATION +local CASSETTE_MOTOR_OFF_DELAY_FRAMES = common_autoboot.DEFAULT_CASSETTE_MOTOR_OFF_DELAY_FRAMES + +local function boot_default(cassette_handler) + common_autoboot.type_at_frame(frame_num, "*TAPE\n", 50, BUTTON_PRESS_DURATION) + common_autoboot.type_at_frame(frame_num, "CHAIN\"\"\n", 100, BUTTON_PRESS_DURATION) + common_autoboot.play_cassette_at_frame(frame_num, ":cassette", 300) + + local cassette_load_done = cassette_handler(frame_num, CASSETTE_MOTOR_OFF_DELAY_FRAMES) + + if cassette_load_done then + -- emu.keypost('RUN\n') + end +end + +-- --- Main Script Logic --- + +-- Determine the currently loaded software name +local current_software_name = manager.machine.images:at(1).filename + +-- Map software names to their respective boot functions +local boot_sequences = { + ["default"] = boot_default, +} + +-- --- Cassette Loading Handler Setup --- +local cassette_handler = common_autoboot.create_cassette_handler(":cassette") + +-- MAME machine frame notification callback +local function process_frame() + frame_num = frame_num + 1 + + -- Initial setup/info display at frame 1 + if frame_num == 1 then + emu.print_info("System Driver: " .. emu.romname()) + emu.print_info("Loaded Software Name (from image filename): " .. current_software_name) + + -- Determine which profile will be used + if boot_sequences[current_software_name] then + emu.print_info("--- Booting using game specific profile: " .. current_software_name .. " ---") + else + emu.print_info("--- Booting using default profile ---") + end + end + + -- --- Print current frame number every 100 frames --- + if frame_num % 100 == 0 then -- The modulo operator (%) gives the remainder of a division + emu.print_info("Current Frame: " .. frame_num) + end + + -- Execute the relevant boot sequence based on the loaded software + local boot_function = boot_sequences[current_software_name] + + if not boot_function then -- If specific function not found + boot_function = boot_sequences["default"] -- Assign the default function + end + + if boot_function then + boot_function(cassette_handler) + end +end + +-- Subscribe to machine frame notifications +subscription = emu.add_machine_frame_notifier(process_frame) \ No newline at end of file diff --git a/package/batocera/core/batocera-configgen/data/mame/autoboot_scripts/electron_flop.lua b/package/batocera/core/batocera-configgen/data/mame/autoboot_scripts/electron_flop.lua new file mode 100644 index 00000000000..cdc48a6b7a7 --- /dev/null +++ b/package/batocera/core/batocera-configgen/data/mame/autoboot_scripts/electron_flop.lua @@ -0,0 +1,79 @@ +-- electron_flop.lua + +-- Load common autoboot functions +local common_autoboot = dofile("/usr/share/batocera/configgen/data/mame/autoboot_scripts/autoboot_common.lua") + +-- Script-wide variables +local button = {} +local frame_num = 0 + +common_autoboot.populate_buttons(button) + +-- Print the button table contents for debugging +-- common_autoboot.print_buttons(button) + +-- --- Constants for this specific script --- +local BUTTON_PRESS_DURATION = common_autoboot.DEFAULT_BUTTON_PRESS_DURATION +local CASSETTE_MOTOR_OFF_DELAY_FRAMES = common_autoboot.DEFAULT_CASSETTE_MOTOR_OFF_DELAY_FRAMES + +local function boot_default(cassette_handler) + common_autoboot.type_at_frame(frame_num, "*CAT\n", 50, BUTTON_PRESS_DURATION) + common_autoboot.type_at_frame(frame_num, "*EXEC !BOOT\n", 250, BUTTON_PRESS_DURATION) + common_autoboot.play_cassette_at_frame(frame_num, ":cassette", 300) + + local cassette_load_done = cassette_handler(frame_num, CASSETTE_MOTOR_OFF_DELAY_FRAMES) + + if cassette_load_done then + -- emu.keypost('RUN\n') + end +end + +-- --- Main Script Logic --- + +-- Determine the currently loaded software name +local current_software_name = manager.machine.images:at(1).filename + +-- Map software names to their respective boot functions +local boot_sequences = { + ["default"] = boot_default, +} + +-- --- Cassette Loading Handler Setup --- +local cassette_handler = common_autoboot.create_cassette_handler(":cassette") + +-- MAME machine frame notification callback +local function process_frame() + frame_num = frame_num + 1 + + -- Initial setup/info display at frame 1 + if frame_num == 1 then + emu.print_info("System Driver: " .. emu.romname()) + emu.print_info("Loaded Software Name (from image filename): " .. current_software_name) + + -- Determine which profile will be used + if boot_sequences[current_software_name] then + emu.print_info("--- Booting using game specific profile: " .. current_software_name .. " ---") + else + emu.print_info("--- Booting using default profile ---") + end + end + + -- --- Print current frame number every 100 frames --- + if frame_num % 100 == 0 then -- The modulo operator (%) gives the remainder of a division + emu.print_info("Current Frame: " .. frame_num) + end + + -- Execute the relevant boot sequence based on the loaded software + local boot_function = boot_sequences[current_software_name] + + if not boot_function then -- If specific function not found + boot_function = boot_sequences["default"] -- Assign the default function + end + + if boot_function then + boot_function(cassette_handler) + end +end + +-- Subscribe to machine frame notifications +subscription = emu.add_machine_frame_notifier(process_frame) \ No newline at end of file diff --git a/package/batocera/core/batocera-configgen/data/mame/autoboot_scripts/famicom_cass.lua b/package/batocera/core/batocera-configgen/data/mame/autoboot_scripts/famicom_cass.lua new file mode 100644 index 00000000000..a9fab2a4d19 --- /dev/null +++ b/package/batocera/core/batocera-configgen/data/mame/autoboot_scripts/famicom_cass.lua @@ -0,0 +1,97 @@ +--local button = {} +--print("=== Ports ===") +--for i, j in pairs(manager.machine.ioport.ports) do +-- for field_name, field in pairs(j.fields) do +-- id = field.port.tag .. ',' .. field.mask .. ',' .. field.type +-- print(field_name,": ", id) +-- button[id] = field +-- end +--end + +print("") +print("=== Cassettes ===") +for i, j in pairs(manager.machine.cassettes) do + print(i) +end + +print("") +print("=== Images ===") +for i, j in pairs(manager.machine.images) do + print(i) +end + +part = {} +table.insert(part, os.getenv("RANDOMAME_PART_1")) +table.insert(part, os.getenv("RANDOMAME_PART_2")) +--table.insert(part, os.getenv("RANDOMAME_PART_3")) + +feature = {} +table.insert(feature, os.getenv("RANDOMAME_PART_FEATURE_1")) +table.insert(feature, os.getenv("RANDOMAME_PART_FEATURE_2")) +--table.insert(feature, os.getenv("RANDOMAME_PART_FEATURE_3")) + +commands = {} + +for i,p in ipairs(part) do + my_feature = table.remove(feature,1) + if my_feature == nil then + table.insert(commands, "LOAD") + else + if my_feature == 'part_id:BG Data' then + table.insert(commands, "LOADS") + else + table.insert(commands, "LOAD") + end + end +end + +local frame_num = 0 +local current_command = nil +local run_end_frame = 0 + +local function process_frame() + frame_num = frame_num + 1 + + if frame_num > run_end_frame + 100 then + if current_command == nil then + current_command = table.remove(commands,1) + if current_command == "LOADS" then + manager.machine.images[":exp:fc_keyboard:tape"]:load_software(table.remove(part,1)) + emu.keypost("LOADS\n") + manager.machine.cassettes[":exp:fc_keyboard:tape"]:play() + elseif current_command == "LOAD" then + manager.machine.images[":exp:fc_keyboard:tape"]:load_software(table.remove(part,1)) + emu.keypost("LOAD\n") + manager.machine.cassettes[":exp:fc_keyboard:tape"]:play() + end + end + end + + if manager.machine.cassettes[":exp:fc_keyboard:tape"].is_stopped == false and manager.machine.cassettes[":exp:fc_keyboard:tape"].motor_state == true then + manager.machine.video.throttled = false + manager.machine.video.frameskip = 12 + else + manager.machine.video.throttled = true + manager.machine.video.frameskip = 0 + + if current_command ~= nil then + if current_command == "LOADS" then + current_command = nil + elseif current_command == "LOAD" then + current_command = nil + -- if next command is a "LOAD", issue a "RUN" and wait for its execution + if commands[1] == "LOAD" then + emu.keypost("RUN\n") + run_end_frame = frame_num + 800 + end + end + + -- if last command, issue a "RUN" + if commands[1] == nil then + emu.keypost("RUN\n") + end + end + end +end + +subscription=emu.add_machine_frame_notifier(process_frame) diff --git a/package/batocera/core/batocera-configgen/data/mame/autoboot_scripts/fm7_cass.lua b/package/batocera/core/batocera-configgen/data/mame/autoboot_scripts/fm7_cass.lua new file mode 100644 index 00000000000..22792d7c740 --- /dev/null +++ b/package/batocera/core/batocera-configgen/data/mame/autoboot_scripts/fm7_cass.lua @@ -0,0 +1,40 @@ +local frame_num = 0 +local load_done_frame_num = 0 +local motor_state_false_frame_num = 0 +local function process_frame() + frame_num = frame_num + 1 + + if frame_num == 1 then + emu.keypost('LOAD\n') + manager.machine.cassettes[":cassette"]:play() + end + + if frame_num > 150 then + if motor_state_false_frame_num == 0 then + if manager.machine.cassettes[":cassette"].motor_state == false then + motor_state_false_frame_num = frame_num + end + else + if frame_num > motor_state_false_frame_num + 60 then + if load_done_frame_num == 0 then + load_done_frame_num = frame_num + emu.keypost('RUN\n') + end + end + end + end + + if manager.machine.cassettes[":cassette"].motor_state == true then + motor_state_false_frame_num = 0 + end + + if manager.machine.cassettes[":cassette"].is_stopped == false and manager.machine.cassettes[":cassette"].motor_state == true then + manager.machine.video.throttled = false + manager.machine.video.frameskip = 12 + else + manager.machine.video.throttled = true + manager.machine.video.frameskip = 0 + end +end + +subscription=emu.add_machine_frame_notifier(process_frame) diff --git a/package/batocera/core/batocera-configgen/data/mame/autoboot_scripts/galaxy.lua b/package/batocera/core/batocera-configgen/data/mame/autoboot_scripts/galaxy.lua new file mode 100644 index 00000000000..74911ccf35f --- /dev/null +++ b/package/batocera/core/batocera-configgen/data/mame/autoboot_scripts/galaxy.lua @@ -0,0 +1,78 @@ +-- galaxy.lua + +-- Load common autoboot functions +local common_autoboot = dofile("/usr/share/batocera/configgen/data/mame/autoboot_scripts/autoboot_common.lua") + +-- Script-wide variables +local button = {} +local frame_num = 0 + +-- Print Info +common_autoboot.print_image_info() + +-- --- Cassette Loading Handler Setup --- +local CASSETTE_SLOT = 2 +local CASSETTE_DEVICE_TAG = manager.machine.images:at(CASSETTE_SLOT).device.tag +local cassette_handler = common_autoboot.create_cassette_handler(CASSETTE_DEVICE_TAG) + +common_autoboot.populate_buttons(button) + +-- --- Constants for this specific script --- +local BUTTON_PRESS_DURATION = common_autoboot.DEFAULT_BUTTON_PRESS_DURATION +local CASSETTE_MOTOR_OFF_DELAY_FRAMES = common_autoboot.DEFAULT_CASSETTE_MOTOR_OFF_DELAY_FRAMES + +local function boot_default(cassette_handler) + common_autoboot.type_at_frame(frame_num, "OLD\n", 100) + common_autoboot.play_cassette_at_frame(frame_num, CASSETTE_DEVICE_TAG, 200) + + local cassette_load_done = cassette_handler(frame_num, CASSETTE_MOTOR_OFF_DELAY_FRAMES) + + if cassette_load_done then + emu.keypost('RUN\n') + end +end + +-- --- Main Script Logic --- + +-- Determine the currently loaded software name +local current_software_name = manager.machine.images:at(CASSETTE_SLOT).filename + +-- Map software names to their respective boot functions +local boot_sequences = { + ['default'] = boot_default, +} + +-- MAME machine frame notification callback +local function process_frame() + frame_num = frame_num + 1 + + -- Initial setup/info display at frame 1 + if frame_num == 1 then + emu.print_info("System Driver: " .. emu.romname()) + emu.print_info("Loaded Software Name (from image filename): " .. current_software_name) + + -- Determine which profile will be used + if boot_sequences[current_software_name] then + emu.print_info("--- Booting using game specific profile: " .. current_software_name .. " ---") + else + emu.print_info("--- Booting using default profile ---") + end + end + + --- DEBUG: Print current frame number every 100 frames --- + common_autoboot.debug_frame_num(frame_num) + + -- Execute the relevant boot sequence based on the loaded software + local boot_function = boot_sequences[current_software_name] + + if not boot_function then -- If specific function not found + boot_function = boot_sequences["default"] -- Assign the default function + end + + if boot_function then + boot_function(cassette_handler) + end +end + +-- Subscribe to machine frame notifications +subscription = emu.add_machine_frame_notifier(process_frame) \ No newline at end of file diff --git a/package/batocera/core/batocera-configgen/data/mame/autoboot_scripts/io_base.lua b/package/batocera/core/batocera-configgen/data/mame/autoboot_scripts/io_base.lua new file mode 100644 index 00000000000..a9c76e08350 --- /dev/null +++ b/package/batocera/core/batocera-configgen/data/mame/autoboot_scripts/io_base.lua @@ -0,0 +1,19 @@ +local io_base = {} + +function io_base.get() + button = {} + for i, j in pairs(manager.machine.ioport.ports) do + for field_name, field in pairs(j.fields) do + --print(field_name) + --print(" tag", field.port.tag) + --print(" mask", field.mask) + --print(" type", field.type) + id = field.port.tag .. ',' .. field.mask .. ',' .. field.type + print(field_name,'=',id) + button[id] = field + end + end + return button +end + +return io_base diff --git a/package/batocera/core/batocera-configgen/data/mame/autoboot_scripts/juku.lua b/package/batocera/core/batocera-configgen/data/mame/autoboot_scripts/juku.lua new file mode 100644 index 00000000000..508adf290fc --- /dev/null +++ b/package/batocera/core/batocera-configgen/data/mame/autoboot_scripts/juku.lua @@ -0,0 +1,53 @@ +button = {} + +for i, j in pairs(manager.machine.ioport.ports) do + for field_name, field in pairs(j.fields) do + id = field.port.tag .. ',' .. field.mask .. ',' .. field.type + print(field_name,": ", id) + button[id] = field + end +end + +local frame_num = 0 +local function process_frame() + frame_num = frame_num + 1 + + if frame_num == 200 then + button[":COL.4,8,49"]:set_value(1) -- T + elseif frame_num == 205 then + button[":COL.4,8,49"]:set_value(0) -- T + + elseif frame_num == 250 then + button[":COL.6,32,49"]:set_value(1) -- D + elseif frame_num == 255 then + button[":COL.6,32,49"]:set_value(0) -- D + + elseif frame_num == 300 then + button[":COL.6,32,49"]:set_value(1) -- D + elseif frame_num == 305 then + button[":COL.6,32,49"]:set_value(0) -- D + + elseif frame_num == 600 then + button[":COL.6,32,49"]:set_value(1) -- D + elseif frame_num == 605 then + button[":COL.6,32,49"]:set_value(0) -- D + + elseif frame_num == 610 then + button[":COL.14,8,49"]:set_value(1) -- I + elseif frame_num == 615 then + button[":COL.14,8,49"]:set_value(0) -- I + + elseif frame_num == 620 then + button[":COL.2,8,49"]:set_value(1) -- R + elseif frame_num == 625 then + button[":COL.2,8,49"]:set_value(0) -- R + + elseif frame_num == 630 then + button[":COL.8,32,49"]:set_value(1) -- RETURN + elseif frame_num == 635 then + button[":COL.8,32,49"]:set_value(0) -- RETURN + + end +end + +subscription=emu.add_machine_frame_notifier(process_frame) diff --git a/package/batocera/core/batocera-configgen/data/mame/autoboot_scripts/jupace_cass.lua b/package/batocera/core/batocera-configgen/data/mame/autoboot_scripts/jupace_cass.lua new file mode 100644 index 00000000000..660d4530e7c --- /dev/null +++ b/package/batocera/core/batocera-configgen/data/mame/autoboot_scripts/jupace_cass.lua @@ -0,0 +1,173 @@ +-- jupace_cass.lua + +-- Load common autoboot functions +local common_autoboot = dofile("/usr/share/batocera/configgen/data/mame/autoboot_scripts/autoboot_common.lua") + +-- Script-wide variables +local button = {} +local frame_num = 0 + +-- --- Map all buttons --- +common_autoboot.populate_buttons(button) + +-- --- Print Info --- +common_autoboot.print_image_info() +-- common_autoboot.print_buttons(button) + +-- --- Cassette Loading Handler Setup --- +local CASSETTE_SLOT = 1 +local CASSETTE_DEVICE_TAG = manager.machine.images:at(CASSETTE_SLOT).device.tag +local cassette_handler = common_autoboot.create_cassette_handler(CASSETTE_DEVICE_TAG) + +-- --- Constants for this specific script --- +local BUTTON_PRESS_DURATION = common_autoboot.DEFAULT_BUTTON_PRESS_DURATION +local CASSETTE_MOTOR_OFF_DELAY_FRAMES = common_autoboot.DEFAULT_CASSETTE_MOTOR_OFF_DELAY_FRAMES + +-- local function boot_default(title_to_type) +-- return function() +-- common_autoboot.type_at_frame(frame_num, title_to_type .. "\n", 100) +-- end +-- end + +local function boot_default(title_to_type) + return function(cassette_handler_arg) + common_autoboot.type_at_frame(frame_num, title_to_type .. "\n", 100) + + common_autoboot.play_cassette_at_frame(frame_num, CASSETTE_DEVICE_TAG, 300) + local cassette_load_done = cassette_handler_arg(frame_num, CASSETTE_MOTOR_OFF_DELAY_FRAMES) + + if cassette_load_done then + -- emu.keypost('/\n') + end + end +end + +-- --- Main Script Logic --- + +-- Determine the currently loaded software name +local current_software_name = manager.machine.images:at(CASSETTE_SLOT).filename + +-- Map software names to their respective boot functions +local boot_sequences = { + ['3dmaze'] = boot_default("load 3dmaze 3dmaze"), + ['avoider'] = boot_default("load avoider run"), + ['acecase'] = boot_default("load case"), + ['demotape'] = boot_default("load banner"), + ['graphics'] = boot_default("load ?plot"), + ['graphicsa'] = boot_default("load ?plot"), + ['aceinvad'] = boot_default("load graphics gr load run run"), + ['invader1'] = boot_default("load GR load GR load GR load GM load GM load GM z p"), + ['acemines'] = boot_default("load mines mines"), + ['acestar'] = boot_default("0 0 bload astar 16384 call"), + ['acevocab'] = boot_default("load listv"), + ['acevader'] = boot_default("load gr load acevaders gr r"), + ['adventa'] = boot_default("0 0 bload adva 16384 call"), + ['aliendef'] = boot_default("load defender g"), + ['aswarm'] = boot_default("load gr load swarm gr go"), + ['amazmaze'] = boot_default("load maze g"), + ['amisound'] = boot_default("load amisndkit go"), + ['asmdasm'] = boot_default("load a1"), + ['astrian'] = boot_default("load Lander run"), + ['aticraid'] = boot_default("load atic go"), + ['blackisl'] = boot_default("load black island"), + ['bomber'] = boot_default("load bomber play"), + ['breakout'] = boot_default("load breakout b"), + ['calendar'] = boot_default("load calendar"), + ['callisto'] = boot_default("8930 0 bload callisto"), + ['cavern'] = boot_default("load graphics load run gr run"), + ['cenipede'] = boot_default("load centipede go"), + ['m_champs'] = boot_default("0 0 bload mines 11272 64 bload g load m 7 jeu"), + ['chase'] = boot_default("load chase chase"), + ['chess'] = boot_default("load chess run"), + ['schess2'] = boot_default("load chess run"), + ['cygnus'] = boot_default("load cygnus g"), + ['database'] = boot_default("load database"), + ['dodge'] = boot_default("load Dodger game"), + ['dotman'] = boot_default("load s load run s run"), + ['ducksht'] = boot_default("11272 0 bload dchars load duckshoot d"), + ['firebird'] = boot_default("load FIREBIRD run"), + ['frogger'] = boot_default("load frogger go"), + ['frogger1'] = boot_default("load frogger g"), + ['fungle'] = boot_default("load FU run"), + ['gobbgook'] = boot_default("load gobblegook gobblegook"), + ['grandprx'] = boot_default("load grandprix g"), + ['grexfrog'] = boot_default("load graphics load run grf run"), + ['guessing'] = boot_default("load game game"), + ['hdrmon'] = boot_default("load HDRMON go"), + ['invaders'] = boot_default("load invaders g"), + ['jumpman'] = boot_default("load jumpman start"), + ['kkong'] = boot_default("load CHARACTERS CHARACTERS load KONG run"), + ['life'] = boot_default("load life13 life"), + ['lightrac'] = boot_default("load LightRacer run"), + ['loader'] = boot_default("load loader"), + ['llander'] = boot_default("load ll ll"), + ['memstars'] = boot_default("load run run"), + ['meteor'] = boot_default("load gr meteor load meteor game"), + ['metracer'] = boot_default("load GR load GR load GR load GM load GM z p"), + ['meteors'] = boot_default("load asteroids g"), + ['micromaz'] = boot_default("load graphics load run s run"), + ['minefld'] = boot_default("11272 0 bload mchars load minefield m"), + ['moonbugg'] = boot_default("load moonbuggy g"), + ['morsecod'] = boot_default("load morse_tuto go"), + ['othello'] = boot_default("load othello 11272 0 bload pieces go"), + ['owler'] = boot_default("load owler loading"), + ['quickdrw'] = boot_default("load quick-draw go"), + ['quickdrp'] = boot_default("load Qdraw run"), + ['robohnch'] = boot_default("load robohench g"), + ['robohunt'] = boot_default("load robohunt g"), + ['sailhorn'] = boot_default("load hornpipe hornpipe"), + ['sambombs'] = boot_default("load sam g"), + ['scramblr'] = boot_default("load scrambler"), + ['sokoace'] = boot_default("load sokoace play"), + ['sacemap'] = boot_default("load sokoed ed"), + ['sbattle'] = boot_default("load graphics load run gr run"), + ['spreadsh'] = boot_default("load s"), + ['startrek'] = boot_default("load startrek g"), + ['sudoku'] = boot_default("load sudoku sudoku"), + ['surround'] = boot_default("load surround go"), + ['tankbatl'] = boot_default("load tankbattle g"), + ['teach'] = boot_default("load forth"), + ['tetris'] = boot_default("load tetris tetris"), + ['turbo'] = boot_default("load turbo 0 0 bload track turbo"), + ['hanoi'] = boot_default("load hanoi hanoi"), + ['words'] = boot_default("load words"), + ['wrd'] = boot_default("load wrd"), + ['zapem'] = boot_default("11272 0 bload zchars load zapem z"), + ['zombies'] = boot_default("load zombpot zombpot"), + ['zxprint'] = boot_default("load zx-printer"), +} + +-- MAME machine frame notification callback +local function process_frame() + frame_num = frame_num + 1 + + -- Initial setup/info display at frame 1 + if frame_num == 1 then + emu.print_info("System Driver: " .. emu.romname()) + emu.print_info("Loaded Software Name (from image filename): " .. current_software_name) + + -- Determine which profile will be used + if boot_sequences[current_software_name] then + emu.print_info("--- Booting using game specific profile: " .. current_software_name .. " ---") + else + emu.print_info("--- Booting using default profile ---") + end + end + + --- DEBUG: Print current frame number every 100 frames --- + common_autoboot.debug_frame_num(frame_num) + + -- Execute the relevant boot sequence based on the loaded software + local boot_function = boot_sequences[current_software_name] + + if not boot_function then -- If specific function not found + boot_function = boot_sequences["default"] -- Assign the default function + end + + if boot_function then + boot_function(cassette_handler) + end +end + +-- Subscribe to machine frame notifications +subscription = emu.add_machine_frame_notifier(process_frame) \ No newline at end of file diff --git a/package/batocera/core/batocera-configgen/data/mame/autoboot_scripts/jupace_snap.lua b/package/batocera/core/batocera-configgen/data/mame/autoboot_scripts/jupace_snap.lua new file mode 100644 index 00000000000..188ff6e91ff --- /dev/null +++ b/package/batocera/core/batocera-configgen/data/mame/autoboot_scripts/jupace_snap.lua @@ -0,0 +1,80 @@ +-- jupace_snap.lua + +-- Load common autoboot functions +local common_autoboot = dofile("/usr/share/batocera/configgen/data/mame/autoboot_scripts/autoboot_common.lua") + +-- Script-wide variables +local button = {} +local frame_num = 0 + +-- --- Map all buttons --- +common_autoboot.populate_buttons(button) + +-- --- Print Info --- +common_autoboot.print_image_info() +-- common_autoboot.print_buttons(button) + +-- --- Cassette Loading Handler Setup --- +local SOFTWARE_SLOT = 3 + +-- --- Constants for this specific script --- +local BUTTON_PRESS_DURATION = common_autoboot.DEFAULT_BUTTON_PRESS_DURATION + +local function boot_default() + common_autoboot.type_at_frame(frame_num, "\n", 200) +end + +local function boot_milliped() + common_autoboot.type_at_frame(frame_num, "milliped\n", 200) +end + +local function boot_pacman() + common_autoboot.type_at_frame(frame_num, "run\n", 200) +end + +-- --- Main Script Logic --- + +-- Determine the currently loaded software name +local current_software_name = manager.machine.images:at(SOFTWARE_SLOT).filename + +-- Map software names to their respective boot functions +local boot_sequences = { + ['default'] = boot_default, + milliped = boot_milliped, + pacman = boot_pacman, +} + +-- MAME machine frame notification callback +local function process_frame() + frame_num = frame_num + 1 + + -- Initial setup/info display at frame 1 + if frame_num == 1 then + emu.print_info("System Driver: " .. emu.romname()) + emu.print_info("Loaded Software Name (from image filename): " .. current_software_name) + + -- Determine which profile will be used + if boot_sequences[current_software_name] then + emu.print_info("--- Booting using game specific profile: " .. current_software_name .. " ---") + else + emu.print_info("--- Booting using default profile ---") + end + end + + --- DEBUG: Print current frame number every 100 frames --- + common_autoboot.debug_frame_num(frame_num) + + -- Execute the relevant boot sequence based on the loaded software + local boot_function = boot_sequences[current_software_name] + + if not boot_function then -- If specific function not found + boot_function = boot_sequences["default"] -- Assign the default function + end + + if boot_function then + boot_function() + end +end + +-- Subscribe to machine frame notifications +subscription = emu.add_machine_frame_notifier(process_frame) \ No newline at end of file diff --git a/package/batocera/core/batocera-configgen/data/mame/autoboot_scripts/m5_cass.lua b/package/batocera/core/batocera-configgen/data/mame/autoboot_scripts/m5_cass.lua new file mode 100644 index 00000000000..59941f420fc --- /dev/null +++ b/package/batocera/core/batocera-configgen/data/mame/autoboot_scripts/m5_cass.lua @@ -0,0 +1,76 @@ +-- m5_cass.lua + +-- Load common autoboot functions +local common_autoboot = dofile("/usr/share/batocera/configgen/data/mame/autoboot_scripts/autoboot_common.lua") + +-- Script-wide variables +local button = {} +local frame_num = 0 + +-- --- Cassette Loading Handler Setup --- +local CASSETTE_DEVICE_TAG = manager.machine.images:at(2).device.tag +local cassette_handler = common_autoboot.create_cassette_handler(CASSETTE_DEVICE_TAG) + +common_autoboot.populate_buttons(button) + +common_autoboot.print_image_info() + +-- --- Constants for this specific script --- +local BUTTON_PRESS_DURATION = common_autoboot.DEFAULT_BUTTON_PRESS_DURATION +local CASSETTE_MOTOR_OFF_DELAY_FRAMES = common_autoboot.DEFAULT_CASSETTE_MOTOR_OFF_DELAY_FRAMES + +local function boot_default(cassette_handler) + common_autoboot.type_at_frame(frame_num, "CHAIN\n", 100) + common_autoboot.play_cassette_at_frame(frame_num, CASSETTE_DEVICE_TAG, 200) + + local cassette_load_done = cassette_handler(frame_num, CASSETTE_MOTOR_OFF_DELAY_FRAMES) + + if cassette_load_done then + -- emu.keypost('RUN\n') + end +end + +-- --- Main Script Logic --- + +-- Determine the currently loaded software name +local current_software_name = manager.machine.images:at(1).filename + +-- Map software names to their respective boot functions +local boot_sequences = { + ['default'] = boot_default, +} + +-- MAME machine frame notification callback +local function process_frame() + frame_num = frame_num + 1 + + -- Initial setup/info display at frame 1 + if frame_num == 1 then + emu.print_info("System Driver: " .. emu.romname()) + emu.print_info("Loaded Software Name (from image filename): " .. current_software_name) + + -- Determine which profile will be used + if boot_sequences[current_software_name] then + emu.print_info("--- Booting using game specific profile: " .. current_software_name .. " ---") + else + emu.print_info("--- Booting using default profile ---") + end + end + + --- DEBUG: Print current frame number every 100 frames --- + common_autoboot.debug_frame_num(frame_num) + + -- Execute the relevant boot sequence based on the loaded software + local boot_function = boot_sequences[current_software_name] + + if not boot_function then -- If specific function not found + boot_function = boot_sequences["default"] -- Assign the default function + end + + if boot_function then + boot_function(cassette_handler) + end +end + +-- Subscribe to machine frame notifications +subscription = emu.add_machine_frame_notifier(process_frame) \ No newline at end of file diff --git a/package/batocera/core/batocera-configgen/data/mame/autoboot_scripts/mc10_cass.lua b/package/batocera/core/batocera-configgen/data/mame/autoboot_scripts/mc10_cass.lua new file mode 100644 index 00000000000..4728f1c9c28 --- /dev/null +++ b/package/batocera/core/batocera-configgen/data/mame/autoboot_scripts/mc10_cass.lua @@ -0,0 +1,231 @@ +-- mc10_cass.lua +-- +-- Dedicated MC-10 cassette autoboot. This follows XRoar's basic autorun +-- heuristic more closely than trs80_cass.lua: +-- type 0 (BASIC) -> CLOAD, then RUN after tape stops +-- type 2 (machine) -> CLOADM:EXEC only when load >= $01A9, else CLOADM +-- +-- MC-10 tapes don't have the same remote motor-control behaviour as CoCo/TRS-80 +-- setups, so load completion is tracked from cassette play/stop state rather +-- than motor_state. + +local common_autoboot = dofile("/usr/share/batocera/configgen/data/mame/autoboot_scripts/autoboot_common.lua") +local zip_util = dofile("/usr/share/batocera/configgen/data/mame/autoboot_scripts/autoboot_zip.lua") + +local CAS_SCAN_BYTES = 4096 + +local TAPE_TYPE_BASIC = 0 +local TAPE_TYPE_DATA = 1 +local TAPE_TYPE_MACHINE = 2 + +local BUTTON_PRESS_DURATION = common_autoboot.DEFAULT_BUTTON_PRESS_DURATION +local CASSETTE_STOP_DELAY_FRAMES = common_autoboot.DEFAULT_CASSETTE_MOTOR_OFF_DELAY_FRAMES + +local button = {} +local frame_num = 0 + +common_autoboot.populate_buttons(button) + +local function be16(data, pos) + local hi = data:byte(pos) or 0 + local lo = data:byte(pos + 1) or 0 + return (hi << 8) | lo +end + +local function find_cassette_image() + for _, cass in pairs(manager.machine.cassettes) do + if cass.exists and cass.filename and #cass.filename > 0 then + return cass.filename, cass.device and cass.device.tag or ":cassette" + end + end + + for _, img in pairs(manager.machine.images) do + if img.exists and img.filename and #img.filename > 0 then + local fn = img.filename:lower() + if fn:find("%.cas$") or fn:find("%.c10$") then + return img.filename, img.device and img.device.tag or ":cassette" + end + end + end + + return nil, ":cassette" +end + +local function parse_tape_header(data) + if not data or #data < 20 then + return nil + end + + local i = 1 + local saw_leader = false + + while i <= #data do + local b = data:byte(i) + + if b == 0x55 then + saw_leader = true + i = i + 1 + elseif b == 0x3C and saw_leader then + local base = i + 1 + if base + 16 > #data then + return nil + end + + local block_type = data:byte(base) + local block_size = data:byte(base + 1) + + if block_type == 0x00 and block_size >= 15 then + local dstart = base + 2 + local name = {} + for j = 0, 7 do + local c = data:byte(dstart + j) + if c and c ~= 0x00 and c ~= 0x20 then + table.insert(name, string.char(c)) + end + end + + local file_type = data:byte(dstart + 8) + local start_address = be16(data, dstart + 11) + local load_address = be16(data, dstart + 13) + + print(string.format( + "[MC-10 Autoboot] Header name='%s' type=%d load=0x%04X exec=0x%04X", + table.concat(name), file_type or -1, load_address, start_address)) + + return { + file_type = file_type, + load_address = load_address, + start_address = start_address, + } + end + + saw_leader = false + i = i + 1 + else + saw_leader = false + i = i + 1 + end + end + + return nil +end + +local function detect_tape_info() + local path = find_cassette_image() + if not path then + print("[MC-10 Autoboot] No cassette image found.") + return { file_type = TAPE_TYPE_BASIC, load_address = 0 } + end + + print("[MC-10 Autoboot] Tape path: " .. path) + local data = zip_util.read_bytes(path, CAS_SCAN_BYTES) + if not data then + print("[MC-10 Autoboot] Could not read tape header, defaulting to BASIC.") + return { file_type = TAPE_TYPE_BASIC, load_address = 0 } + end + + local info = parse_tape_header(data) + if info then + return info + end + + print("[MC-10 Autoboot] No valid header found, defaulting to BASIC.") + return { file_type = TAPE_TYPE_BASIC, load_address = 0 } +end + +local function create_mc10_cassette_handler(cassette_device_tag) + local stop_frame = 0 + local seen_playing = false + local load_done = false + + return function(current_frame_num, delay_frames_after_stop) + local cassette_device = manager.machine.cassettes[cassette_device_tag] + if not cassette_device then + print("[MC-10 Autoboot] Cassette device not found: " .. cassette_device_tag) + return false + end + + if cassette_device.is_stopped == false then + manager.machine.video.throttled = false + manager.machine.video.frameskip = 12 + seen_playing = true + else + manager.machine.video.throttled = true + manager.machine.video.frameskip = 0 + if seen_playing and stop_frame == 0 then + stop_frame = current_frame_num + print("[MC-10 Autoboot] Cassette stop detected at frame " .. current_frame_num) + end + end + + if not load_done and stop_frame ~= 0 and current_frame_num > stop_frame + delay_frames_after_stop then + load_done = true + print("[MC-10 Autoboot] Cassette load complete at frame " .. current_frame_num) + return true + end + + return false + end +end + +local function play_cassette(cassette_tag, start_frame) + if frame_num == start_frame then + local cassette_device = manager.machine.cassettes[cassette_tag] + if cassette_device then + cassette_device:play() + print("[MC-10 Autoboot] Playing cassette at frame " .. frame_num) + else + print("[MC-10 Autoboot] Cassette device not found for play: " .. cassette_tag) + end + end +end + +local tape_info = nil +local cassette_tag = ":cassette" +local cassette_handler = nil + +local function boot_basic() + common_autoboot.type_at_frame(frame_num, "CLOAD\n", 140, BUTTON_PRESS_DURATION) + play_cassette(cassette_tag, 260) + + if cassette_handler and cassette_handler(frame_num, CASSETTE_STOP_DELAY_FRAMES) then + emu.keypost("RUN\n") + end +end + +local function boot_machine() + local need_exec = (tape_info.load_address or 0) >= 0x01A9 + local cmd = need_exec and "CLOADM:EXEC\n" or "CLOADM\n" + + common_autoboot.type_at_frame(frame_num, cmd, 140, BUTTON_PRESS_DURATION) + play_cassette(cassette_tag, 260) + + if cassette_handler then + cassette_handler(frame_num, CASSETTE_STOP_DELAY_FRAMES) + end +end + +local function process_frame() + frame_num = frame_num + 1 + + if frame_num == 1 then + emu.print_info("System Driver: " .. emu.romname()) + common_autoboot.print_image_info() + local _, detected_tag = find_cassette_image() + cassette_tag = detected_tag or ":cassette" + cassette_handler = create_mc10_cassette_handler(cassette_tag) + tape_info = detect_tape_info() + end + + common_autoboot.debug_frame_num(frame_num) + + if not tape_info or tape_info.file_type == TAPE_TYPE_DATA then + boot_basic() + elseif tape_info.file_type == TAPE_TYPE_MACHINE then + boot_machine() + else + boot_basic() + end +end + +subscription = emu.add_machine_frame_notifier(process_frame) diff --git a/package/batocera/core/batocera-configgen/data/mame/autoboot_scripts/megacd.lua b/package/batocera/core/batocera-configgen/data/mame/autoboot_scripts/megacd.lua new file mode 100644 index 00000000000..fd5e06bb0a9 --- /dev/null +++ b/package/batocera/core/batocera-configgen/data/mame/autoboot_scripts/megacd.lua @@ -0,0 +1,32 @@ +button = {} + +for i, j in pairs(manager.machine.ioport.ports) do + for field_name, field in pairs(j.fields) do + id = field.port.tag .. ',' .. field.mask .. ',' .. field.type + print(field_name,": ", id) + button[id] = field + end +end + +frame_num=0 +function process_frame() + frame_num = frame_num + 1 + + if frame_num == 1 then + -- P1 start + button[":ctrl1:mdpad:PAD,128,46"]:set_value(1) + end + if frame_num == 40 then + button[":ctrl1:mdpad:PAD,128,46"]:set_value(0) + end + if frame_num == 80 then + -- P1 start + button[":ctrl1:mdpad:PAD,128,46"]:set_value(1) + end + if frame_num == 120 then + button[":ctrl1:mdpad:PAD,128,46"]:set_value(0) + end +end + +subscription = emu.add_machine_frame_notifier(process_frame) + diff --git a/package/batocera/core/batocera-configgen/data/mame/autoboot_scripts/mo5_cass.lua b/package/batocera/core/batocera-configgen/data/mame/autoboot_scripts/mo5_cass.lua new file mode 100644 index 00000000000..96191e81009 --- /dev/null +++ b/package/batocera/core/batocera-configgen/data/mame/autoboot_scripts/mo5_cass.lua @@ -0,0 +1,23 @@ +local function process_frame() + if manager.machine.cassettes[":cassette"].is_stopped == false and manager.machine.cassettes[":cassette"].motor_state == true then + manager.machine.video.throttled = false + manager.machine.video.frameskip = 12 + else + manager.machine.video.throttled = true + manager.machine.video.frameskip = 0 + end +end + +info_usage = os.getenv("RANDOMAME_INFO_USAGE") + +command = "" +if info_usage == "Load with LOADM" then + command = 'LOADM\n' +else + command = 'RUN"\n' +end + +emu.keypost(command) +manager.machine.cassettes[":cassette"]:play() + +subscription=emu.add_machine_frame_notifier(process_frame) diff --git a/package/batocera/core/batocera-configgen/data/mame/autoboot_scripts/mo6_cass.lua b/package/batocera/core/batocera-configgen/data/mame/autoboot_scripts/mo6_cass.lua new file mode 100644 index 00000000000..c552dda04fa --- /dev/null +++ b/package/batocera/core/batocera-configgen/data/mame/autoboot_scripts/mo6_cass.lua @@ -0,0 +1,76 @@ +-- mo6_cass.lua + +-- Load common autoboot functions +local common_autoboot = dofile("/usr/share/batocera/configgen/data/mame/autoboot_scripts/autoboot_common.lua") + +-- Script-wide variables +local button = {} +local frame_num = 0 + +common_autoboot.populate_buttons(button) + +-- --- Constants for this specific script --- +local BUTTON_PRESS_DURATION = common_autoboot.DEFAULT_BUTTON_PRESS_DURATION +local CASSETTE_MOTOR_OFF_DELAY_FRAMES = common_autoboot.DEFAULT_CASSETTE_MOTOR_OFF_DELAY_FRAMES + +local function boot_default(cassette_handler) + common_autoboot.type_at_frame(frame_num, "1\n", 100, BUTTON_PRESS_DURATION) + common_autoboot.type_at_frame(frame_num, "RUN\"CASS:\n", 200, BUTTON_PRESS_DURATION) + common_autoboot.play_cassette_at_frame(frame_num, ":cassette", 300) + + local cassette_load_done = cassette_handler(frame_num, CASSETTE_MOTOR_OFF_DELAY_FRAMES) + + if cassette_load_done then + -- emu.keypost('RUN\n') + end +end + +-- --- Main Script Logic --- + +-- Determine the currently loaded software name +local current_software_name = manager.machine.images:at(3).filename + +-- Map software names to their respective boot functions +local boot_sequences = { + ["default"] = boot_default, +} + +-- --- Cassette Loading Handler Setup --- +local cassette_handler = common_autoboot.create_cassette_handler(":cassette") + +-- MAME machine frame notification callback +local function process_frame() + frame_num = frame_num + 1 + + -- Initial setup/info display at frame 1 + if frame_num == 1 then + emu.print_info("System Driver: " .. emu.romname()) + emu.print_info("Loaded Software Name (from image filename): " .. current_software_name) + + -- Determine which profile will be used + if boot_sequences[current_software_name] then + emu.print_info("--- Booting using game specific profile: " .. current_software_name .. " ---") + else + emu.print_info("--- Booting using default profile ---") + end + end + + --- Print current frame number every 100 frames --- + if frame_num % 100 == 0 then -- The modulo operator (%) gives the remainder of a division + emu.print_info("Current Frame: " .. frame_num) + end + + -- Execute the relevant boot sequence based on the loaded software + local boot_function = boot_sequences[current_software_name] + + if not boot_function then -- If specific function not found + boot_function = boot_sequences["default"] -- Assign the default function + end + + if boot_function then + boot_function(cassette_handler) + end +end + +-- Subscribe to machine frame notifications +subscription = emu.add_machine_frame_notifier(process_frame) \ No newline at end of file diff --git a/package/batocera/core/batocera-configgen/data/mame/autoboot_scripts/msx1_cass.lua b/package/batocera/core/batocera-configgen/data/mame/autoboot_scripts/msx1_cass.lua new file mode 100644 index 00000000000..f9d132a56fa --- /dev/null +++ b/package/batocera/core/batocera-configgen/data/mame/autoboot_scripts/msx1_cass.lua @@ -0,0 +1,199 @@ +-- msx1_cass.lua +-- +-- MSX1 cassette autoboot with header-based file-type detection. +-- +-- MSX .cas block layout: +-- Offset 0-7 : sync header 1F A6 DE BA CC 13 7D 74 +-- Offset 8-17 : type indicator (10 identical bytes) +-- 0xD3 x10 → tokenised BASIC (CSAVE) → CLOAD, then RUN after motor off +-- 0xD0 x10 → binary / machine code (BSAVE) → BLOAD"CAS:",R +-- 0xEA x10 → ASCII BASIC (SAVE) → RUN"CAS:" +-- Offset 18-23 : filename (6 ASCII bytes, space-padded) +-- +-- Load commands (ref: http://www.msxblue.com/manual/runningcas_c.htm): +-- CSAVE files (0xD3) : CLOAD → RUN after motor off +-- BSAVE files (0xD0) : BLOAD"CAS:",R (auto-executes, no post-command) +-- SAVE files (0xEA) : LOAD"CAS:" → RUN after motor off +-- (RUN"CAS:" fails on MSX2 DISK-BASIC: it doesn't block for the motor signal) +-- +-- Reference: MSX2 Technical Handbook chapter 5a; blueMSX source (ducasp/blueMSX). + +local common_autoboot = dofile("/usr/share/batocera/configgen/data/mame/autoboot_scripts/autoboot_common.lua") +local zip_util = dofile("/usr/share/batocera/configgen/data/mame/autoboot_scripts/autoboot_zip.lua") + +local CAS_SCAN_BYTES = 4096 + +local TAPE_TYPE_BASIC = 0 +local TAPE_TYPE_MACHINE = 1 +local TAPE_TYPE_ASCII = 2 + +local MSX_CAS_SYNC = { 0x1F, 0xA6, 0xDE, 0xBA, 0xCC, 0x13, 0x7D, 0x74 } + +local BUTTON_PRESS_DURATION = common_autoboot.DEFAULT_BUTTON_PRESS_DURATION +local CASSETTE_MOTOR_OFF_DELAY_FRAMES = common_autoboot.DEFAULT_CASSETTE_MOTOR_OFF_DELAY_FRAMES + +local CASSETTE_DEVICE_TAG = ":cassette" + +local button = {} +local frame_num = 0 +common_autoboot.populate_buttons(button) + +local function find_cassette_path() + for _, cass in pairs(manager.machine.cassettes) do + if cass.exists and cass.filename and #cass.filename > 0 then + print("[MSX1 Cass Autoboot] Cassette device: " .. cass.filename) + return cass.filename + end + end + for _, img in pairs(manager.machine.images) do + if img.exists and img.filename and #img.filename > 0 then + local fn = img.filename:lower() + if fn:find("%.cas$") or fn:find("%.wav$") then + print("[MSX1 Cass Autoboot] Image device cassette: " .. img.filename) + return img.filename + end + end + end + return nil +end + +-- ── MSX .cas header parser ──────────────────────────────────────────────────── +-- +-- Scan for the 8-byte sync sequence, then inspect the 10 type bytes that follow. + +local function find_sync(data) + if not data or #data < 18 then return nil end + for i = 1, #data - 17 do + local ok = true + for j = 1, 8 do + if data:byte(i + j - 1) ~= MSX_CAS_SYNC[j] then + ok = false; break + end + end + if ok then return i end + end + return nil +end + +local function parse_cas_header(data) + local sync_pos = find_sync(data) + if not sync_pos then return nil end + + -- 10 type-indicator bytes follow the 8-byte sync + local type_base = sync_pos + 8 + if type_base + 9 > #data then return nil end + + local type_byte = data:byte(type_base) + local uniform = true + for i = 1, 9 do + if data:byte(type_base + i) ~= type_byte then + uniform = false; break + end + end + if not uniform then return nil end + + -- 6-byte filename follows the type indicator + local name_base = type_base + 10 + local filename = "" + if name_base + 5 <= #data then + local chars = {} + for i = 0, 5 do + local c = data:byte(name_base + i) + if c and c ~= 0x00 and c ~= 0x20 then + table.insert(chars, string.char(c)) + end + end + filename = table.concat(chars) + end + + if type_byte == 0xD3 then + print(string.format("[MSX1 Cass Autoboot] Header: BASIC name='%s'", filename)) + return { file_type = TAPE_TYPE_BASIC, filename = filename } + elseif type_byte == 0xD0 then + print(string.format("[MSX1 Cass Autoboot] Header: MACHINE name='%s'", filename)) + return { file_type = TAPE_TYPE_MACHINE, filename = filename } + elseif type_byte == 0xEA then + print(string.format("[MSX1 Cass Autoboot] Header: ASCII name='%s'", filename)) + return { file_type = TAPE_TYPE_ASCII, filename = filename } + end + + print(string.format("[MSX1 Cass Autoboot] Unknown type byte 0x%02X, defaulting to BASIC.", type_byte)) + return { file_type = TAPE_TYPE_BASIC, filename = filename } +end + +local function detect_tape_info() + print("[MSX1 Cass Autoboot] --- Tape type detection ---") + local path = find_cassette_path() + if not path then + print("[MSX1 Cass Autoboot] No cassette found, defaulting to BASIC.") + return { file_type = TAPE_TYPE_BASIC, filename = "" } + end + local data = zip_util.read_bytes(path, CAS_SCAN_BYTES) + if not data then + print("[MSX1 Cass Autoboot] Could not read cassette, defaulting to BASIC.") + return { file_type = TAPE_TYPE_BASIC, filename = "" } + end + local info = parse_cas_header(data) + if info then return info end + print("[MSX1 Cass Autoboot] No valid header found, defaulting to BASIC.") + return { file_type = TAPE_TYPE_BASIC, filename = "" } +end + +-- ── Boot functions ──────────────────────────────────────────────────────────── + +local cassette_handler = common_autoboot.create_cassette_handler(CASSETTE_DEVICE_TAG) +local tape_info = nil + +local function boot_basic() + -- Tokenised BASIC saved with CSAVE (0xD3): CLOAD, then RUN after motor off + common_autoboot.type_at_frame(frame_num, "CLOAD\n", 150, BUTTON_PRESS_DURATION) + common_autoboot.play_cassette_at_frame(frame_num, CASSETTE_DEVICE_TAG, 250) + + if cassette_handler(frame_num, CASSETTE_MOTOR_OFF_DELAY_FRAMES) then + emu.keypost("RUN\n") + end +end + +local function boot_machine() + -- Binary saved with BSAVE (0xD0): BLOAD"CAS:",R — auto-executes, no post-load command + common_autoboot.type_at_frame(frame_num, 'BLOAD"CAS:",R\n', 150, BUTTON_PRESS_DURATION) + common_autoboot.play_cassette_at_frame(frame_num, CASSETTE_DEVICE_TAG, 250) + + cassette_handler(frame_num, CASSETTE_MOTOR_OFF_DELAY_FRAMES) +end + +local function boot_ascii() + -- ASCII BASIC saved with SAVE"CAS:",A (0xEA): LOAD"CAS:" waits for motor like CLOAD, + -- then RUN after tape stops. RUN"CAS:" returns "bad file name" on MSX2 DISK-BASIC + -- because it doesn't block for the motor signal before timing out. + common_autoboot.type_at_frame(frame_num, 'LOAD"CAS:"\n', 150, BUTTON_PRESS_DURATION) + common_autoboot.play_cassette_at_frame(frame_num, CASSETTE_DEVICE_TAG, 250) + + if cassette_handler(frame_num, CASSETTE_MOTOR_OFF_DELAY_FRAMES) then + emu.keypost("RUN\n") + end +end + +-- ── Main ────────────────────────────────────────────────────────────────────── + +local function process_frame() + frame_num = frame_num + 1 + + if frame_num == 1 then + emu.print_info("System Driver: " .. emu.romname()) + common_autoboot.print_image_info() + tape_info = detect_tape_info() + end + + common_autoboot.debug_frame_num(frame_num) + + if tape_info.file_type == TAPE_TYPE_MACHINE then + boot_machine() + elseif tape_info.file_type == TAPE_TYPE_ASCII then + boot_ascii() + else + boot_basic() + end +end + +subscription = emu.add_machine_frame_notifier(process_frame) diff --git a/package/batocera/core/batocera-configgen/data/mame/autoboot_scripts/mtx_cass.lua b/package/batocera/core/batocera-configgen/data/mame/autoboot_scripts/mtx_cass.lua new file mode 100644 index 00000000000..b873c87d3d8 --- /dev/null +++ b/package/batocera/core/batocera-configgen/data/mame/autoboot_scripts/mtx_cass.lua @@ -0,0 +1,135 @@ +-- mtx_cass.lua + +-- Load common autoboot functions +local common_autoboot = dofile("/usr/share/batocera/configgen/data/mame/autoboot_scripts/autoboot_common.lua") + +-- Script-wide variables +local button = {} +local frame_num = 0 + +-- --- Cassette Loading Handler Setup --- +local CASSETTE_SLOT = 4 +local CASSETTE_DEVICE_TAG = manager.machine.images:at(CASSETTE_SLOT).device.tag +local cassette_handler = common_autoboot.create_cassette_handler(CASSETTE_DEVICE_TAG) + +common_autoboot.populate_buttons(button) + +common_autoboot.print_image_info() + +-- --- Constants for this specific script --- +local BUTTON_PRESS_DURATION = common_autoboot.DEFAULT_BUTTON_PRESS_DURATION +local CASSETTE_MOTOR_OFF_DELAY_FRAMES = common_autoboot.DEFAULT_CASSETTE_MOTOR_OFF_DELAY_FRAMES + +-- Detect MTX500-only games by filename. +-- Returns "mtx500_run" if the game needs POKE+NEW+LOAD+RUN, +-- "mtx500" if the game needs POKE+NEW+LOAD (no RUN), +-- "default" otherwise. +local function detect_game_type(filename) + local lower = string.lower(filename) + if lower:find("mtx500") or lower:find("mtx 500") then + -- filename convention: (poke 64122,0--new--load--run) or (poke 64122,0--new--load) + if lower:find("load%-%-run") or lower:find("load%-run") then + return "mtx500_run" + else + return "mtx500" + end + end + return "default" +end + +local function boot_default(cassette_handler) + common_autoboot.type_at_frame(frame_num, "LOAD\"\"\n", 100) + common_autoboot.play_cassette_at_frame(frame_num, CASSETTE_DEVICE_TAG, 200) + + local cassette_load_done = cassette_handler(frame_num, CASSETTE_MOTOR_OFF_DELAY_FRAMES) + + if cassette_load_done then + -- emu.keypost('RUN\n') + end +end + +-- MTX500-only games: ROM at 0xFA00-0xFFFF must be banked out before loading. +-- POKE 64122,0 disables the MTX ROM overlay and maps RAM in its place. + +-- Persistent state for motor detection and RUN scheduling +local mtx500_motor_was_on = false +local mtx500_motor_off_frame = 0 +local mtx500_run_sent = false +local RUN_DELAY_FRAMES = 120 + +local function boot_mtx500_base(cassette_handler, with_run) + common_autoboot.type_at_frame(frame_num, "POKE 64122,0\n", 100) + common_autoboot.type_at_frame(frame_num, "NEW\n", 200) + common_autoboot.type_at_frame(frame_num, "LOAD\"\"\n", 300) + common_autoboot.play_cassette_at_frame(frame_num, CASSETTE_DEVICE_TAG, 400) + + -- Use cassette_handler for throttle management only + cassette_handler(frame_num, CASSETTE_MOTOR_OFF_DELAY_FRAMES) + + if frame_num < 400 then return end + + -- Direct cassette state monitoring (independent of cassette_handler) + local cass = manager.machine.cassettes[CASSETTE_DEVICE_TAG] + if not cass then return end + + local playing = (cass.is_stopped == false) and (cass.motor_state == true) + + if playing then + mtx500_motor_was_on = true + mtx500_motor_off_frame = 0 -- reset if motor comes back on (inter-block gap) + elseif mtx500_motor_was_on and mtx500_motor_off_frame == 0 then + mtx500_motor_off_frame = frame_num + end + + if with_run and not mtx500_run_sent + and mtx500_motor_off_frame > 0 + and frame_num >= mtx500_motor_off_frame + RUN_DELAY_FRAMES then + manager.machine.video.throttled = true + manager.machine.video.frameskip = 0 + emu.keypost("RUN\n") + mtx500_run_sent = true + end +end + +local function boot_mtx500(cassette_handler) + boot_mtx500_base(cassette_handler, false) +end + +local function boot_mtx500_run(cassette_handler) + boot_mtx500_base(cassette_handler, true) +end + +-- --- Main Script Logic --- + +local current_software_name = manager.machine.images:at(CASSETTE_SLOT).filename +local game_type = detect_game_type(current_software_name) + +local boot_sequences = { + ['default'] = boot_default, + ['mtx500'] = boot_mtx500, + ['mtx500_run'] = boot_mtx500_run, +} + +-- MAME machine frame notification callback +local function process_frame() + frame_num = frame_num + 1 + + -- Initial setup/info display at frame 1 + if frame_num == 1 then + emu.print_info("System Driver: " .. emu.romname()) + emu.print_info("Loaded Software Name (from image filename): " .. current_software_name) + emu.print_info("--- Detected game type: " .. game_type .. " ---") + end + + --- DEBUG: Print current frame number every 100 frames --- + common_autoboot.debug_frame_num(frame_num) + + local boot_function = boot_sequences[game_type] or boot_sequences["default"] + + if boot_function then + boot_function(cassette_handler) + end +end + +-- Subscribe to machine frame notifications +subscription = emu.add_machine_frame_notifier(process_frame) diff --git a/package/batocera/core/batocera-configgen/data/mame/autoboot_scripts/mz2000_cass.lua b/package/batocera/core/batocera-configgen/data/mame/autoboot_scripts/mz2000_cass.lua new file mode 100644 index 00000000000..40123c775d5 --- /dev/null +++ b/package/batocera/core/batocera-configgen/data/mame/autoboot_scripts/mz2000_cass.lua @@ -0,0 +1,25 @@ +local frame_num = 0 + +local function process_frame() + frame_num = frame_num + 1 + + if frame_num == 150 then + emu.keypost('C') + end + + if frame_num == 200 then + manager.machine.cassettes[":cassette"]:play() + end + + if manager.machine.cassettes[":cassette"].is_stopped == false and manager.machine.cassettes[":cassette"].motor_state == true then + manager.machine.video.throttled = false + manager.machine.video.frameskip = 12 + else + manager.machine.video.throttled = true + manager.machine.video.frameskip = 0 + end +end + +subscription=emu.add_machine_frame_notifier(process_frame) + + diff --git a/package/batocera/core/batocera-configgen/data/mame/autoboot_scripts/mz700_cass.lua b/package/batocera/core/batocera-configgen/data/mame/autoboot_scripts/mz700_cass.lua new file mode 100644 index 00000000000..c4922cbe307 --- /dev/null +++ b/package/batocera/core/batocera-configgen/data/mame/autoboot_scripts/mz700_cass.lua @@ -0,0 +1,81 @@ +-- bbc_cass.lua + +-- WIP: Some games require *RUN instead of CHAIN, WIP WIP!! + +-- Load common autoboot functions +local common_autoboot = dofile("/usr/share/batocera/configgen/data/mame/autoboot_scripts/autoboot_common.lua") + +-- Script-wide variables +local button = {} +local frame_num = 0 + +common_autoboot.populate_buttons(button) + +-- Print the button table contents for debugging +-- common_autoboot.print_buttons(button) + +-- --- Constants for this specific script --- +local BUTTON_PRESS_DURATION = common_autoboot.DEFAULT_BUTTON_PRESS_DURATION +local CASSETTE_MOTOR_OFF_DELAY_FRAMES = common_autoboot.DEFAULT_CASSETTE_MOTOR_OFF_DELAY_FRAMES + +local function boot_default(cassette_handler) + common_autoboot.type_at_frame(frame_num, "LOAD\n", 50, BUTTON_PRESS_DURATION) +-- common_autoboot.type_at_frame(frame_num, "CHAIN\"\"\n", 100, BUTTON_PRESS_DURATION) + common_autoboot.play_cassette_at_frame(frame_num, ":cassette", 100) + + local cassette_load_done = cassette_handler(frame_num, CASSETTE_MOTOR_OFF_DELAY_FRAMES) + + if cassette_load_done then + -- emu.keypost('RUN\n') + end +end + +-- --- Main Script Logic --- + +-- Determine the currently loaded software name +local current_software_name = manager.machine.images:at(1).filename + +-- Map software names to their respective boot functions +local boot_sequences = { + ["default"] = boot_default, +} + +-- --- Cassette Loading Handler Setup --- +local cassette_handler = common_autoboot.create_cassette_handler(":cassette") + +-- MAME machine frame notification callback +local function process_frame() + frame_num = frame_num + 1 + + -- Initial setup/info display at frame 1 + if frame_num == 1 then + emu.print_info("System Driver: " .. emu.romname()) + emu.print_info("Loaded Software Name (from image filename): " .. current_software_name) + + -- Determine which profile will be used + if boot_sequences[current_software_name] then + emu.print_info("--- Booting using game specific profile: " .. current_software_name .. " ---") + else + emu.print_info("--- Booting using default profile ---") + end + end + + -- --- Print current frame number every 100 frames --- + if frame_num % 100 == 0 then -- The modulo operator (%) gives the remainder of a division + emu.print_info("Current Frame: " .. frame_num) + end + + -- Execute the relevant boot sequence based on the loaded software + local boot_function = boot_sequences[current_software_name] + + if not boot_function then -- If specific function not found + boot_function = boot_sequences["default"] -- Assign the default function + end + + if boot_function then + boot_function(cassette_handler) + end +end + +-- Subscribe to machine frame notifications +subscription = emu.add_machine_frame_notifier(process_frame) diff --git a/package/batocera/core/batocera-configgen/data/mame/autoboot_scripts/mz800_cass.lua b/package/batocera/core/batocera-configgen/data/mame/autoboot_scripts/mz800_cass.lua new file mode 100644 index 00000000000..40123c775d5 --- /dev/null +++ b/package/batocera/core/batocera-configgen/data/mame/autoboot_scripts/mz800_cass.lua @@ -0,0 +1,25 @@ +local frame_num = 0 + +local function process_frame() + frame_num = frame_num + 1 + + if frame_num == 150 then + emu.keypost('C') + end + + if frame_num == 200 then + manager.machine.cassettes[":cassette"]:play() + end + + if manager.machine.cassettes[":cassette"].is_stopped == false and manager.machine.cassettes[":cassette"].motor_state == true then + manager.machine.video.throttled = false + manager.machine.video.frameskip = 12 + else + manager.machine.video.throttled = true + manager.machine.video.frameskip = 0 + end +end + +subscription=emu.add_machine_frame_notifier(process_frame) + + diff --git a/package/batocera/core/batocera-configgen/data/mame/autoboot_scripts/mz80k_cass.lua b/package/batocera/core/batocera-configgen/data/mame/autoboot_scripts/mz80k_cass.lua new file mode 100644 index 00000000000..86c7cde0d6d --- /dev/null +++ b/package/batocera/core/batocera-configgen/data/mame/autoboot_scripts/mz80k_cass.lua @@ -0,0 +1,33 @@ +local frame_num = 0 +local function process_frame() + frame_num = frame_num + 1 + if frame_num == 10 then + emu.keypost('L') + end + if frame_num == 50 then + emu.keypost('O') + end + if frame_num == 100 then + emu.keypost('A') + end + if frame_num == 150 then + emu.keypost('D') + end + if frame_num == 200 then + emu.keypost('\n') + end + if frame_num == 250 then + manager.machine.cassettes[":cassette"]:play() + end + if manager.machine.cassettes[":cassette"].is_stopped == false and manager.machine.cassettes[":cassette"].motor_state == true then + manager.machine.video.throttled = false + manager.machine.video.frameskip = 12 + else + manager.machine.video.throttled = true + manager.machine.video.frameskip = 0 + end +end + +subscription=emu.add_machine_frame_notifier(process_frame) + + diff --git a/package/batocera/core/batocera-configgen/data/mame/autoboot_scripts/ngp.lua b/package/batocera/core/batocera-configgen/data/mame/autoboot_scripts/ngp.lua new file mode 100644 index 00000000000..ce4c4c1b151 --- /dev/null +++ b/package/batocera/core/batocera-configgen/data/mame/autoboot_scripts/ngp.lua @@ -0,0 +1,23 @@ +local button = {} +local ports = manager.machine.ioport.ports[":Controls"] +for field_name, field in pairs(ports.fields) do + button[field_name] = field +end + +local frame_num = 0 +local function process_frame() + frame_num = frame_num + 1 + + if frame_num < 100 * 6 then + if frame_num % 2 == 0 then + button["Button A"]:set_value(1) + else + button["Button A"]:set_value(0) + end + end +end + +subscription=emu.add_machine_frame_notifier(process_frame) + + + diff --git a/package/batocera/core/batocera-configgen/data/mame/autoboot_scripts/orao_cass.lua b/package/batocera/core/batocera-configgen/data/mame/autoboot_scripts/orao_cass.lua new file mode 100644 index 00000000000..a50ce109fc7 --- /dev/null +++ b/package/batocera/core/batocera-configgen/data/mame/autoboot_scripts/orao_cass.lua @@ -0,0 +1,24 @@ +local frame_num = 0 +local function process_frame() + frame_num = frame_num + 1 + + if frame_num == 40 then + emu.keypost('BC\n') + elseif frame_num == 90 then + emu.keypost('\n') + elseif frame_num == 190 then + emu.keypost('LMEM""\n') + elseif frame_num == 220 then + manager.machine.cassettes[":cassette"]:play() + end + + if manager.machine.cassettes[":cassette"].is_stopped == false and manager.machine.cassettes[":cassette"].motor_state == true then + manager.machine.video.throttled = false + manager.machine.video.frameskip = 12 + else + manager.machine.video.throttled = true + manager.machine.video.frameskip = 0 + end +end + +subscription=emu.add_machine_frame_notifier(process_frame) diff --git a/package/batocera/core/batocera-configgen/data/mame/autoboot_scripts/oric1_cass.lua b/package/batocera/core/batocera-configgen/data/mame/autoboot_scripts/oric1_cass.lua new file mode 100644 index 00000000000..1c079751fa2 --- /dev/null +++ b/package/batocera/core/batocera-configgen/data/mame/autoboot_scripts/oric1_cass.lua @@ -0,0 +1,76 @@ +-- oric1_cass.lua + +-- Load common autoboot functions +local common_autoboot = dofile("/usr/share/batocera/configgen/data/mame/autoboot_scripts/autoboot_common.lua") + +-- Script-wide variables +local button = {} +local frame_num = 0 + +-- Load buttons +common_autoboot.populate_buttons(button) + +-- --- Constants for this specific script --- +local BUTTON_PRESS_DURATION = common_autoboot.DEFAULT_BUTTON_PRESS_DURATION +local CASSETTE_MOTOR_OFF_DELAY_FRAMES = common_autoboot.DEFAULT_CASSETTE_MOTOR_OFF_DELAY_FRAMES + +local function boot_default(cassette_handler) + common_autoboot.type_at_frame(frame_num, "CLOAD\"\"\n", 200, BUTTON_PRESS_DURATION) + common_autoboot.play_cassette_at_frame(frame_num, ":cassette", 300) + + local cassette_load_done = cassette_handler(frame_num, CASSETTE_MOTOR_OFF_DELAY_FRAMES) + + if cassette_load_done then + -- emu.keypost('RUN\n') + end +end + +-- --- Main Script Logic --- + +-- Determine the currently loaded software name +local current_software_name = manager.machine.images:at(2).filename + +-- Map software names to their respective boot functions +local boot_sequences = { + ["default"] = boot_default, +} + +-- --- Cassette Loading Handler Setup --- +local cassette_handler = common_autoboot.create_cassette_handler(":cassette") + +-- MAME machine frame notification callback +local function process_frame() + frame_num = frame_num + 1 + + -- Initial setup/info display at frame 1 + if frame_num == 1 then + emu.print_info("System Driver: " .. emu.romname()) + emu.print_info("Loaded Software Name (from image filename): " .. current_software_name) + + -- Determine which profile will be used + if boot_sequences[current_software_name] then + emu.print_info("--- Booting using game specific profile: " .. current_software_name .. " ---") + else + emu.print_info("--- Booting using default profile ---") + end + end + + --- Print current frame number every 100 frames --- + if frame_num % 100 == 0 then -- The modulo operator (%) gives the remainder of a division + emu.print_info("Current Frame: " .. frame_num) + end + + -- Execute the relevant boot sequence based on the loaded software + local boot_function = boot_sequences[current_software_name] + + if not boot_function then -- If specific function not found + boot_function = boot_sequences["default"] -- Assign the default function + end + + if boot_function then + boot_function(cassette_handler) + end +end + +-- Subscribe to machine frame notifications +subscription = emu.add_machine_frame_notifier(process_frame) \ No newline at end of file diff --git a/package/batocera/core/batocera-configgen/data/mame/autoboot_scripts/orion_cass.lua b/package/batocera/core/batocera-configgen/data/mame/autoboot_scripts/orion_cass.lua new file mode 100644 index 00000000000..e6bf0d41bbb --- /dev/null +++ b/package/batocera/core/batocera-configgen/data/mame/autoboot_scripts/orion_cass.lua @@ -0,0 +1,152 @@ +-- orion_cass.lua + +-- Load common autoboot functions +local common_autoboot = dofile("/usr/share/batocera/configgen/data/mame/autoboot_scripts/autoboot_common.lua") + +-- Script-wide variables +local button = {} +local frame_num = 0 + +-- --- Load butons --- +common_autoboot.populate_buttons(button) + +-- --- Print Info --- +common_autoboot.print_image_info() +common_autoboot.print_buttons(button) + +-- --- Cassette Loading Handler Setup --- +local CASSETTE_SLOT = 1 +local CASSETTE_DEVICE_TAG = manager.machine.images:at(CASSETTE_SLOT).device.tag +local cassette_handler = common_autoboot.create_cassette_handler(CASSETTE_DEVICE_TAG) + + +-- --- Constants for this specific script --- +local BUTTON_PRESS_DURATION = common_autoboot.DEFAULT_BUTTON_PRESS_DURATION +local CASSETTE_MOTOR_OFF_DELAY_FRAMES = 100 -- common_autoboot.DEFAULT_CASSETTE_MOTOR_OFF_DELAY_FRAMES + +local function create_boot_default_sequence(frame_num, cassette_monitor_func) -- 'cassette_monitor_func' is the per-frame callable handler + local sequence_state = 0 + -- Define states for clarity + local STATES = { + INIT_PRE_LOAD_ACTIONS = 0, -- Initial actions before CLOAD/PLAY + PLAY_CASS_INITIATED = 1, -- CLOAD/PLAY commands sent, waiting for motor to spin up + LOADING_CASS = 2, -- Cassette motor is ON, monitoring for completion + CASS_LOAD_DONE = 3, -- Cassette has finished loading, waiting for post-load actions + POST_LOAD_ACTIONS = 4, -- Executing actions after cassette load + COMPLETE = 5 -- Sequence finished + } + + local post_load_start_frame = 0 -- Frame at which post-load actions began + + return function(current_frame_num) -- This inner function runs every frame + -- Always call cassette_monitor_func once we are in a state where loading should be active. + -- This ensures continuous monitoring, throttling, and detection of load completion. + if sequence_state >= STATES.LOADING_CASS and sequence_state < STATES.CASS_LOAD_DONE then + local cassette_load_done = cassette_monitor_func(current_frame_num, CASSETTE_MOTOR_OFF_DELAY_FRAMES) + if cassette_load_done then + sequence_state = STATES.CASS_LOAD_DONE -- Transition directly to CASS_LOAD_DONE + post_load_start_frame = current_frame_num -- Mark start for relative timing of post-actions + emu.print_info("Boot Default: Cassette loading is complete (triggered by handler). Transitioning to post-load actions.") + return -- Exit early for this frame after state change, to prevent double-execution + end + end + + -- --- State Machine Logic --- + if sequence_state == STATES.INIT_PRE_LOAD_ACTIONS then + -- Schedule initial pre-load actions + common_autoboot.press_and_release(button, current_frame_num, "Cursor Down", 100, 1) + common_autoboot.press_and_release(button, current_frame_num, "Cursor Down", 110, 1) + common_autoboot.press_and_release(button, current_frame_num, "Cursor Down", 120, 1) + common_autoboot.press_and_release(button, current_frame_num, "Enter", 150, BUTTON_PRESS_DURATION) + common_autoboot.type_at_frame(current_frame_num, "A\n", 200) + + common_autoboot.play_cassette_at_frame(current_frame_num, CASSETTE_DEVICE_TAG, 350) + + -- Transition to PLAY_CASS_INITIATED after these actions are scheduled (at the last target frame or beyond) + if current_frame_num >= 350 then + sequence_state = STATES.PLAY_CASS_INITIATED + emu.print_info("Boot Default: Pre-load actions scheduled. Waiting for cassette motor to engage.") + end + + elseif sequence_state == STATES.PLAY_CASS_INITIATED then + -- Wait for the cassette motor to actually spin up + local cassette_device = manager.machine.cassettes[CASSETTE_DEVICE_TAG] + if cassette_device and cassette_device.motor_state == true then + sequence_state = STATES.LOADING_CASS + emu.print_info("Boot Default: Cassette motor confirmed ON. Beginning continuous monitoring.") + end + + -- If sequence_state becomes CASS_LOAD_DONE (from the monitor above), this block is skipped, + -- and the logic will fall through to the next `elseif` for POST_LOAD_ACTIONS in the subsequent frame. + + elseif sequence_state == STATES.CASS_LOAD_DONE then + -- This state is primarily a transition point. + -- The 'RUN' command is NOT issued here. It's issued by the cassette_monitor_func internally. + -- We transition immediately to POST_LOAD_ACTIONS after load is detected. + sequence_state = STATES.POST_LOAD_ACTIONS + emu.print_info("Boot Default: Transitioning to post-load actions.") + + elseif sequence_state == STATES.POST_LOAD_ACTIONS then + -- --- Actions to perform AFTER cassette load is done --- + -- Use `post_load_start_frame` for relative timing + common_autoboot.press_and_release(button, current_frame_num, "F4", post_load_start_frame + 300, 1) + common_autoboot.press_and_release(button, current_frame_num, "Cursor Right", post_load_start_frame + 400, 1) + common_autoboot.press_and_release(button, current_frame_num, "Cursor Down", post_load_start_frame + 410, 1) + common_autoboot.press_and_release(button, current_frame_num, "Enter", post_load_start_frame + 420, BUTTON_PRESS_DURATION) + + -- Check if all post-load actions are completed + if current_frame_num >= post_load_start_frame + 1000 + BUTTON_PRESS_DURATION then + sequence_state = STATES.COMPLETE + emu.print_info("Boot Default: All post-load actions completed. Sequence finished.") + end + + elseif sequence_state == STATES.COMPLETE then + -- Do nothing, sequence is finished. + end + end +end + +-- --- Main Script Logic --- + +-- Determine the currently loaded software name +local current_software_name = manager.machine.images:at(CASSETTE_SLOT).filename + +-- Map software names to their respective boot functions +local boot_sequences = { + ['default'] = create_boot_default_sequence(frame_num, cassette_handler), +} + +-- MAME machine frame notification callback +local function process_frame() + frame_num = frame_num + 1 + + -- Initial setup/info display at frame 1 + if frame_num == 1 then + emu.print_info("System Driver: " .. emu.romname()) + emu.print_info("Loaded Software Name (from image filename): " .. current_software_name) + + -- Determine which profile will be used + if boot_sequences[current_software_name] then + emu.print_info("--- Booting using game specific profile: " .. current_software_name .. " ---") + else + emu.print_info("--- Booting using default profile ---") + end + end + + --- DEBUG: Print current frame number every 100 frames --- + common_autoboot.debug_frame_num(frame_num) + + -- Execute the relevant boot sequence based on the loaded software + local boot_function = boot_sequences[current_software_name] + + if not boot_function then -- If specific function not found + boot_function = boot_sequences["default"] -- Assign the default function + end + + if boot_function then + boot_function(frame_num, cassette_handler) + end +end + +-- Subscribe to machine frame notifications +subscription = emu.add_machine_frame_notifier(process_frame) \ No newline at end of file diff --git a/package/batocera/core/batocera-configgen/data/mame/autoboot_scripts/pc6001_cass.lua b/package/batocera/core/batocera-configgen/data/mame/autoboot_scripts/pc6001_cass.lua new file mode 100644 index 00000000000..7e7884d2bd7 --- /dev/null +++ b/package/batocera/core/batocera-configgen/data/mame/autoboot_scripts/pc6001_cass.lua @@ -0,0 +1,50 @@ +local frame_num = 0 +local throttle_asked = 0 +local throttle_qty = 0 +local command + +local function process_frame() + frame_num = frame_num + 1 + + --if manager.machine.cassettes[":cassette"].is_stopped == false and manager.machine.cassettes[":cassette"].motor_state == true then + -- if throttle_asked == 0 then + -- throttle_qty = throttle_qty + 1 + -- throttle_asked = 1 + + -- manager.machine.video.throttled = false + -- manager.machine.video.frameskip = 12 + -- end + --else + -- if throttle_asked == 1 then + -- throttle_asked = 0 + + -- manager.machine.video.throttled = true + -- manager.machine.video.frameskip = 0 + + -- emu.keypost("RUN\n") + -- end + --end + + if frame_num == 200 then + emu.keypost(command_page) + elseif frame_num == 250 then + emu.keypost("CLOAD\n") + end +end + +command_page = '' + +info_usage = os.getenv("RANDOMAME_INFO_USAGE") +if info_usage ~= nil and info_usage ~= "" then + if string.find(info_usage, "Page 1") then + command_page = "1\n" + elseif string.find(info_usage, "Page 2") then + command_page = "2\n" + elseif string.find(info_usage, "Page 3") then + command_page = "3\n" + elseif string.find(info_usage, "Page 4") then + command_page = "4\n" + end +end + +subscription=emu.add_machine_frame_notifier(process_frame) diff --git a/package/batocera/core/batocera-configgen/data/mame/autoboot_scripts/pc6001mk2_cass.lua b/package/batocera/core/batocera-configgen/data/mame/autoboot_scripts/pc6001mk2_cass.lua new file mode 100644 index 00000000000..147333351ee --- /dev/null +++ b/package/batocera/core/batocera-configgen/data/mame/autoboot_scripts/pc6001mk2_cass.lua @@ -0,0 +1,67 @@ +local frame_num = 0 +local throttle_asked = 0 +local throttle_qty = 0 +local command + +local function process_frame() + frame_num = frame_num + 1 + + --if manager.machine.cassettes[":cas_hack"].is_stopped == false and manager.machine.cassettes[":cas_hack"].motor_state == true then + -- if throttle_asked == 0 then + -- throttle_qty = throttle_qty + 1 + -- throttle_asked = 1 + + -- manager.machine.video.throttled = false + -- manager.machine.video.frameskip = 12 + -- end + --else + -- if throttle_asked == 1 then + -- throttle_asked = 0 + + -- manager.machine.video.throttled = true + -- manager.machine.video.frameskip = 0 + + -- emu.keypost("RUN\n") + -- end + --end + + if frame_num == 200 then + print("Mode: ",command_mode) + emu.keypost(command_mode) + elseif frame_num == 250 then + print("Page: ",command_page) + emu.keypost(command_page) + elseif frame_num == 300 then + emu.keypost("CLOAD\n") + end +end + +command_page = '' +command_mode = '' + +info_usage = os.getenv("RANDOMAME_INFO_USAGE") +if info_usage ~= nil and info_usage ~= "" then + if string.find(info_usage, "Page 1") then + command_page = "1\n" + elseif string.find(info_usage, "Page 2") then + command_page = "2\n" + elseif string.find(info_usage, "Page 3") then + command_page = "3\n" + elseif string.find(info_usage, "Page 4") then + command_page = "4\n" + end + + if string.find(info_usage, "Mode 1") then + command_mode = "1" + elseif string.find(info_usage, "Mode 2") then + command_mode = "2" + elseif string.find(info_usage, "Mode 3") then + command_mode = "3" + elseif string.find(info_usage, "Mode 4") then + command_mode = "4" + elseif string.find(info_usage, "Mode 5") then + command_mode = "5" + end +end + +subscription=emu.add_machine_frame_notifier(process_frame) diff --git a/package/batocera/core/batocera-configgen/data/mame/autoboot_scripts/pcecd.lua b/package/batocera/core/batocera-configgen/data/mame/autoboot_scripts/pcecd.lua new file mode 100644 index 00000000000..944dd86fa64 --- /dev/null +++ b/package/batocera/core/batocera-configgen/data/mame/autoboot_scripts/pcecd.lua @@ -0,0 +1,26 @@ +local button = {} + +for i, j in pairs(manager.machine.ioport.ports) do + for field_name, field in pairs(j.fields) do + id = field.port.tag .. ',' .. field.mask .. ',' .. field.type + print(field_name,"->", id) + button[id] = field + end +end + + +local frame_num = 0 +local function process_frame() + frame_num = frame_num + 1 + + if frame_num == 25 then + button[":ctrl:joypad2:BUTTONS,8,46"]:set_value(1) + elseif frame_num == 30 then + button[":ctrl:joypad2:BUTTONS,8,46"]:set_value(0) + end +end + +subscription=emu.add_machine_frame_notifier(process_frame) + + + diff --git a/package/batocera/core/batocera-configgen/data/mame/autoboot_scripts/pecom_cass.lua b/package/batocera/core/batocera-configgen/data/mame/autoboot_scripts/pecom_cass.lua new file mode 100644 index 00000000000..e8a55ae721d --- /dev/null +++ b/package/batocera/core/batocera-configgen/data/mame/autoboot_scripts/pecom_cass.lua @@ -0,0 +1,113 @@ +-- pecom_cass.lua + +local common_autoboot = dofile("/usr/share/batocera/configgen/data/mame/autoboot_scripts/autoboot_common.lua") + +local frame_num = 0 + +-- Build button table keyed by "port_tag,mask,type" for direct key presses (juku.lua style) +local button = {} +for _, port in pairs(manager.machine.ioport.ports) do + for _, field in pairs(port.fields) do + button[field.port.tag .. "," .. field.mask .. "," .. field.type] = field + end +end + +common_autoboot.print_image_info() + +local CASSETTE_DEVICE_TAG = ":cassette" +local CASSETTE_MOTOR_OFF_DELAY_FRAMES = common_autoboot.DEFAULT_CASSETTE_MOTOR_OFF_DELAY_FRAMES + +-- RUN key IDs (port_tag, mask, IPT_KEYBOARD=49) +-- Source: src/mame/sfrj/pecom.cpp +local KEY = { + R = ":LINE19,1,49", + U = ":LINE20,2,49", + N = ":LINE17,1,49", + RETURN = ":LINE0,1,49", +} +local KEY_DURATION = 5 +local KEY_GAP = 10 + +local function press_key(id, press_frame) + if not button[id] then return end + if frame_num == press_frame then + button[id]:set_value(1) + elseif frame_num == press_frame + KEY_DURATION then + button[id]:set_value(0) + end +end + +-- is_stopped-based handler: works even when the Pecom does not assert motor_state. +-- Mirrors the approach used in mc10_cass.lua. +local function create_pecom_cassette_handler(tag) + local stop_frame = 0 + local seen_playing = false + local load_done = false + + return function(current_frame_num, delay) + local dev = manager.machine.cassettes[tag] + if not dev then + emu.print_info("WARNING: Cassette device '" .. tag .. "' not found!") + return false + end + + if dev.is_stopped == false then + manager.machine.video.throttled = false + manager.machine.video.frameskip = 12 + seen_playing = true + else + manager.machine.video.throttled = true + manager.machine.video.frameskip = 0 + if seen_playing and stop_frame == 0 then + stop_frame = current_frame_num + emu.print_info("[Pecom] Cassette stopped at frame " .. current_frame_num) + end + end + + if not load_done and stop_frame ~= 0 and current_frame_num > stop_frame + delay then + load_done = true + emu.print_info("[Pecom] Cassette load complete at frame " .. current_frame_num) + return true + end + + return false + end +end + +local cassette_handler = create_pecom_cassette_handler(CASSETTE_DEVICE_TAG) +local post_load_frame = nil + +local function boot_default() + common_autoboot.type_at_frame(frame_num, "PLOAD\n", 50) + common_autoboot.play_cassette_at_frame(frame_num, CASSETTE_DEVICE_TAG, 150) + + if cassette_handler(frame_num, CASSETTE_MOTOR_OFF_DELAY_FRAMES) then + post_load_frame = frame_num + end + + -- Type RUN + Return via direct key presses to avoid natural-keyboard case issues + if post_load_frame then + local base = post_load_frame + 10 + press_key(KEY.R, base) + press_key(KEY.U, base + (KEY_DURATION + KEY_GAP)) + press_key(KEY.N, base + (KEY_DURATION + KEY_GAP) * 2) + press_key(KEY.RETURN, base + (KEY_DURATION + KEY_GAP) * 3) + end +end + +local current_software_name = manager.machine.images:at(1).filename + +local function process_frame() + frame_num = frame_num + 1 + + if frame_num == 1 then + emu.print_info("System Driver: " .. emu.romname()) + emu.print_info("Loaded Software: " .. current_software_name) + end + + common_autoboot.debug_frame_num(frame_num) + + boot_default() +end + +subscription = emu.add_machine_frame_notifier(process_frame) diff --git a/package/batocera/core/batocera-configgen/data/mame/autoboot_scripts/radio86_cass.lua b/package/batocera/core/batocera-configgen/data/mame/autoboot_scripts/radio86_cass.lua new file mode 100644 index 00000000000..ab824ba9580 --- /dev/null +++ b/package/batocera/core/batocera-configgen/data/mame/autoboot_scripts/radio86_cass.lua @@ -0,0 +1,78 @@ +-- radio86_cass.lua + +-- Load common autoboot functions +local common_autoboot = dofile("/usr/share/batocera/configgen/data/mame/autoboot_scripts/autoboot_common.lua") + +-- Script-wide variables +local button = {} +local frame_num = 0 + +-- --- Print Info --- +common_autoboot.print_image_info() + +-- --- Cassette Loading Handler Setup --- +local CASSETTE_SLOT = 1 +local CASSETTE_DEVICE_TAG = manager.machine.images:at(CASSETTE_SLOT).device.tag +local cassette_handler = common_autoboot.create_cassette_handler(CASSETTE_DEVICE_TAG) + +common_autoboot.populate_buttons(button) + +-- --- Constants for this specific script --- +local BUTTON_PRESS_DURATION = common_autoboot.DEFAULT_BUTTON_PRESS_DURATION +local CASSETTE_MOTOR_OFF_DELAY_FRAMES = common_autoboot.DEFAULT_CASSETTE_MOTOR_OFF_DELAY_FRAMES + +local function boot_default(cassette_handler) + common_autoboot.type_at_frame(frame_num, "I\n", 100) + common_autoboot.play_cassette_at_frame(frame_num, CASSETTE_DEVICE_TAG, 200) + + local cassette_load_done = cassette_handler(frame_num, CASSETTE_MOTOR_OFF_DELAY_FRAMES) + + if cassette_load_done then + emu.keypost('G\n') + end +end + +-- --- Main Script Logic --- + +-- Determine the currently loaded software name +local current_software_name = manager.machine.images:at(CASSETTE_SLOT).filename + +-- Map software names to their respective boot functions +local boot_sequences = { + ['default'] = boot_default, +} + +-- MAME machine frame notification callback +local function process_frame() + frame_num = frame_num + 1 + + -- Initial setup/info display at frame 1 + if frame_num == 1 then + emu.print_info("System Driver: " .. emu.romname()) + emu.print_info("Loaded Software Name (from image filename): " .. current_software_name) + + -- Determine which profile will be used + if boot_sequences[current_software_name] then + emu.print_info("--- Booting using game specific profile: " .. current_software_name .. " ---") + else + emu.print_info("--- Booting using default profile ---") + end + end + + --- DEBUG: Print current frame number every 100 frames --- + common_autoboot.debug_frame_num(frame_num) + + -- Execute the relevant boot sequence based on the loaded software + local boot_function = boot_sequences[current_software_name] + + if not boot_function then -- If specific function not found + boot_function = boot_sequences["default"] -- Assign the default function + end + + if boot_function then + boot_function(cassette_handler) + end +end + +-- Subscribe to machine frame notifications +subscription = emu.add_machine_frame_notifier(process_frame) \ No newline at end of file diff --git a/package/batocera/core/batocera-configgen/data/mame/autoboot_scripts/rx78_cass.lua b/package/batocera/core/batocera-configgen/data/mame/autoboot_scripts/rx78_cass.lua new file mode 100644 index 00000000000..f4cf0dd8da2 --- /dev/null +++ b/package/batocera/core/batocera-configgen/data/mame/autoboot_scripts/rx78_cass.lua @@ -0,0 +1,92 @@ +-- rx78_cass.lua + +-- Load common autoboot functions +local common_autoboot = dofile("/usr/share/batocera/configgen/data/mame/autoboot_scripts/autoboot_common.lua") + +-- Script-wide variables +local button = {} +local frame_num = 0 + +common_autoboot.populate_buttons(button) + +-- --- Constants for this specific script --- +local BUTTON_PRESS_DURATION = common_autoboot.DEFAULT_BUTTON_PRESS_DURATION +local CASSETTE_MOTOR_OFF_DELAY_FRAMES = common_autoboot.DEFAULT_CASSETTE_MOTOR_OFF_DELAY_FRAMES + +local function boot_graphmaths(cassette_handler) + common_autoboot.type_at_frame(frame_num, "MON\n", 100) + common_autoboot.type_at_frame(frame_num, "L\n", 200) + common_autoboot.type_at_frame(frame_num, "GRM\n", 300) + common_autoboot.play_cassette_at_frame(frame_num, ":cassette", 400) + + local cassette_load_done = cassette_handler(frame_num, CASSETTE_MOTOR_OFF_DELAY_FRAMES) + + if cassette_load_done then + emu.keypost('RUN\n') + end +end + +local function boot_yellowcab(cassette_handler) + -- common_autoboot.play_cassette_at_frame(frame_num, ":cassette", 100) + common_autoboot.type_at_frame(frame_num, "MON\n", 100) + common_autoboot.type_at_frame(frame_num, "L\n", 200) + common_autoboot.type_at_frame(frame_num, "CAB\n", 300) + common_autoboot.play_cassette_at_frame(frame_num, ":cassette", 400) + + local cassette_load_done = cassette_handler(frame_num, CASSETTE_MOTOR_OFF_DELAY_FRAMES) + + if cassette_load_done then + -- emu.keypost('CAB\n') + end +end + +-- --- Main Script Logic --- + +-- Determine the currently loaded software name +local current_software_name = manager.machine.images:at(2).filename + +-- Map software names to their respective boot functions +local boot_sequences = { + graphmaths = boot_graphmaths, + yellowcab = boot_yellowcab, +} + +-- --- Cassette Loading Handler Setup --- +local cassette_handler = common_autoboot.create_cassette_handler(":cassette") + +-- MAME machine frame notification callback +local function process_frame() + frame_num = frame_num + 1 + + -- Initial setup/info display at frame 1 + if frame_num == 1 then + emu.print_info("System Driver: " .. emu.romname()) + emu.print_info("Loaded Software Name (from image filename): " .. current_software_name) + + -- Determine which profile will be used + if boot_sequences[current_software_name] then + emu.print_info("--- Booting using game specific profile: " .. current_software_name .. " ---") + else + emu.print_info("--- Booting using default profile ---") + end + end + + --- Print current frame number every 100 frames --- + if frame_num % 100 == 0 then -- The modulo operator (%) gives the remainder of a division + emu.print_info("Current Frame: " .. frame_num) + end + + -- Execute the relevant boot sequence based on the loaded software + local boot_function = boot_sequences[current_software_name] + + if not boot_function then -- If specific function not found + boot_function = boot_sequences["default"] -- Assign the default function + end + + if boot_function then + boot_function(cassette_handler) + end +end + +-- Subscribe to machine frame notifications +subscription = emu.add_machine_frame_notifier(process_frame) \ No newline at end of file diff --git a/package/batocera/core/batocera-configgen/data/mame/autoboot_scripts/samcoupe_flop.lua b/package/batocera/core/batocera-configgen/data/mame/autoboot_scripts/samcoupe_flop.lua new file mode 100644 index 00000000000..71778841fca --- /dev/null +++ b/package/batocera/core/batocera-configgen/data/mame/autoboot_scripts/samcoupe_flop.lua @@ -0,0 +1 @@ +emu.keypost('\nboot\n') diff --git a/package/batocera/core/batocera-configgen/data/mame/autoboot_scripts/saturn.lua b/package/batocera/core/batocera-configgen/data/mame/autoboot_scripts/saturn.lua new file mode 100644 index 00000000000..cd49e0632bf --- /dev/null +++ b/package/batocera/core/batocera-configgen/data/mame/autoboot_scripts/saturn.lua @@ -0,0 +1,40 @@ +local button = {} + +for i, j in pairs(manager.machine.ioport.ports) do + for field_name, field in pairs(j.fields) do + id = field.port.tag .. ',' .. field.mask .. ',' .. field.type + print(field_name,"->", id) + button[id] = field + end +end + + +local frame_num = 0 +local start_frame = 1000 +local function process_frame() + frame_num = frame_num + 1 + + -- Up + if frame_num == start_frame + 0 then + button[":ctrl1:joypad:JOY,4096,51"]:set_value(1) + elseif frame_num == start_frame + 10 then + button[":ctrl1:joypad:JOY,4096,51"]:set_value(0) + -- Up + elseif frame_num == start_frame + 20 then + button[":ctrl1:joypad:JOY,4096,51"]:set_value(1) + elseif frame_num == start_frame + 30 then + button[":ctrl1:joypad:JOY,4096,51"]:set_value(0) + -- left + elseif frame_num == start_frame + 40 then + button[":ctrl1:joypad:JOY,16384,53"]:set_value(1) + elseif frame_num == start_frame + 50 then + button[":ctrl1:joypad:JOY,16384,53"]:set_value(0) + -- Button A + elseif frame_num == start_frame + 60 then + button[":ctrl1:joypad:JOY,1024,64"]:set_value(1) + elseif frame_num == start_frame + 70 then + button[":ctrl1:joypad:JOY,1024,64"]:set_value(0) + end +end + +subscription=emu.add_machine_frame_notifier(process_frame) diff --git a/package/batocera/core/batocera-configgen/data/mame/autoboot_scripts/sc3000_cass.lua b/package/batocera/core/batocera-configgen/data/mame/autoboot_scripts/sc3000_cass.lua new file mode 100644 index 00000000000..8eb54915a26 --- /dev/null +++ b/package/batocera/core/batocera-configgen/data/mame/autoboot_scripts/sc3000_cass.lua @@ -0,0 +1,77 @@ +-- sc3000_cass.lua + +-- Load common autoboot functions +local common_autoboot = dofile("/usr/share/batocera/configgen/data/mame/autoboot_scripts/autoboot_common.lua") + +-- Script-wide variables +local button = {} +local frame_num = 0 + +-- --- Cassette Loading Handler Setup --- +local CASSETTE_SLOT = 1 +local CASSETTE_DEVICE_TAG = manager.machine.images:at(CASSETTE_SLOT).device.tag +local cassette_handler = common_autoboot.create_cassette_handler(CASSETTE_DEVICE_TAG) + +common_autoboot.populate_buttons(button) + +common_autoboot.print_image_info() + +-- --- Constants for this specific script --- +local BUTTON_PRESS_DURATION = common_autoboot.DEFAULT_BUTTON_PRESS_DURATION +local CASSETTE_MOTOR_OFF_DELAY_FRAMES = common_autoboot.DEFAULT_CASSETTE_MOTOR_OFF_DELAY_FRAMES + +local function boot_default(cassette_handler) + common_autoboot.type_at_frame(frame_num, "LOAD\n", 300) + common_autoboot.play_cassette_at_frame(frame_num, CASSETTE_DEVICE_TAG, 400) + + local cassette_load_done = cassette_handler(frame_num, CASSETTE_MOTOR_OFF_DELAY_FRAMES) + + if cassette_load_done then + emu.keypost('RUN\n') + end +end + +-- --- Main Script Logic --- + +-- Determine the currently loaded software name +local current_software_name = manager.machine.images:at(CASSETTE_SLOT).filename + +-- Map software names to their respective boot functions +local boot_sequences = { + ['default'] = boot_default, +} + +-- MAME machine frame notification callback +local function process_frame() + frame_num = frame_num + 1 + + -- Initial setup/info display at frame 1 + if frame_num == 1 then + emu.print_info("System Driver: " .. emu.romname()) + emu.print_info("Loaded Software Name (from image filename): " .. current_software_name) + + -- Determine which profile will be used + if boot_sequences[current_software_name] then + emu.print_info("--- Booting using game specific profile: " .. current_software_name .. " ---") + else + emu.print_info("--- Booting using default profile ---") + end + end + + --- DEBUG: Print current frame number every 100 frames --- + common_autoboot.debug_frame_num(frame_num) + + -- Execute the relevant boot sequence based on the loaded software + local boot_function = boot_sequences[current_software_name] + + if not boot_function then -- If specific function not found + boot_function = boot_sequences["default"] -- Assign the default function + end + + if boot_function then + boot_function(cassette_handler) + end +end + +-- Subscribe to machine frame notifications +subscription = emu.add_machine_frame_notifier(process_frame) \ No newline at end of file diff --git a/package/batocera/core/batocera-configgen/data/mame/autoboot_scripts/sorcerer_cass.lua b/package/batocera/core/batocera-configgen/data/mame/autoboot_scripts/sorcerer_cass.lua new file mode 100644 index 00000000000..4f5fecabeb9 --- /dev/null +++ b/package/batocera/core/batocera-configgen/data/mame/autoboot_scripts/sorcerer_cass.lua @@ -0,0 +1,158 @@ +-- sorcerer_cass.lua + +-- Load common autoboot functions +local common_autoboot = dofile("/usr/share/batocera/configgen/data/mame/autoboot_scripts/autoboot_common.lua") + +-- Script-wide variables +local button = {} +local frame_num = 0 + +common_autoboot.populate_buttons(button) + +-- --- Constants for this specific script --- +local BUTTON_PRESS_DURATION = common_autoboot.DEFAULT_BUTTON_PRESS_DURATION +local CASSETTE_MOTOR_OFF_DELAY_FRAMES = common_autoboot.DEFAULT_CASSETTE_MOTOR_OFF_DELAY_FRAMES + +local function boot_default(cassette_handler) + common_autoboot.type_at_frame(frame_num, "CLOAD\n", 300, BUTTON_PRESS_DURATION) + common_autoboot.play_cassette_at_frame(frame_num, ":cassette", 400) + + local cassette_load_done = cassette_handler(frame_num, CASSETTE_MOTOR_OFF_DELAY_FRAMES) + + if cassette_load_done then + emu.keypost('RUN\n') + end +end + +local function create_boot_type_1_sequence() + return function(cassette_handler_arg) + common_autoboot.type_at_frame(frame_num, "BYE\n", 300) + common_autoboot.type_at_frame(frame_num, "LOG\n", 400) + common_autoboot.play_cassette_at_frame(frame_num, ":cassette", 500) + + local cassette_load_done = cassette_handler_arg(frame_num, CASSETTE_MOTOR_OFF_DELAY_FRAMES) + + if cassette_load_done then + -- emu.keypost('/\n') + end + end +end + +-- --- Main Script Logic --- + +-- Determine the currently loaded software name +local current_software_name = manager.machine.images:at(3).filename + +-- Map software names to their respective boot functions +local boot_sequences = { + adv1 = create_boot_type_1_sequence(), + adv2 = create_boot_type_1_sequence(), + adv3 = create_boot_type_1_sequence(), + adv4 = create_boot_type_1_sequence(), + adv5 = create_boot_type_1_sequence(), + adv6 = create_boot_type_1_sequence(), + adv7 = create_boot_type_1_sequence(), + adv8 = create_boot_type_1_sequence(), + adv9 = create_boot_type_1_sequence(), + amaze = create_boot_type_1_sequence(), + apatrol = create_boot_type_1_sequence(), + arith = create_boot_type_1_sequence(), + arrow = create_boot_type_1_sequence(), + arrow2 = create_boot_type_1_sequence(), + ast = create_boot_type_1_sequence(), + aster = create_boot_type_1_sequence(), + astro = create_boot_type_1_sequence(), + atc = create_boot_type_1_sequence(), + atc1 = create_boot_type_1_sequence(), + biochart = create_boot_type_1_sequence(), + biorhythm = create_boot_type_1_sequence(), + blackj = create_boot_type_1_sequence(), + cadas = create_boot_type_1_sequence(), + char = create_boot_type_1_sequence(), + chess1 = create_boot_type_1_sequence(), + chess2 = create_boot_type_1_sequence(), + chomp = create_boot_type_1_sequence(), + com48 = create_boot_type_1_sequence(), + cosmc = create_boot_type_1_sequence(), + crash = create_boot_type_1_sequence(), + debug = create_boot_type_1_sequence(), + defcm = create_boot_type_1_sequence(), + dfndr = create_boot_type_1_sequence(), + disas2 = create_boot_type_1_sequence(), + dterm = create_boot_type_1_sequence(), + dybug = create_boot_type_1_sequence(), + ezy = create_boot_type_1_sequence(), + fgam = create_boot_type_1_sequence(), + flip = create_boot_type_1_sequence(), + flite = create_boot_type_1_sequence(), + galax = create_boot_type_1_sequence(), + galx = create_boot_type_1_sequence(), + grotnik = create_boot_type_1_sequence(), + homerun = create_boot_type_1_sequence(), + hpad = create_boot_type_1_sequence(), + interceptor = create_boot_type_1_sequence(), + invad2 = create_boot_type_1_sequence(), + invaders = create_boot_type_1_sequence(), + kalid = create_boot_type_1_sequence(), + killerg = create_boot_type_1_sequence(), + kilo = create_boot_type_1_sequence(), + kilopede = create_boot_type_1_sequence(), + l2x = create_boot_type_1_sequence(), + landarc = create_boot_type_1_sequence(), + ldg = create_boot_type_1_sequence(), + magicmaze = create_boot_type_1_sequence(), + midas = create_boot_type_1_sequence(), + minva = create_boot_type_1_sequence(), + misil = create_boot_type_1_sequence(), + mmind = create_boot_type_1_sequence(), + munch = create_boot_type_1_sequence(), + nike2 = create_boot_type_1_sequence(), + robot = create_boot_type_1_sequence(), + spider = create_boot_type_1_sequence(), + starf = create_boot_type_1_sequence(), + sword = create_boot_type_1_sequence(), + wumpus = create_boot_type_1_sequence(), + zetu = create_boot_type_1_sequence(), + + ["default"] = boot_default, +} + +-- --- Cassette Loading Handler Setup --- +local cassette_handler = common_autoboot.create_cassette_handler(":cassette1") + +-- MAME machine frame notification callback +local function process_frame() + frame_num = frame_num + 1 + + -- Initial setup/info display at frame 1 + if frame_num == 1 then + emu.print_info("System Driver: " .. emu.romname()) + emu.print_info("Loaded Software Name (from image filename): " .. current_software_name) + + -- Determine which profile will be used + if boot_sequences[current_software_name] then + emu.print_info("--- Booting using game specific profile: " .. current_software_name .. " ---") + else + emu.print_info("--- Booting using default profile ---") + end + end + + --- Print current frame number every 100 frames --- + if frame_num % 100 == 0 then -- The modulo operator (%) gives the remainder of a division + emu.print_info("Current Frame: " .. frame_num) + end + + -- Execute the relevant boot sequence based on the loaded software + local boot_function = boot_sequences[current_software_name] + + if not boot_function then -- If specific function not found + boot_function = boot_sequences["default"] -- Assign the default function + end + + if boot_function then + boot_function(cassette_handler) + end +end + +-- Subscribe to machine frame notifications +subscription = emu.add_machine_frame_notifier(process_frame) \ No newline at end of file diff --git a/package/batocera/core/batocera-configgen/data/mame/autoboot_scripts/specpls3_flop.lua b/package/batocera/core/batocera-configgen/data/mame/autoboot_scripts/specpls3_flop.lua new file mode 100644 index 00000000000..2791876398e --- /dev/null +++ b/package/batocera/core/batocera-configgen/data/mame/autoboot_scripts/specpls3_flop.lua @@ -0,0 +1,3 @@ +emu.keypost('\n') + + diff --git a/package/batocera/core/batocera-configgen/data/mame/autoboot_scripts/spectrum_cass.lua b/package/batocera/core/batocera-configgen/data/mame/autoboot_scripts/spectrum_cass.lua new file mode 100644 index 00000000000..3cc215bd01e --- /dev/null +++ b/package/batocera/core/batocera-configgen/data/mame/autoboot_scripts/spectrum_cass.lua @@ -0,0 +1,15 @@ +local function process_frame() + if manager.machine.cassettes[":cassette"].is_stopped == false and manager.machine.cassettes[":cassette"].motor_state == true then + manager.machine.video.throttled = false + manager.machine.video.frameskip = 12 + else + manager.machine.video.throttled = true + manager.machine.video.frameskip = 0 + end +end + +subscription=emu.add_machine_frame_notifier(process_frame) + +emu.keypost('\nj""\n') +manager.machine.cassettes[":cassette"]:play() + diff --git a/package/batocera/core/batocera-configgen/data/mame/autoboot_scripts/super80_cass.lua b/package/batocera/core/batocera-configgen/data/mame/autoboot_scripts/super80_cass.lua new file mode 100644 index 00000000000..dd2b94bbb67 --- /dev/null +++ b/package/batocera/core/batocera-configgen/data/mame/autoboot_scripts/super80_cass.lua @@ -0,0 +1,284 @@ +-- super80_cass.lua + +-- WIP: need to figure out how to load games with usage + +-- Load common autoboot functions +local common_autoboot = dofile("/usr/share/batocera/configgen/data/mame/autoboot_scripts/autoboot_common.lua") + +-- Script-wide variables +local button = {} +local frame_num = 0 + +common_autoboot.populate_buttons(button) + +common_autoboot.print_image_info() + +-- Print the button table contents for debugging +-- common_autoboot.print_buttons(button) + +-- --- Constants for this specific script --- +local BUTTON_PRESS_DURATION = common_autoboot.DEFAULT_BUTTON_PRESS_DURATION +local CASSETTE_MOTOR_OFF_DELAY_FRAMES = common_autoboot.DEFAULT_CASSETTE_MOTOR_OFF_DELAY_FRAMES + +local function boot_default(cassette_handler) + common_autoboot.type_at_frame(frame_num, "CLOAD\n", 100, BUTTON_PRESS_DURATION) + + common_autoboot.play_cassette_at_frame(frame_num, ":cassette", 400) -- need to wait at least 300 frames after CLOAD cmd, otherwise will have LOADING ERROR! + + local cassette_load_done = cassette_handler(frame_num, CASSETTE_MOTOR_OFF_DELAY_FRAMES) + + if cassette_load_done then + emu.keypost('RUN\n') + end +end + +local function create_boot_type_1_sequence(title_to_type) + return function(cassette_handler_arg) + common_autoboot.type_at_frame(frame_num, "L\n", 100) + common_autoboot.play_cassette_at_frame(frame_num, ":cassette", 200) + + local cassette_load_done = cassette_handler_arg(frame_num, CASSETTE_MOTOR_OFF_DELAY_FRAMES) + + if cassette_load_done then + emu.keypost(title_to_type .. '\n') + end + end +end + +local function create_boot_type_2_sequence() + return function(cassette_handler_arg) + common_autoboot.type_at_frame(frame_num, "GD000\n", 100) -- GD000 to enter BASIC MODE + common_autoboot.type_at_frame(frame_num, "LOAD\n", 200) + common_autoboot.play_cassette_at_frame(frame_num, ":cassette", 300) + + local cassette_load_done = cassette_handler_arg(frame_num, CASSETTE_MOTOR_OFF_DELAY_FRAMES) + + if cassette_load_done then + emu.keypost('RUN\n') + end + end +end + +-- --- Main Script Logic --- + +-- Determine the currently loaded software name +local current_software_name = manager.machine.images:at(3).filename + +-- Map software names to their respective boot functions +local boot_sequences = { + aim = create_boot_type_2_sequence(), + accounts = create_boot_type_2_sequence(), + add = create_boot_type_2_sequence(), + basic = create_boot_type_1_sequence("G100"), + basic1 = create_boot_type_1_sequence("G100"), + bio = create_boot_type_2_sequence(), + bunny = create_boot_type_2_sequence(), + camel = create_boot_type_2_sequence(), + cap = create_boot_type_2_sequence(), + carrace = create_boot_type_2_sequence(), + cavemon = create_boot_type_2_sequence(), + cavemon4 = create_boot_type_2_sequence(), + cavepit = create_boot_type_2_sequence(), + chase = create_boot_type_2_sequence(), + chomp = create_boot_type_2_sequence(), + chucka = create_boot_type_2_sequence(), + crash = create_boot_type_2_sequence(), + crashj = create_boot_type_2_sequence(), + crazym = create_boot_type_1_sequence("G0"), + crazymj = create_boot_type_1_sequence("G0"), + crosfire = create_boot_type_2_sequence(), + cvoicec = create_boot_type_2_sequence(), + cvoicei = create_boot_type_2_sequence(), + debug = create_boot_type_1_sequence("G100"), + debug4 = create_boot_type_1_sequence("G100"), + dungeon = create_boot_type_2_sequence(), + dungeon4 = create_boot_type_2_sequence(), + edasm = create_boot_type_1_sequence("G8000"), + eldraw = create_boot_type_1_sequence("G100"), + epromp = create_boot_type_2_sequence(), + epromr = create_boot_type_2_sequence(), + findword = create_boot_type_2_sequence(), + hangman = create_boot_type_2_sequence(), + horserac = create_boot_type_2_sequence(), + horserace4 = create_boot_type_2_sequence(), + horseracem = create_boot_type_2_sequence(), + hydrax = create_boot_type_2_sequence(), + hydraxj = create_boot_type_2_sequence(), + inva = create_boot_type_1_sequence("G400"), + inva1 = create_boot_type_1_sequence("G400"), + inva1j = create_boot_type_1_sequence("G400"), + inva2 = create_boot_type_1_sequence("G400"), + inva2j = create_boot_type_1_sequence("G400"), + invanal = create_boot_type_2_sequence(), + juggle = create_boot_type_2_sequence(), + lcxf = create_boot_type_2_sequence(), + life = create_boot_type_1_sequence("G0"), + loancalc = create_boot_type_2_sequence(), + lunar1 = create_boot_type_2_sequence(), + lunar2 = create_boot_type_2_sequence(), + lunar3 = create_boot_type_2_sequence(), + lwriter = create_boot_type_2_sequence(), + lwriter4 = create_boot_type_2_sequence(), + l2basic = create_boot_type_1_sequence("G0"), + masterm = create_boot_type_2_sequence(), + matchmat = create_boot_type_2_sequence(), + maths = create_boot_type_2_sequence(), + mem = create_boot_type_2_sequence(), + meteor = create_boot_type_2_sequence(), + meteorj = create_boot_type_2_sequence(), + minitype = create_boot_type_1_sequence("G8000"), + minit1 = create_boot_type_1_sequence("GB700"), + minit3 = create_boot_type_1_sequence("G8000"), + missile = create_boot_type_2_sequence(), + missile4 = create_boot_type_2_sequence(), + missile4j = create_boot_type_2_sequence(), + moneytst = create_boot_type_2_sequence(), + monopoly = create_boot_type_2_sequence(), + moorads = create_boot_type_2_sequence(), + morse = create_boot_type_2_sequence(), + mortor1 = create_boot_type_2_sequence(), + mortor2 = create_boot_type_2_sequence(), + mortor4 = create_boot_type_2_sequence(), + onearm = create_boot_type_2_sequence(), + onearm4 = create_boot_type_2_sequence(), + opposite = create_boot_type_2_sequence(), + othello = create_boot_type_2_sequence(), + pelatron = create_boot_type_1_sequence("G100"), + petrol = create_boot_type_2_sequence(), + pi = create_boot_type_2_sequence(), + planet = create_boot_type_2_sequence(), + pontoon = create_boot_type_2_sequence(), + puzzle = create_boot_type_2_sequence(), + riverboa = create_boot_type_2_sequence(), + riverboatj = create_boot_type_2_sequence(), + rotate = create_boot_type_2_sequence(), + russroul = create_boot_type_2_sequence(), + s80solit = create_boot_type_2_sequence(), + sets = create_boot_type_2_sequence(), + shell = create_boot_type_2_sequence(), + shell4 = create_boot_type_2_sequence(), + shootgal = create_boot_type_2_sequence(), + shootgal1 = create_boot_type_2_sequence(), + sinv = create_boot_type_2_sequence(), + sinvj = create_boot_type_2_sequence(), + skier = create_boot_type_2_sequence(), + smart = create_boot_type_1_sequence("G100"), + snake = create_boot_type_2_sequence(), + snakej = create_boot_type_2_sequence(), + solit = create_boot_type_2_sequence(), + soundemo = create_boot_type_2_sequence(), + stopwatc = create_boot_type_2_sequence(), + suprtrek = create_boot_type_2_sequence(), + sword = create_boot_type_2_sequence(), + takeaway = create_boot_type_2_sequence(), + tenpin = create_boot_type_2_sequence(), + thisisbu = create_boot_type_2_sequence(), + thisisbu4 = create_boot_type_2_sequence(), + tibetian = create_boot_type_2_sequence(), + tictac = create_boot_type_2_sequence(), + treasure = create_boot_type_2_sequence(), + vonshrin = create_boot_type_2_sequence(), + x6845p = create_boot_type_2_sequence(), + x4inarow = create_boot_type_2_sequence(), + xaa = create_boot_type_1_sequence("G100"), + xaaj = create_boot_type_1_sequence("G100"), + xadvland = create_boot_type_1_sequence("G3DE0"), + xaster = create_boot_type_1_sequence("G100"), + xasterj = create_boot_type_1_sequence("G100"), + xastp = create_boot_type_1_sequence("G349B"), + xastp3 = create_boot_type_1_sequence("G349B"), + xastp3j = create_boot_type_1_sequence("G349B"), + xastro = create_boot_type_1_sequence("G100"), + xastroj = create_boot_type_1_sequence("G100"), + xatc = create_boot_type_1_sequence("G0"), + xbemon = create_boot_type_1_sequence("G6000"), + xbeez80 = create_boot_type_1_sequence("GA00"), + xcdes = create_boot_type_1_sequence("G100"), + xcstrek = create_boot_type_1_sequence("G100"), + xcaliens = create_boot_type_1_sequence("G100"), + xcharp = create_boot_type_2_sequence(), + xchomp = create_boot_type_1_sequence("G100"), + xchompj = create_boot_type_1_sequence("G100"), + xcorral = create_boot_type_2_sequence(), + xdemon = create_boot_type_1_sequence("G0"), + xdefend = create_boot_type_1_sequence("G100"), + xexmon = create_boot_type_1_sequence("GAA00"), + xexmon3 = create_boot_type_1_sequence("GAA00"), + xfgam = create_boot_type_1_sequence("G1F0"), + xgalax2j = create_boot_type_1_sequence("G100"), + xgalaxj = create_boot_type_1_sequence("G100"), + xgrtkitf = create_boot_type_2_sequence(), + xhangman = create_boot_type_2_sequence(), + xhoh = create_boot_type_2_sequence(), + xhoh3 = create_boot_type_2_sequence(), + xkadath = create_boot_type_2_sequence(), + xkilo = create_boot_type_1_sequence("G1C0A"), + xkilo1 = create_boot_type_1_sequence("G1C0A"), + xkilo1j = create_boot_type_1_sequence("G1C0A"), + xkiloj = create_boot_type_1_sequence("G1C0A"), + xmart = create_boot_type_1_sequence("G0"), + xmart1j = create_boot_type_1_sequence("G0"), + xmerlin = create_boot_type_2_sequence(), + xmerlin2 = create_boot_type_2_sequence(), + xmidas = create_boot_type_1_sequence("G200"), + xmission = create_boot_type_1_sequence("G3C80"), + xpenetr = create_boot_type_1_sequence("G100"), + xpenetrj = create_boot_type_1_sequence("G100"), + xpinegap = create_boot_type_2_sequence(), + xschess1 = create_boot_type_1_sequence("G100"), + xschess2 = create_boot_type_1_sequence("G4040"), + xsupinv = create_boot_type_2_sequence(), + xsupinvj = create_boot_type_2_sequence(), + xvalley = create_boot_type_2_sequence(), + xvalley3 = create_boot_type_2_sequence(), + xvalley3a = create_boot_type_2_sequence(), + xwbee = create_boot_type_1_sequence("G9000"), + xwbee2 = create_boot_type_1_sequence("G9000"), + xbreak = create_boot_type_2_sequence(), + xbreakj = create_boot_type_2_sequence(), + xmsinv = create_boot_type_1_sequence("G1000"), + xmsinvj = create_boot_type_1_sequence("G1000"), + xmsinv2 = create_boot_type_1_sequence("G1000"), + xmsinv2j = create_boot_type_1_sequence("G1000"), + xsub = create_boot_type_2_sequence(), +} + +-- --- Cassette Loading Handler Setup --- +local cassette_handler = common_autoboot.create_cassette_handler(":cassette") + +-- MAME machine frame notification callback +local function process_frame() + frame_num = frame_num + 1 + + -- Initial setup/info display at frame 1 + if frame_num == 1 then + emu.print_info("System Driver: " .. emu.romname()) + emu.print_info("Loaded Software Name (from image filename): " .. current_software_name) + + -- Determine which profile will be used + if boot_sequences[current_software_name] then + emu.print_info("--- Booting using game specific profile: " .. current_software_name .. " ---") + else + emu.print_info("--- Booting using default profile ---") + end + end + + -- --- Print current frame number every 100 frames --- + -- if frame_num % 100 == 0 then -- The modulo operator (%) gives the remainder of a division + -- emu.print_info("Current Frame: " .. frame_num) + -- end + + -- Execute the relevant boot sequence based on the loaded software + local boot_function = boot_sequences[current_software_name] + + if not boot_function then -- If specific function not found + boot_function = boot_sequences["default"] -- Assign the default function + end + + if boot_function then + boot_function(cassette_handler) + end +end + +-- Subscribe to machine frame notifications +subscription = emu.add_machine_frame_notifier(process_frame) \ No newline at end of file diff --git a/package/batocera/core/batocera-configgen/data/mame/autoboot_scripts/svi318_cass.lua b/package/batocera/core/batocera-configgen/data/mame/autoboot_scripts/svi318_cass.lua new file mode 100644 index 00000000000..796d8310c04 --- /dev/null +++ b/package/batocera/core/batocera-configgen/data/mame/autoboot_scripts/svi318_cass.lua @@ -0,0 +1,94 @@ +-- sv318_cass.lua + +-- Load common autoboot functions +local common_autoboot = dofile("/usr/share/batocera/configgen/data/mame/autoboot_scripts/autoboot_common.lua") + +-- Script-wide variables +local button = {} +local frame_num = 0 +local bload_frame = -1 + +common_autoboot.populate_buttons(button) + +-- --- Constants for this specific script --- +local BUTTON_PRESS_DURATION = common_autoboot.DEFAULT_BUTTON_PRESS_DURATION +local CASSETTE_MOTOR_OFF_DELAY_FRAMES = common_autoboot.DEFAULT_CASSETTE_MOTOR_OFF_DELAY_FRAMES + +local function boot_ninja(cassette_handler) + common_autoboot.type_at_frame(frame_num, "CLOAD\n", 200, BUTTON_PRESS_DURATION) + common_autoboot.play_cassette_at_frame(frame_num, ":cassette", 300) + + local cassette_load_done = cassette_handler(frame_num, CASSETTE_MOTOR_OFF_DELAY_FRAMES) + + if cassette_load_done then + bload_frame = frame_num + 50 + + if bload_frame == frame_num then + manager.machine.cassettes[":cassette"]:seek(0.0,"set") + emu.keypost('BLOAD "CAS:",R\n') + manager.machine.cassettes[":cassette"]:play() + end + end +end + +local function boot_default(cassette_handler) + common_autoboot.type_at_frame(frame_num, "CLOAD\n", 200, BUTTON_PRESS_DURATION) + common_autoboot.play_cassette_at_frame(frame_num, ":cassette", 300) + + local cassette_load_done = cassette_handler(frame_num, CASSETTE_MOTOR_OFF_DELAY_FRAMES) + + if cassette_load_done then + emu.keypost('RUN\n') + end +end + +-- --- Main Script Logic --- + +-- Determine the currently loaded software name +local current_software_name = manager.machine.images:at(1).filename + +-- Map software names to their respective boot functions +local boot_sequences = { + ninja = boot_ninja, + ["default"] = boot_default, +} + +-- --- Cassette Loading Handler Setup --- +local cassette_handler = common_autoboot.create_cassette_handler(":cassette") + +-- MAME machine frame notification callback +local function process_frame() + frame_num = frame_num + 1 + + -- Initial setup/info display at frame 1 + if frame_num == 1 then + emu.print_info("System Driver: " .. emu.romname()) + emu.print_info("Loaded Software Name (from image filename): " .. current_software_name) + + -- Determine which profile will be used + if boot_sequences[current_software_name] then + emu.print_info("--- Booting using game specific profile: " .. current_software_name .. " ---") + else + emu.print_info("--- Booting using default profile ---") + end + end + + -- --- Print current frame number every 100 frames --- + if frame_num % 100 == 0 then -- The modulo operator (%) gives the remainder of a division + emu.print_info("Current Frame: " .. frame_num) + end + + -- Execute the relevant boot sequence based on the loaded software + local boot_function = boot_sequences[current_software_name] + + if not boot_function then -- If specific function not found + boot_function = boot_sequences["default"] -- Assign the default function + end + + if boot_function then + boot_function(cassette_handler) + end +end + +-- Subscribe to machine frame notifications +subscription = emu.add_machine_frame_notifier(process_frame) \ No newline at end of file diff --git a/package/batocera/core/batocera-configgen/data/mame/autoboot_scripts/templates/cassette.lua b/package/batocera/core/batocera-configgen/data/mame/autoboot_scripts/templates/cassette.lua new file mode 100644 index 00000000000..09ceaca5729 --- /dev/null +++ b/package/batocera/core/batocera-configgen/data/mame/autoboot_scripts/templates/cassette.lua @@ -0,0 +1,82 @@ +-- _cass.lua + +-- Load common autoboot functions +local common_autoboot = dofile("/usr/share/batocera/configgen/data/mame/autoboot_scripts/autoboot_common.lua") + +-- Script-wide variables +local button = {} +local frame_num = 0 + +-- --- Map all buttons --- +common_autoboot.populate_buttons(button) + +-- --- Print Info --- +common_autoboot.print_image_info() +-- common_autoboot.print_buttons(button) + +-- --- Cassette Loading Handler Setup --- +local CASSETTE_SLOT = 3 +local CASSETTE_DEVICE_TAG = manager.machine.images:at(CASSETTE_SLOT).device.tag +local cassette_handler = common_autoboot.create_cassette_handler(CASSETTE_DEVICE_TAG) + + +-- --- Constants for this specific script --- +local BUTTON_PRESS_DURATION = common_autoboot.DEFAULT_BUTTON_PRESS_DURATION +local CASSETTE_MOTOR_OFF_DELAY_FRAMES = common_autoboot.DEFAULT_CASSETTE_MOTOR_OFF_DELAY_FRAMES + +local function boot_default(cassette_handler) + common_autoboot.type_at_frame(frame_num, "\n", 300) + common_autoboot.type_at_frame(frame_num, "LOAD\n", 400) + common_autoboot.play_cassette_at_frame(frame_num, CASSETTE_DEVICE_TAG, 500) + + local cassette_load_done = cassette_handler(frame_num, CASSETTE_MOTOR_OFF_DELAY_FRAMES) + + if cassette_load_done then + -- emu.keypost('RUN\n') + end +end + +-- --- Main Script Logic --- + +-- Determine the currently loaded software name +local current_software_name = manager.machine.images:at(CASSETTE_SLOT).filename + +-- Map software names to their respective boot functions +local boot_sequences = { + ['default'] = boot_default, +} + +-- MAME machine frame notification callback +local function process_frame() + frame_num = frame_num + 1 + + -- Initial setup/info display at frame 1 + if frame_num == 1 then + emu.print_info("System Driver: " .. emu.romname()) + emu.print_info("Loaded Software Name (from image filename): " .. current_software_name) + + -- Determine which profile will be used + if boot_sequences[current_software_name] then + emu.print_info("--- Booting using game specific profile: " .. current_software_name .. " ---") + else + emu.print_info("--- Booting using default profile ---") + end + end + + --- DEBUG: Print current frame number every 100 frames --- + common_autoboot.debug_frame_num(frame_num) + + -- Execute the relevant boot sequence based on the loaded software + local boot_function = boot_sequences[current_software_name] + + if not boot_function then -- If specific function not found + boot_function = boot_sequences["default"] -- Assign the default function + end + + if boot_function then + boot_function(cassette_handler) + end +end + +-- Subscribe to machine frame notifications +subscription = emu.add_machine_frame_notifier(process_frame) \ No newline at end of file diff --git a/package/batocera/core/batocera-configgen/data/mame/autoboot_scripts/tg16cd.lua b/package/batocera/core/batocera-configgen/data/mame/autoboot_scripts/tg16cd.lua new file mode 100644 index 00000000000..ea23fd41132 --- /dev/null +++ b/package/batocera/core/batocera-configgen/data/mame/autoboot_scripts/tg16cd.lua @@ -0,0 +1,26 @@ +local button = {} + +for i, j in pairs(manager.machine.ioport.ports) do + for field_name, field in pairs(j.fields) do + id = field.port.tag .. ',' .. field.mask .. ',' .. field.type + print(field_name,"->", id) + button[id] = field + end +end + + +local frame_num = 0 +local function process_frame() + frame_num = frame_num + 1 + + if frame_num == 25 then + button[":ctrl:joypad2_turbo:BUTTONS,8,46"]:set_value(1) + elseif frame_num == 30 then + button[":ctrl:joypad2_turbo:BUTTONS,8,46"]:set_value(0) + end +end + +subscription=emu.add_machine_frame_notifier(process_frame) + + + diff --git a/package/batocera/core/batocera-configgen/data/mame/autoboot_scripts/timex_cass.lua b/package/batocera/core/batocera-configgen/data/mame/autoboot_scripts/timex_cass.lua new file mode 100644 index 00000000000..64d91b03d4b --- /dev/null +++ b/package/batocera/core/batocera-configgen/data/mame/autoboot_scripts/timex_cass.lua @@ -0,0 +1,82 @@ +-- timex_cass.lua + +-- Load common autoboot functions +local common_autoboot = dofile("/usr/share/batocera/configgen/data/mame/autoboot_scripts/autoboot_common.lua") + +-- Script-wide variables +local button = {} +local frame_num = 0 + +common_autoboot.populate_buttons(button) + +-- --- Print Info --- +common_autoboot.print_buttons(button) +common_autoboot.print_image_info() + +-- --- Cassette Loading Handler Setup --- +local CASSETTE_SLOT = 3 +local CASSETTE_DEVICE_TAG = manager.machine.images:at(CASSETTE_SLOT).device.tag +local cassette_handler = common_autoboot.create_cassette_handler(CASSETTE_DEVICE_TAG) + + + +-- --- Constants for this specific script --- +local BUTTON_PRESS_DURATION = common_autoboot.DEFAULT_BUTTON_PRESS_DURATION +local CASSETTE_MOTOR_OFF_DELAY_FRAMES = common_autoboot.DEFAULT_CASSETTE_MOTOR_OFF_DELAY_FRAMES + +local function boot_default(cassette_handler) + common_autoboot.type_at_frame(frame_num, "j\n", 100) + common_autoboot.type_at_frame(frame_num, "\"\"\n", 200) + common_autoboot.play_cassette_at_frame(frame_num, CASSETTE_DEVICE_TAG, 300) + + local cassette_load_done = cassette_handler(frame_num, CASSETTE_MOTOR_OFF_DELAY_FRAMES) + + if cassette_load_done then + -- emu.keypost('RUN\n') + end +end + +-- --- Main Script Logic --- + +-- Determine the currently loaded software name +local current_software_name = manager.machine.images:at(CASSETTE_SLOT).filename + +-- Map software names to their respective boot functions +local boot_sequences = { + ['default'] = boot_default, +} + +-- MAME machine frame notification callback +local function process_frame() + frame_num = frame_num + 1 + + -- Initial setup/info display at frame 1 + if frame_num == 1 then + emu.print_info("System Driver: " .. emu.romname()) + emu.print_info("Loaded Software Name (from image filename): " .. current_software_name) + + -- Determine which profile will be used + if boot_sequences[current_software_name] then + emu.print_info("--- Booting using game specific profile: " .. current_software_name .. " ---") + else + emu.print_info("--- Booting using default profile ---") + end + end + + --- DEBUG: Print current frame number every 100 frames --- + common_autoboot.debug_frame_num(frame_num) + + -- Execute the relevant boot sequence based on the loaded software + local boot_function = boot_sequences[current_software_name] + + if not boot_function then -- If specific function not found + boot_function = boot_sequences["default"] -- Assign the default function + end + + if boot_function then + boot_function(cassette_handler) + end +end + +-- Subscribe to machine frame notifications +subscription = emu.add_machine_frame_notifier(process_frame) \ No newline at end of file diff --git a/package/batocera/core/batocera-configgen/data/mame/autoboot_scripts/to_flop.lua b/package/batocera/core/batocera-configgen/data/mame/autoboot_scripts/to_flop.lua new file mode 100644 index 00000000000..4f6caf3d924 --- /dev/null +++ b/package/batocera/core/batocera-configgen/data/mame/autoboot_scripts/to_flop.lua @@ -0,0 +1 @@ +emu.keypost('B') diff --git a/package/batocera/core/batocera-configgen/data/mame/autoboot_scripts/to_flop_to9.lua b/package/batocera/core/batocera-configgen/data/mame/autoboot_scripts/to_flop_to9.lua new file mode 100644 index 00000000000..ac100ec2652 --- /dev/null +++ b/package/batocera/core/batocera-configgen/data/mame/autoboot_scripts/to_flop_to9.lua @@ -0,0 +1 @@ +emu.keypost('D') diff --git a/package/batocera/core/batocera-configgen/data/mame/autoboot_scripts/trs80_cass.lua b/package/batocera/core/batocera-configgen/data/mame/autoboot_scripts/trs80_cass.lua new file mode 100644 index 00000000000..9e62928a321 --- /dev/null +++ b/package/batocera/core/batocera-configgen/data/mame/autoboot_scripts/trs80_cass.lua @@ -0,0 +1,326 @@ +-- trs80_cass.lua + +local zip_util = dofile("/usr/share/batocera/configgen/data/mame/autoboot_scripts/autoboot_zip.lua") + +-- ── Configuration ───────────────────────────────────────────────────────────── +local CAS_SCAN_BYTES = 4096 -- bytes to read from CAS for header search + +-- ── Tape type constants (XRoar tape.h) ──────────────────────────────────────── +local TAPE_TYPE_BASIC = 0 -- CLOAD\r + RUN\r +local TAPE_TYPE_DATA = 1 -- no autorun +local TAPE_TYPE_MACHINE = 2 -- CLOADM\r + EXEC\r + +-- ── State machine ───────────────────────────────────────────────────────────── +local STATE_IDLE = 0 +local STATE_BOOTING = 1 +local STATE_TYPING_LOAD = 2 +local STATE_WAITING = 3 +local STATE_TYPING_RUN = 4 +local STATE_DONE = 5 + +-- ── Runtime state ───────────────────────────────────────────────────────────── +local state = STATE_IDLE +local frame_counter = 0 +local tape_file_type = nil +local load_command = nil +local run_command = nil +local cmd_queue = {} +local load_start_frame = 0 + + +-- ── CAS header parser ───────────────────────────────────────────────────────── + +--- Scan a raw Lua byte-string for a valid CoCo CAS file header. +--- Mirrors XRoar's tape_file_next() / tape_autorun() in tape.c. +--- Returns the file-type byte (0/1/2) on success, nil if not found. +local function parse_cas_header(data) + if not data or #data < 20 then return nil end + + local i = 1 + local saw_leader = false + + while i <= #data do + local b = data:byte(i) + + if b == 0x55 then + saw_leader = true + i = i + 1 + + elseif b == 0x3C and saw_leader then + -- Sync byte after leader sequence → header block follows + local base = i + 1 + if base + 16 > #data then return nil end + + local block_type = data:byte(base) + local block_size = data:byte(base + 1) + + if block_type == 0x00 and block_size >= 15 then + local dstart = base + 2 + if dstart + 8 <= #data then + local file_type = data:byte(dstart + 8) + -- Log the filename (bytes 0-7 of block data) + local fname = "" + for fi = 0, 7 do + local c = data:byte(dstart + fi) + if c and c ~= 0x20 and c ~= 0x00 then + fname = fname .. string.char(c) + end + end + print(string.format( + "[CoCo Autoboot] CAS header: file='%s' type=%d", + fname, file_type)) + return file_type + end + else + -- Malformed block after sync; keep scanning + saw_leader = false + i = i + 1 + end + + else + saw_leader = false + i = i + 1 + end + end + + return nil +end + + +-- ── Cassette path discovery ─────────────────────────────────────────────────── + +--- Find the mounted cassette image filename via MAME's image API. +local function find_cassette_path() + -- First try the specific cassette device enumerator + for _, cass in pairs(manager.machine.cassettes) do + if cass.exists and cass.filename and #cass.filename > 0 then + print("[CoCo Autoboot] Cassette device: " .. cass.filename) + return cass.filename + end + end + -- Fallback: scan all image devices for .cas extension + for _, img in pairs(manager.machine.images) do + if img.exists and img.filename and #img.filename > 0 then + local fn = img.filename:lower() + if fn:find("%.cas$") then + print("[CoCo Autoboot] Image device (.cas): " .. img.filename) + return img.filename + end + end + end + return nil +end + +--- Top-level detection. +local function detect_tape_type() + print("[CoCo Autoboot] --- Tape type detection ---") + + local path = find_cassette_path() + if not path then + print("[CoCo Autoboot] No cassette image found.") + return nil + end + print("[CoCo Autoboot] Path: " .. path) + + local data = zip_util.read_bytes(path, CAS_SCAN_BYTES) + if not data then + print("[CoCo Autoboot] Could not read CAS data — defaulting to BASIC.") + return TAPE_TYPE_BASIC + end + + local ft = parse_cas_header(data) + if ft ~= nil then return ft end + + print("[CoCo Autoboot] No valid CAS header found — defaulting to BASIC.") + return TAPE_TYPE_BASIC +end + + + +-- Load common autoboot functions +local common_autoboot = dofile("/usr/share/batocera/configgen/data/mame/autoboot_scripts/autoboot_common.lua") + +-- Script-wide variables +local button = {} +local frame_num = 0 + +common_autoboot.populate_buttons(button) + +-- --- Constants for this specific script --- +local BUTTON_PRESS_DURATION = common_autoboot.DEFAULT_BUTTON_PRESS_DURATION +local CASSETTE_MOTOR_OFF_DELAY_FRAMES = common_autoboot.DEFAULT_CASSETTE_MOTOR_OFF_DELAY_FRAMES + +local function boot_default_basic(cassette_handler) + common_autoboot.type_at_frame(frame_num, "CLOAD\n", 100, BUTTON_PRESS_DURATION) + common_autoboot.play_cassette_at_frame(frame_num, ":cassette", 200) + + local cassette_load_done = cassette_handler(frame_num, CASSETTE_MOTOR_OFF_DELAY_FRAMES) + + if cassette_load_done then + emu.keypost('RUN\n') + end +end + +local function boot_default_machine(cassette_handler) + common_autoboot.type_at_frame(frame_num, "CLOADM:EXEC\n", 100, BUTTON_PRESS_DURATION) + common_autoboot.play_cassette_at_frame(frame_num, ":cassette", 200) + + local cassette_load_done = cassette_handler(frame_num, CASSETTE_MOTOR_OFF_DELAY_FRAMES) + +end + +local function create_boot_type_1_sequence(title_to_type) -- New name for clarity + return function(cassette_handler_arg) -- This is the function that process_frame will call every frame + common_autoboot.type_at_frame(frame_num, "L\n", 100) + common_autoboot.type_at_frame(frame_num, "\n", 200) + common_autoboot.type_at_frame(frame_num, "SYSTEM\n", 300) + common_autoboot.type_at_frame(frame_num, title_to_type .. "\n", 400) + common_autoboot.play_cassette_at_frame(frame_num, ":cassette", 500) + + local cassette_load_done = cassette_handler_arg(frame_num, CASSETTE_MOTOR_OFF_DELAY_FRAMES) + + if cassette_load_done then + emu.keypost('/\n') + end + end +end + +local function create_boot_type_2_sequence() -- New name for clarity + return function(cassette_handler_arg) -- This is the function that process_frame will call every frame + common_autoboot.type_at_frame(frame_num, "L\n", 100) + common_autoboot.type_at_frame(frame_num, "\n", 200) + common_autoboot.type_at_frame(frame_num, "CLOAD\n", 300) + common_autoboot.play_cassette_at_frame(frame_num, ":cassette", 400) + + local cassette_load_done = cassette_handler_arg(frame_num, CASSETTE_MOTOR_OFF_DELAY_FRAMES) + + if cassette_load_done then + emu.keypost('RUN\n') + end + end +end + +local function boot_dtrap(cassette_handler) + common_autoboot.type_at_frame(frame_num, "L\n", 100, BUTTON_PRESS_DURATION) + common_autoboot.type_at_frame(frame_num, "\n", 200, BUTTON_PRESS_DURATION) + common_autoboot.type_at_frame(frame_num, "32640\n", 300, BUTTON_PRESS_DURATION) + common_autoboot.type_at_frame(frame_num, "CLOAD\n", 400, BUTTON_PRESS_DURATION) + common_autoboot.play_cassette_at_frame(frame_num, ":cassette", 500) + + local cassette_load_done = cassette_handler(frame_num, CASSETTE_MOTOR_OFF_DELAY_FRAMES) + + if cassette_load_done then + emu.keypost('RUN\n') + end +end + +-- --- Main Script Logic --- + +-- Determine the currently loaded software name +local current_software_name = manager.machine.images:at(1).filename + +-- Map software names to their respective boot functions +local boot_sequences = { + adv03 = create_boot_type_1_sequence("MISSIO"), + adv10 = create_boot_type_1_sequence("SAVAGE"), + android = create_boot_type_2_sequence(), + baccarat = create_boot_type_2_sequence(), + backgamm = create_boot_type_2_sequence(), + blakjack = create_boot_type_2_sequence(), + chess = create_boot_type_1_sequence("SARGON"), + colliss = create_boot_type_2_sequence(), + cosmic = create_boot_type_1_sequence("COSMIC"), + craps = create_boot_type_2_sequence(), + dddd = create_boot_type_1_sequence("DANDEM"), + defense = create_boot_type_2_sequence("DEFENS"), + dtrap = boot_dtrap, + eliza = create_boot_type_1_sequence("ELIZA"), + env = create_boot_type_1_sequence("ENV"), + escape = create_boot_type_1_sequence("ESCAPE"), + galaxy1 = create_boot_type_1_sequence("GALAXY"), + galaxy2 = create_boot_type_1_sequence("GALAXY"), + headon = create_boot_type_1_sequence("HEADON"), + heliko = create_boot_type_1_sequence("HELIKO"), + hoppy = create_boot_type_1_sequence("HG"), + invaders = create_boot_type_1_sequence("INVADE"), + invasion = create_boot_type_1_sequence("INVADE"), + keno = create_boot_type_2_sequence(), + kinghill = create_boot_type_1_sequence("R"), + meteor2 = create_boot_type_1_sequence("METEOR"), + microply = create_boot_type_2_sequence(), + penetr = create_boot_type_1_sequence("PENETR"), + pinball = create_boot_type_2_sequence(), + pyrmd = create_boot_type_1_sequence("PYRMD"), + qwatson = create_boot_type_2_sequence(), + robot = create_boot_type_1_sequence("ROBOT"), + roulette = create_boot_type_2_sequence(), + scarfman = create_boot_type_1_sequence("SCARFM"), + scripsit = create_boot_type_1_sequence("SCRIPS"), + seadragon = create_boot_type_1_sequence("SEADRA"), + slot = create_boot_type_2_sequence(), + spaceinv = create_boot_type_1_sequence("INVADE"), + spcinv = create_boot_type_1_sequence("SPCINV"), + spcwarp = create_boot_type_1_sequence("SPWAR"), + starfi = create_boot_type_1_sequence("STARFI"), + starsm = create_boot_type_1_sequence("STARSM"), + startrek = create_boot_type_2_sequence(), + starwar = create_boot_type_2_sequence(), + swamp = create_boot_type_1_sequence("SWAMP"), + taipan = create_boot_type_2_sequence(), + trollcru = create_boot_type_2_sequence(), + wheel = create_boot_type_2_sequence(), + zchess = create_boot_type_1_sequence("ZCHESS"), + + ["default_basic"] = boot_default_basic, + ["default_machine"] = boot_default_machine, +} + +-- --- Cassette Loading Handler Setup --- +local cassette_handler = common_autoboot.create_cassette_handler(":cassette") + +-- MAME machine frame notification callback +local function process_frame() + frame_num = frame_num + 1 + + -- Initial setup/info display at frame 1 + if frame_num == 1 then + emu.print_info("System Driver: " .. emu.romname()) + emu.print_info("Loaded Software Name (from image filename): " .. current_software_name) + + -- Determine which profile will be used + if boot_sequences[current_software_name] then + emu.print_info("--- Booting using game specific profile: " .. current_software_name .. " ---") + else + emu.print_info("--- Booting using default profile ---") + end + + tape_file_type = detect_tape_type() + end + + -- --- Print current frame number every 100 frames --- + if frame_num % 100 == 0 then -- The modulo operator (%) gives the remainder of a division + emu.print_info("Current Frame: " .. frame_num) + end + + -- Execute the relevant boot sequence based on the loaded software + local boot_function = boot_sequences[current_software_name] + + + if not boot_function then -- If specific function not found + + if tape_file_type == TAPE_TYPE_MACHINE then + boot_function = boot_sequences["default_machine"] -- Assign the default function + elseif tape_file_type == TAPE_TYPE_BASIC then + boot_function = boot_sequences["default_basic"] -- Assign the default function + else + boot_function = boot_sequences["default_basic"] -- Assign the default function + end + end + + if boot_function then + boot_function(cassette_handler) + end +end + +-- Subscribe to machine frame notifications +subscription = emu.add_machine_frame_notifier(process_frame) diff --git a/package/batocera/core/batocera-configgen/data/mame/autoboot_scripts/trs80_flop.lua b/package/batocera/core/batocera-configgen/data/mame/autoboot_scripts/trs80_flop.lua new file mode 100644 index 00000000000..2aa82a25578 --- /dev/null +++ b/package/batocera/core/batocera-configgen/data/mame/autoboot_scripts/trs80_flop.lua @@ -0,0 +1,413 @@ +-- trs80_flop.lua +-- Autoboot for CoCo floppy disks: compute SHA1 of the disk image, +-- look up the "basic" command in coco_sha1_map.json, and type it. + +local SHA1_MAP_PATH = "/usr/share/batocera/configgen/data/mame/autoboot_scripts/coco_sha1_map.json" + +-- Load common autoboot functions +local common_autoboot = dofile("/usr/share/batocera/configgen/data/mame/autoboot_scripts/autoboot_common.lua") +local zip_util = dofile("/usr/share/batocera/configgen/data/mame/autoboot_scripts/autoboot_zip.lua") + +local frame_num = 0 +local boot_command = nil + + +-- ── Path helpers ────────────────────────────────────────────────────────────── + +local function has_os9_name_hint(path) + local lower = path:lower() + return lower:find("%(os%-9%)", 1, false) ~= nil +end + + + +-- ── SHA1 computation ────────────────────────────────────────────────────────── + +local function sha1_of_file(filepath) + local fq = filepath:gsub("'", "'\\''") + local pipe = io.popen(string.format("sha1sum '%s' 2>/dev/null", fq), "r") + if not pipe then return nil end + local line = pipe:read("*l") + pipe:close() + return line and line:match("^(%x+)") +end + +local function sha1_of_bytes(data) + local tmp = "/tmp/coco_sha1_tmp.bin" + local f = io.open(tmp, "wb") + if not f then return nil end + f:write(data) + f:close() + local pipe = io.popen("sha1sum '" .. tmp .. "' 2>/dev/null", "r") + if not pipe then os.remove(tmp); return nil end + local line = pipe:read("*l") + pipe:close() + os.remove(tmp) + return line and line:match("^(%x+)") +end + +local function compute_sha1(mame_filename) + local kind, zippath, entrypath = zip_util.parse_mame_path(mame_filename) + if kind == "zip" then + print(string.format("[CoCo Flop] ZIP: '%s' entry: '%s'", zippath, entrypath)) + local data = zip_util.read_entry_from_zip(zippath, entrypath, nil) + if not data then return nil end + return sha1_of_bytes(data) + else + return sha1_of_file(entrypath) + end +end + + +-- ── JSON lookup ─────────────────────────────────────────────────────────────── + +-- Decode a JSON string value (handles \n \r \t \" \\ escapes). +local function decode_json_string(json_str, start_pos) + local result = {} + local i = start_pos -- points to the char after the opening " + while i <= #json_str do + local c = json_str:sub(i, i) + if c == "\\" then + i = i + 1 + local e = json_str:sub(i, i) + if e == "n" then table.insert(result, "\n") + elseif e == "r" then table.insert(result, "\r") + elseif e == "t" then table.insert(result, "\t") + elseif e == '"' then table.insert(result, '"') + elseif e == "\\" then table.insert(result, "\\") + else table.insert(result, e) + end + elseif c == '"' then + break -- end of JSON string + else + table.insert(result, c) + end + i = i + 1 + end + return table.concat(result) +end + +-- Extract the value of a JSON key from a small JSON object string. +local function json_get_string(block, key) + local search = '"' .. key:gsub("([%(%)%.%%%+%-%*%?%[%^%$])", "%%%1") .. '"' + local kpos = block:find(search, 1, true) + if not kpos then return nil end + -- skip past key, optional whitespace, colon, optional whitespace, opening quote + local qpos = block:find('"', kpos + #search + 1) -- +1 for the colon + if not qpos then return nil end + return decode_json_string(block, qpos + 1) +end + +-- Load coco_sha1_map.json and return the "basic" field for the given sha1, or nil. +local function lookup_sha1(sha1) + local f = io.open(SHA1_MAP_PATH, "r") + if not f then + print("[CoCo Flop] Cannot open SHA1 map: " .. SHA1_MAP_PATH) + return nil + end + local content = f:read("*a") + f:close() + + local key_start = content:find('"' .. sha1 .. '"', 1, true) + if not key_start then + print("[CoCo Flop] SHA1 not in map: " .. sha1) + return nil + end + + -- Extract the balanced {} block for this entry + local brace_start = content:find("{", key_start) + if not brace_start then return nil end + local depth, i = 0, brace_start + while i <= #content do + local c = content:sub(i, i) + if c == "{" then depth = depth + 1 + elseif c == "}" then + depth = depth - 1 + if depth == 0 then + return json_get_string(content:sub(brace_start, i), "basic") + end + end + i = i + 1 + end + return nil +end + + +-- ── Floppy image discovery ──────────────────────────────────────────────────── + +local function find_dsk_in_zip(zippath) + return zip_util.find_zip_entry(zippath, function(n) return n:lower():match("%.dsk$") end) +end + +local function find_floppy_path() + for _, img in pairs(manager.machine.images) do + if img.exists and img.filename and #img.filename > 0 then + local fn = img.filename:lower() + if fn:find("%.dsk") or fn:find("%.dmk") or fn:find("%.vdk") then + print("[CoCo Flop] Floppy image: " .. img.filename) + return img.filename + end + -- Bare .zip: resolve the DSK entry name and return a virtual path + if fn:match("%.zip$") then + local dsk = find_dsk_in_zip(img.filename) + if dsk then + local vpath = img.filename .. "/" .. dsk + print("[CoCo Flop] Floppy image (zip): " .. vpath) + return vpath + end + end + end + end + return nil +end + + +-- ── RSDOS directory parser ──────────────────────────────────────────────────── +-- Standard CoCo JVC disk: 35 tracks, 18 sectors/track, 256 bytes/sector. +-- Directory starts at track 17 (0-based), sector index 2 (sector 3, 1-based). +-- Each 32-byte entry: bytes 0-7 name, 8-10 extension, 11 file type +-- type 0=BASIC tokenized type 1=data type 2=machine code type 3=ASCII BASIC + +local RSDOS_DIR_OFFSET = (17 * 18 + 2) * 256 -- 78848 + +-- Detect OS-9 disk via LSN0 (track 0, sector 1, offset 0) field values. +-- OS-9 stores DD.TKS (sectors/track) at byte 3; CoCo standard = 0x12 (18). +-- DD.TOT (total sectors, 3 bytes big-endian) at bytes 0-2 is > 0 and byte 0 = 0x00. +-- RSDOS disks have 0xFF at byte 0 (blank sector) so this is unambiguous. +-- A dual-format disk may have both OS-9 LSN0 and a valid RSDOS directory; +-- RSDOS is preferred in that case (BASIC boots first on a dual-format disk). +local function is_os9_disk(disk_data) + if #disk_data < 4 then return false end + local b0 = disk_data:byte(1) -- DD.TOT high byte (0x00 for any sane floppy) + local tks = disk_data:byte(4) -- DD.TKS = sectors per track + return b0 == 0x00 and tks == 0x12 +end + +local function parse_rsdos_dir_data(data) + local all_ff = true -- every slot is 0xFF (no directory at all) + local first_cmd = nil -- first runnable entry found + local boot_cmd = nil -- entry named BOOT (takes priority) + + for idx = 0, 71 do + local off = idx * 32 + 1 -- 1-based Lua index + if off + 31 > #data then break end + + local first = data:byte(off) + if first == 0x00 then + all_ff = false + + -- Some real-world images contain 0x00-prefixed garbage/deleted slots + -- before later valid entries. Only treat an all-zero slot as true + -- end-of-directory; otherwise skip and keep scanning. + local all_zero = true + for i = 1, 31 do + if data:byte(off + i) ~= 0x00 then + all_zero = false + break + end + end + if all_zero then + break + end + + print(string.format("[CoCo Flop] Dir[%d]: skipping 0x00-prefixed slot", idx)) + goto continue + end + + if first ~= 0xFF then + all_ff = false + + -- Non-ASCII first byte: garbled entry (OS-9 data, copy-protection, etc.) — skip it. + if first > 0x7E or first < 0x20 then + print(string.format("[CoCo Flop] Dir[%d]: skipping garbled entry (first=0x%02x)", idx, first)) + goto continue + end + + -- Collect all 8 name bytes up to NUL, then rtrim spaces. + -- Mid-name spaces are valid (e.g. "3D GHOST"); only trailing padding stripped. + local name = {} + local garbled = false + for i = 0, 7 do + local c = data:byte(off + i) + if c == 0x00 then break end + if c > 0x7E then garbled = true; break end + table.insert(name, string.char(c)) + end + if garbled then + print(string.format("[CoCo Flop] Dir[%d]: skipping garbled name", idx)) + goto continue + end + local name_str = table.concat(name):match("^(.-)%s*$") + + local ext = {} + local ext_garbled = false + for i = 8, 10 do + local c = data:byte(off + i) + if c == 0x00 then break end + -- Extension must be alphanumeric or space; anything else is OS-9 data + if not ((c >= 0x30 and c <= 0x39) or (c >= 0x41 and c <= 0x5A) + or (c >= 0x61 and c <= 0x7A) or c == 0x20) then + ext_garbled = true; break + end + table.insert(ext, string.char(c)) + end + if ext_garbled then + print(string.format("[CoCo Flop] Dir[%d]: skipping garbled extension", idx)) + goto continue + end + local ext_str = table.concat(ext):match("^(.-)%s*$") + + local file_type = data:byte(off + 11) + + -- Valid RSDOS file types: 0=BASIC tokenized, 1=data, 2=machine code, 3=ASCII BASIC. + -- Any other value means this is not a real RSDOS directory entry. + if file_type > 3 then + print(string.format("[CoCo Flop] Dir[%d]: skipping invalid ftype=%d", idx, file_type)) + goto continue + end + + if #name_str > 0 and file_type ~= 1 then + -- Infocom Z-machine disks store the interpreter as GAME.BIN but boot via DOS + if name_str == "GAME" and ext_str == "BIN" and file_type == 2 then + print("[CoCo Flop] GAME.BIN detected → DOS") + return "DOS\n" + end + + local cmd + if file_type == 0 or file_type == 3 or ext_str == "BAS" then + cmd = 'RUN "' .. name_str .. '"\n' + else + cmd = 'LOADM "' .. name_str .. '":EXEC\n' + end + + print(string.format( + "[CoCo Flop] Dir[%d]: '%s.%s' type=%d cmd=%s", + idx, name_str, ext_str, file_type, cmd:gsub("\n", "\\n"))) + + if name_str == "BOOT" then + boot_cmd = cmd + elseif first_cmd == nil then + first_cmd = cmd + end + end + end + ::continue:: + end + + -- All 72 slots were 0xFF: no RSDOS directory — likely a bootable disk (e.g. Infocom Z-machine) + if all_ff then + print("[CoCo Flop] All-0xFF directory: bootable disk → DOS") + return "DOS\n" + end + + local cmd = boot_cmd or first_cmd + if cmd then + if boot_cmd then + print("[CoCo Flop] Using BOOT entry: " .. cmd:gsub("\n", "\\n")) + end + return cmd + end + + print("[CoCo Flop] No runnable program found in disk directory.") + return nil +end + +local function read_disk_dir_command(mame_filename) + local kind, zippath, entrypath = zip_util.parse_mame_path(mame_filename) + local disk_data, dir_data + + if (kind == "zip" and zippath and has_os9_name_hint(zippath)) or has_os9_name_hint(entrypath) then + print("[CoCo Flop] OS-9 filename hint detected → DOS") + return "DOS\n" + end + + if kind == "zip" then + disk_data = zip_util.read_entry_from_zip(zippath, entrypath, nil) + if disk_data and #disk_data > RSDOS_DIR_OFFSET then + dir_data = disk_data:sub(RSDOS_DIR_OFFSET + 1, RSDOS_DIR_OFFSET + 72 * 32) + end + else + local f = io.open(entrypath, "rb") + if not f then + print("[CoCo Flop] Cannot open disk: " .. entrypath) + return nil + end + disk_data = f:read("*a") + f:close() + if disk_data and #disk_data > RSDOS_DIR_OFFSET then + dir_data = disk_data:sub(RSDOS_DIR_OFFSET + 1, RSDOS_DIR_OFFSET + 72 * 32) + end + end + + if not dir_data or #dir_data == 0 then + print("[CoCo Flop] Could not read directory sector from disk.") + return nil + end + + local cmd = parse_rsdos_dir_data(dir_data) + if cmd then return cmd end + + -- No valid RSDOS directory found — check OS-9 sector at LSN0 + if disk_data and is_os9_disk(disk_data) then + print("[CoCo Flop] OS-9 disk signature detected → DOS") + return "DOS\n" + end + + return nil +end + + +-- ── Boot command detection ──────────────────────────────────────────────────── + +local function detect_boot_command() + print("[CoCo Flop] --- Boot detection ---") + + local path = find_floppy_path() + if not path then + print("[CoCo Flop] No floppy image found.") + return nil + end + print("[CoCo Flop] Path: " .. path) + + local sha1 = compute_sha1(path) + if sha1 then + print("[CoCo Flop] SHA1: " .. sha1) + local cmd = lookup_sha1(sha1) + if cmd then + print("[CoCo Flop] SHA1 match: " .. cmd:gsub("\n", "\\n"):gsub("\r", "\\r")) + return cmd + end + print("[CoCo Flop] Not in SHA1 map — parsing disk directory.") + else + print("[CoCo Flop] Could not compute SHA1 — parsing disk directory.") + end + + return read_disk_dir_command(path) +end + + +-- ── Main frame callback ─────────────────────────────────────────────────────── + +local function process_frame() + frame_num = frame_num + 1 + + if frame_num == 1 then + emu.print_info("System Driver: " .. emu.romname()) + boot_command = detect_boot_command() + if boot_command then + emu.print_info("[CoCo Flop] Autoboot: " .. boot_command:gsub("\n", "\\n")) + else + emu.print_info("[CoCo Flop] No autoboot.") + end + end + + if frame_num % 100 == 0 then + emu.print_info("Current Frame: " .. frame_num) + end + + if boot_command then + common_autoboot.type_at_frame(frame_num, boot_command, 150) + end +end + +subscription = emu.add_machine_frame_notifier(process_frame) diff --git a/package/batocera/core/batocera-configgen/data/mame/autoboot_scripts/tvc_cass.lua b/package/batocera/core/batocera-configgen/data/mame/autoboot_scripts/tvc_cass.lua new file mode 100644 index 00000000000..76b51990b29 --- /dev/null +++ b/package/batocera/core/batocera-configgen/data/mame/autoboot_scripts/tvc_cass.lua @@ -0,0 +1,78 @@ +-- tvc_cass.lua + +-- Load common autoboot functions +local common_autoboot = dofile("/usr/share/batocera/configgen/data/mame/autoboot_scripts/autoboot_common.lua") + +-- Script-wide variables +local button = {} +local frame_num = 0 + +-- --- Cassette Loading Handler Setup --- +local CASSETTE_SLOT = 3 +local CASSETTE_DEVICE_TAG = manager.machine.images:at(CASSETTE_SLOT).device.tag +local cassette_handler = common_autoboot.create_cassette_handler(CASSETTE_DEVICE_TAG) + +common_autoboot.populate_buttons(button) + +common_autoboot.print_image_info() + +-- --- Constants for this specific script --- +local BUTTON_PRESS_DURATION = common_autoboot.DEFAULT_BUTTON_PRESS_DURATION +local CASSETTE_MOTOR_OFF_DELAY_FRAMES = common_autoboot.DEFAULT_CASSETTE_MOTOR_OFF_DELAY_FRAMES + +local function boot_default(cassette_handler) + common_autoboot.type_at_frame(frame_num, "\n", 300) + common_autoboot.type_at_frame(frame_num, "LOAD\n", 400) + common_autoboot.play_cassette_at_frame(frame_num, CASSETTE_DEVICE_TAG, 500) + + local cassette_load_done = cassette_handler(frame_num, CASSETTE_MOTOR_OFF_DELAY_FRAMES) + + if cassette_load_done then + -- emu.keypost('RUN\n') + end +end + +-- --- Main Script Logic --- + +-- Determine the currently loaded software name +local current_software_name = manager.machine.images:at(CASSETTE_SLOT).filename + +-- Map software names to their respective boot functions +local boot_sequences = { + ['default'] = boot_default, +} + +-- MAME machine frame notification callback +local function process_frame() + frame_num = frame_num + 1 + + -- Initial setup/info display at frame 1 + if frame_num == 1 then + emu.print_info("System Driver: " .. emu.romname()) + emu.print_info("Loaded Software Name (from image filename): " .. current_software_name) + + -- Determine which profile will be used + if boot_sequences[current_software_name] then + emu.print_info("--- Booting using game specific profile: " .. current_software_name .. " ---") + else + emu.print_info("--- Booting using default profile ---") + end + end + + --- DEBUG: Print current frame number every 100 frames --- + common_autoboot.debug_frame_num(frame_num) + + -- Execute the relevant boot sequence based on the loaded software + local boot_function = boot_sequences[current_software_name] + + if not boot_function then -- If specific function not found + boot_function = boot_sequences["default"] -- Assign the default function + end + + if boot_function then + boot_function(cassette_handler) + end +end + +-- Subscribe to machine frame notifications +subscription = emu.add_machine_frame_notifier(process_frame) \ No newline at end of file diff --git a/package/batocera/core/batocera-configgen/data/mame/autoboot_scripts/tvc_flop.lua b/package/batocera/core/batocera-configgen/data/mame/autoboot_scripts/tvc_flop.lua new file mode 100644 index 00000000000..e6696e9f2f9 --- /dev/null +++ b/package/batocera/core/batocera-configgen/data/mame/autoboot_scripts/tvc_flop.lua @@ -0,0 +1,71 @@ +-- tvc_flop.lua +-- +-- TVC floppy autoboot (HBF expansion). +-- +-- From MAME softlist notes: +-- Most titles can be run in BASIC with LOAD"*" +-- If the program does not start automatically, follow with RUN +-- +-- Boot sequence: +-- 1. Clear any initial prompt / splash (Enter at frame 300) +-- 2. Type LOAD"*" to load the first program from the floppy +-- 3. Wait a fixed delay for the disk to finish loading +-- 4. Type RUN to start the program + +local common_autoboot = dofile("/usr/share/batocera/configgen/data/mame/autoboot_scripts/autoboot_common.lua") + +local button = {} +local frame_num = 0 + +common_autoboot.populate_buttons(button) +common_autoboot.print_image_info() + +local BUTTON_PRESS_DURATION = common_autoboot.DEFAULT_BUTTON_PRESS_DURATION + +-- With the HBF expansion active the image order is: +-- 1: printout 2: cartridge 3: floppydisk1 4: floppydisk2 5: cassette 6: quickload +local FLOP_SLOT = 3 + +-- Frames to wait after issuing LOAD"*" before sending RUN. +-- TVC floppy loads at ~300 RPM; a typical game takes up to ~20 s. +-- 1200 frames @ 50 fps = 24 s, which covers the vast majority of titles. +local FLOPPY_LOAD_DELAY_FRAMES = 1200 + +local LOAD_START_FRAME = 400 +local RUN_FRAME = LOAD_START_FRAME + FLOPPY_LOAD_DELAY_FRAMES + +local current_software_name = manager.machine.images:at(FLOP_SLOT).filename + +local function boot_default() + common_autoboot.type_at_frame(frame_num, "\n", 300, BUTTON_PRESS_DURATION) + common_autoboot.type_at_frame(frame_num, 'LOAD"*"\n', LOAD_START_FRAME, BUTTON_PRESS_DURATION) + common_autoboot.type_at_frame(frame_num, "RUN\n", RUN_FRAME, BUTTON_PRESS_DURATION) +end + +local boot_sequences = { + ['default'] = boot_default, +} + +local function process_frame() + frame_num = frame_num + 1 + + if frame_num == 1 then + emu.print_info("System Driver: " .. emu.romname()) + emu.print_info("Loaded Software Name: " .. current_software_name) + + if boot_sequences[current_software_name] then + emu.print_info("--- Booting using game-specific profile: " .. current_software_name .. " ---") + else + emu.print_info("--- Booting using default profile ---") + end + end + + common_autoboot.debug_frame_num(frame_num) + + local boot_function = boot_sequences[current_software_name] or boot_sequences["default"] + if boot_function then + boot_function() + end +end + +subscription = emu.add_machine_frame_notifier(process_frame) diff --git a/package/batocera/core/batocera-configgen/data/mame/autoboot_scripts/vector06_flop.lua b/package/batocera/core/batocera-configgen/data/mame/autoboot_scripts/vector06_flop.lua new file mode 100644 index 00000000000..9cb2af7f6b5 --- /dev/null +++ b/package/batocera/core/batocera-configgen/data/mame/autoboot_scripts/vector06_flop.lua @@ -0,0 +1,101 @@ +-- vector06_flop.lua + +-- Load common autoboot functions +local common_autoboot = dofile("/usr/share/batocera/configgen/data/mame/autoboot_scripts/autoboot_common.lua") + +-- Script-wide variables +local button = {} +local frame_num = 0 + +-- Populate the 'button' table using the common function +common_autoboot.populate_buttons(button) + +-- Print the button table contents for debugging +common_autoboot.print_buttons(button) + +-- --- Constants for this specific script --- +local BUTTON_PRESS_DURATION = common_autoboot.DEFAULT_BUTTON_PRESS_DURATION + +-- --- Game-Specific Boot Sequence Functions --- + +-- Boot sequence for "komrab" software +local function boot_komrab() + common_autoboot.press_and_release(button, frame_num, "AP2", 100, BUTTON_PRESS_DURATION) + common_autoboot.type_at_frame(frame_num, "KOMRAB\n\n", 300) +end + +-- Boot sequence for "cdpacman" software +local function boot_cdpacman() + common_autoboot.press_and_release(button, frame_num, "AP2", 100, BUTTON_PRESS_DURATION) + common_autoboot.press_and_release(button, frame_num, "Rus/Lat", 300, BUTTON_PRESS_DURATION) + common_autoboot.press_and_release(button, frame_num, "2 \"", 600, BUTTON_PRESS_DURATION) +end + +-- Boot sequence for "card" software +local function boot_card() + common_autoboot.press_and_release(button, frame_num, "AP2", 100, BUTTON_PRESS_DURATION) + common_autoboot.press_and_release(button, frame_num, "Space", 300, BUTTON_PRESS_DURATION) + common_autoboot.press_and_release(button, frame_num, "Enter", 600, BUTTON_PRESS_DURATION) +end + +local function boot_gt() + common_autoboot.press_and_release(button, frame_num, "AP2", 100, BUTTON_PRESS_DURATION) + common_autoboot.type_at_frame(frame_num, "GT\n", 800, BUTTON_PRESS_DURATION) + common_autoboot.type_at_frame(frame_num, "Y\n", 1400, BUTTON_PRESS_DURATION) +end + +-- Default boot sequence for software that do not have specific boot sequences +local function boot_default() + common_autoboot.press_and_release(button, frame_num, "AP2", 100, BUTTON_PRESS_DURATION) +end + +-- --- Main Script Logic --- + +-- Determine the currently loaded software name +local current_software_name = manager.machine.images:at(2).filename + +-- Map software names to their respective boot functions +local boot_sequences = { + komrab = boot_komrab, + cdpacman = boot_cdpacman, + card = boot_card, + gt = boot_gt, + ["default"] = boot_default +} + +-- MAME machine frame notification callback +local function process_frame() + frame_num = frame_num + 1 + + -- Initial setup/info display at frame 1 + if frame_num == 1 then + emu.print_info("System Driver: " .. emu.romname()) + emu.print_info("Loaded Software Name (from image filename): " .. current_software_name) + + -- Determine which profile will be used + if boot_sequences[current_software_name] then + emu.print_info("--- Booting using game specific profile: " .. current_software_name .. " ---") + else + emu.print_info("--- Booting using default profile ---") + end + end + + -- --- Print current frame number every 100 frames --- + if frame_num % 100 == 0 then -- The modulo operator (%) gives the remainder of a division + emu.print_info("Current Frame: " .. frame_num) + end + + -- Execute the relevant boot sequence based on the loaded software + local boot_function = boot_sequences[current_software_name] + + if not boot_function then -- If specific function not found + boot_function = boot_sequences["default"] -- Assign the default function + end + + if boot_function then + boot_function() + end +end + +-- Subscribe to machine frame notifications +subscription = emu.add_machine_frame_notifier(process_frame) \ No newline at end of file diff --git a/package/batocera/core/batocera-configgen/data/mame/autoboot_scripts/vg5k.lua b/package/batocera/core/batocera-configgen/data/mame/autoboot_scripts/vg5k.lua new file mode 100644 index 00000000000..ccd3358628b --- /dev/null +++ b/package/batocera/core/batocera-configgen/data/mame/autoboot_scripts/vg5k.lua @@ -0,0 +1,72 @@ +-- vg5k.lua + +-- Load common autoboot functions +local common_autoboot = dofile("/usr/share/batocera/configgen/data/mame/autoboot_scripts/autoboot_common.lua") + +-- Script-wide variables +local button = {} +local frame_num = 0 + +common_autoboot.populate_buttons(button) + +-- --- Constants for this specific script --- +local BUTTON_PRESS_DURATION = common_autoboot.DEFAULT_BUTTON_PRESS_DURATION +local CASSETTE_MOTOR_OFF_DELAY_FRAMES = common_autoboot.DEFAULT_CASSETTE_MOTOR_OFF_DELAY_FRAMES + +-- --- Main Script Logic --- + +-- Determine the currently loaded software name +local current_software_name = manager.machine.images:at(1).filename + +-- Map software names to their respective boot functions +local boot_sequences = { +} + +-- --- Cassette Loading Handler Setup --- +local cassette_handler = common_autoboot.create_cassette_handler(":cassette") + +-- MAME machine frame notification callback +local function process_frame() + frame_num = frame_num + 1 + + -- Initial setup/info display at frame 1 + if frame_num == 1 then + emu.print_info("System Driver: " .. emu.romname()) + emu.print_info("Loaded Software Name (from image filename): " .. current_software_name) + + -- Determine which profile will be used + if boot_sequences[current_software_name] then + emu.print_info("--- Booting using game specific profile: " .. current_software_name .. " ---") + else + emu.print_info("--- Booting using default profile ---") + end + end + + common_autoboot.type_at_frame(frame_num, "CLOAD\n", 100, BUTTON_PRESS_DURATION) + common_autoboot.play_cassette_at_frame(frame_num, ":cassette", 200) + + local cassette_load_done = cassette_handler(frame_num, CASSETTE_MOTOR_OFF_DELAY_FRAMES) + + if cassette_load_done then + emu.keypost('RUN\n') + end + + -- --- Print current frame number every 100 frames --- + if frame_num % 100 == 0 then -- The modulo operator (%) gives the remainder of a division + emu.print_info("Current Frame: " .. frame_num) + end + + -- Execute the relevant boot sequence based on the loaded software + local boot_function = boot_sequences[current_software_name] + + if not boot_function then -- If specific function not found + boot_function = boot_sequences["default"] -- Assign the default function + end + + if boot_function then + boot_function() + end +end + +-- Subscribe to machine frame notifications +subscription = emu.add_machine_frame_notifier(process_frame) \ No newline at end of file diff --git a/package/batocera/core/batocera-configgen/data/mame/autoboot_scripts/vtech2_cass.lua b/package/batocera/core/batocera-configgen/data/mame/autoboot_scripts/vtech2_cass.lua new file mode 100644 index 00000000000..52479893286 --- /dev/null +++ b/package/batocera/core/batocera-configgen/data/mame/autoboot_scripts/vtech2_cass.lua @@ -0,0 +1,79 @@ +-- vtech2_cass.lua + +-- Load common autoboot functions +local common_autoboot = dofile("/usr/share/batocera/configgen/data/mame/autoboot_scripts/autoboot_common.lua") + +-- Script-wide variables +local button = {} +local frame_num = 0 + +common_autoboot.populate_buttons(button) + +-- Print the button table contents for debugging +-- common_autoboot.print_buttons(button) + +-- --- Constants for this specific script --- +local BUTTON_PRESS_DURATION = common_autoboot.DEFAULT_BUTTON_PRESS_DURATION +local CASSETTE_MOTOR_OFF_DELAY_FRAMES = common_autoboot.DEFAULT_CASSETTE_MOTOR_OFF_DELAY_FRAMES + +local function boot_default(cassette_handler) + common_autoboot.type_at_frame(frame_num, "CLOAD\n", 100, BUTTON_PRESS_DURATION) + + common_autoboot.play_cassette_at_frame(frame_num, ":cassette", 400) -- need to wait at least 300 frames after CLOAD cmd, otherwise will have LOADING ERROR! + + local cassette_load_done = cassette_handler(frame_num, CASSETTE_MOTOR_OFF_DELAY_FRAMES) + + if cassette_load_done then + emu.keypost('RUN\n') + end +end + +-- --- Main Script Logic --- + +-- Determine the currently loaded software name +local current_software_name = manager.machine.images:at(1).filename + +-- Map software names to their respective boot functions +local boot_sequences = { + ["default"] = boot_default, +} + +-- --- Cassette Loading Handler Setup --- +local cassette_handler = common_autoboot.create_cassette_handler(":cassette") + +-- MAME machine frame notification callback +local function process_frame() + frame_num = frame_num + 1 + + -- Initial setup/info display at frame 1 + if frame_num == 1 then + emu.print_info("System Driver: " .. emu.romname()) + emu.print_info("Loaded Software Name (from image filename): " .. current_software_name) + + -- Determine which profile will be used + if boot_sequences[current_software_name] then + emu.print_info("--- Booting using game specific profile: " .. current_software_name .. " ---") + else + emu.print_info("--- Booting using default profile ---") + end + end + + -- --- Print current frame number every 100 frames --- + if frame_num % 100 == 0 then -- The modulo operator (%) gives the remainder of a division + emu.print_info("Current Frame: " .. frame_num) + end + + -- Execute the relevant boot sequence based on the loaded software + local boot_function = boot_sequences[current_software_name] + + if not boot_function then -- If specific function not found + boot_function = boot_sequences["default"] -- Assign the default function + end + + if boot_function then + boot_function(cassette_handler) + end +end + +-- Subscribe to machine frame notifications +subscription = emu.add_machine_frame_notifier(process_frame) \ No newline at end of file diff --git a/package/batocera/core/batocera-configgen/data/mame/autoboot_scripts/zx81_cass.lua b/package/batocera/core/batocera-configgen/data/mame/autoboot_scripts/zx81_cass.lua new file mode 100644 index 00000000000..7a1e0442b1f --- /dev/null +++ b/package/batocera/core/batocera-configgen/data/mame/autoboot_scripts/zx81_cass.lua @@ -0,0 +1,48 @@ +button = {} + +for i, j in pairs(manager.machine.ioport.ports) do + for field_name, field in pairs(j.fields) do + id = field.port.tag .. ',' .. field.mask .. ',' .. field.type + print(field_name,": ", id) + button[id] = field + end +end + +local frame_num = 0 +local function process_frame() + frame_num = frame_num + 1 + + if frame_num == 200 then + button[":ROW6,8,49"]:set_value(1) -- J + elseif frame_num == 205 then + button[":ROW6,8,49"]:set_value(0) -- J + elseif frame_num == 215 then + button[":ROW0,1,49"]:set_value(1) -- shift + button[":ROW5,1,49"]:set_value(1) -- P + elseif frame_num == 220 then + button[":ROW0,1,49"]:set_value(0) -- shift + button[":ROW5,1,49"]:set_value(0) -- P + elseif frame_num == 230 then + button[":ROW0,1,49"]:set_value(1) -- shift + button[":ROW5,1,49"]:set_value(1) -- P + elseif frame_num == 235 then + button[":ROW0,1,49"]:set_value(0) -- shift + button[":ROW5,1,49"]:set_value(0) -- P + elseif frame_num == 245 then + button[":ROW6,1,49"]:set_value(1) -- enter + elseif frame_num == 250 then + button[":ROW6,1,49"]:set_value(0) -- enter + elseif frame_num == 300 then + manager.machine.cassettes[":cassette"]:play() + end + + if manager.machine.cassettes[":cassette"].is_stopped == false and manager.machine.cassettes[":cassette"].motor_state == true then + manager.machine.video.throttled = false + manager.machine.video.frameskip = 12 + else + manager.machine.video.throttled = true + manager.machine.video.frameskip = 0 + end +end + +subscription=emu.add_machine_frame_notifier(process_frame) diff --git a/package/batocera/core/batocera-configgen/data/mame/camplynx_cass_autoload.csv b/package/batocera/core/batocera-configgen/data/mame/camplynx_cass_autoload.csv deleted file mode 100644 index 68c916bb9e0..00000000000 --- a/package/batocera/core/batocera-configgen/data/mame/camplynx_cass_autoload.csv +++ /dev/null @@ -1,66 +0,0 @@ -3dmoncrz;MLOAD "3D MONSTER" -backgmmn;LOAD "BACKGAMMON" -battlbrk;LOAD "BATTLEBRICK" -centiped;MLOAD "CENTIPEDE" -colossal;LOAD "COLOSSAL" -dambustr;LOAD "DAM BUSTER" -deathball;LOAD "DEATHBALL" -diggerman;MLOAD "DIGGERMAN" -dungeon;LOAD "DUNGEON" -floydbank;MLOAD "FLOYDS BANK" -gamepak4;MLOAD "GEMPACK 4" -gobblspk;MLOAD "SPOOK" -hangman;LOAD "HANGMAN" -labyrinth;LOAD "LABYRINTHE" -logichess;MLOAD "ECHECS" -invaders;MLOAD "INVADERS" -mastermnd;LOAD "MASTERMIND" -mazeman;MLOAD "MAZEMAN" -minedout;LOAD "MINEDOUTGOOD" -moonfallf;LOAD "Moonfall2" -moonfall;LOAD "MOONFALL" -muncher;LOAD "MUNCHER" -nuclear;LOAD "NUCLEAR" -numerons;LOAD "NUMERONS" -asterix;LOAD "ASTERIX" -ohmummy;MLOAD "OH MUMMY" -panik;LOAD "PANIK" -pengo;MLOAD "PENGO" -pwrblastr;MLOAD "POWER BLASTER" -racer;LOAD "RACER" -rocketman;LOAD "ROCKETMAN" -spellbnd;LOAD "SPELLBOUND" -scrablynx;LOAD "SCRABLYNX" -siege;MLOAD "SIEGE ATTACK" -spactrek;LOAD "TREK" -sairraid;LOAD "SUPER AIR RAID" -treasisld;LOAD "TREASURE" -twinkle;MLOAD "TWINKLE" -worm;LOAD "THE WORM" -wormf;LOAD "THE WORM" -ynxvader;MLOAD "YNXVADERS" -zombie;LOAD "ZombiePanic" -aide;LOAD "AIDE" -cardindx;LOAD "CARD INDEX" -composer;LOAD "COMPOSER" -disassmbf;LOAD "DES.IMP" -disassmb;LOAD "DESASS" -genbasic;LOAD "GENEBASIC" -gencarac;LOAD "GENECAR" -wordproc;MLOAD "LIONTEXT" -moder80;MLOAD "CODE" -musicmstr;LOAD "MUSIC MASTER" -6845p;LOAD "6845P" -chopin;LOAD "CHOPIN" -cinema;LOAD "CINEMA" -forest;LOAD "THE FOREST" -gridtrap;LOAD "GRIDTRAP" -hilo;LOAD "CARDS" -inteltab;LOAD "INTELTABLYNX" -maximots;LOAD "MAXI-MOTS" -planets;LOAD "PLANETS" -risingmn;LOAD "CP FP" -scrndmp;LOAD "RPENROSE" -starover;LOAD "SR" -triangle;LOAD "BRUMBELOW" -tronblkr;LOAD "TRON BLOCKER" \ No newline at end of file diff --git a/package/batocera/core/batocera-configgen/data/mame/coco_archive_fetch.py b/package/batocera/core/batocera-configgen/data/mame/coco_archive_fetch.py new file mode 100644 index 00000000000..9d1f5866ac0 --- /dev/null +++ b/package/batocera/core/batocera-configgen/data/mame/coco_archive_fetch.py @@ -0,0 +1,223 @@ +#!/usr/bin/env python3 +""" +Fetch xroar_config from colorcomputerarchive.com pages, download each zip, +compute SHA1 of the disk0 file inside, and produce a JSON indexed by SHA1. +""" + +import argparse +import hashlib +import io +import json +import re +import sys +import urllib.parse +import urllib.request +import zipfile +from concurrent.futures import ThreadPoolExecutor, as_completed +from pathlib import Path + +URLS = [ + "https://colorcomputerarchive.com/repo/Disks/Games/", + "https://colorcomputerarchive.com/repo/Disks/Games/French/", + "https://colorcomputerarchive.com/repo/Disks/Games/French/Alice%20Software/", + "https://colorcomputerarchive.com/repo/Disks/Games/Infocom%20Adventures/", + "https://colorcomputerarchive.com/repo/Disks/Games/Infocom%20Adventures/64%20column%20(Coco%203)/", + "https://colorcomputerarchive.com/repo/Disks/Games/Inufuto/", + "https://colorcomputerarchive.com/repo/Disks/Games/Portuguese/", + "https://colorcomputerarchive.com/repo/Disks/Games/Scott%20Adams%20Adventures/", + "https://colorcomputerarchive.com/repo/Disks/Games/Sierra%20Games/" + # "https://colorcomputerarchive.com/repo/Disks/Applications/", +] + +OUTPUT = Path("coco_sha1_map.json") +CACHE_DIR = Path("coco_zip_cache") + +WORKERS = 8 # parallel downloads + + +def fetch_text(url: str) -> str: + req = urllib.request.Request(url, headers={"User-Agent": "Mozilla/5.0"}) + with urllib.request.urlopen(req, timeout=30) as resp: + return resp.read().decode("utf-8", errors="replace") + + +def download_zip(url: str, dest: Path) -> None: + req = urllib.request.Request(url, headers={"User-Agent": "Mozilla/5.0"}) + with urllib.request.urlopen(req, timeout=60) as resp: + dest.write_bytes(resp.read()) + + +def sha1_of_bytes(data: bytes) -> str: + return hashlib.sha1(data).hexdigest() + + +def extract_xroar_config(html: str) -> list[dict]: + match = re.search(r"xroar_config\s*=\s*(\{)", html) + if not match: + raise ValueError("xroar_config not found in page") + data, _ = json.JSONDecoder().raw_decode(html, match.start(1)) + files = data.get("files", []) + if not isinstance(files, list): + raise ValueError(f"xroar_config.files is not a list: {type(files)}") + return files + + +def decode_basic(value: str) -> str: + return value.replace("%5Cr", "\n").replace("%5cr", "\n").replace("%22", '"') + + +def process_entry(base_url: str, entry: dict) -> list[tuple[str, dict]]: + """Download zip, compute SHA1 for every DSK inside, return list of (sha1, entry).""" + zip_filename = entry.get("filename") + if not zip_filename or not entry.get("disk0"): + return [] + + zip_url = base_url + urllib.parse.quote(zip_filename) + zip_path = CACHE_DIR / zip_filename + + # Download (use cache if already present) + if not zip_path.exists(): + try: + download_zip(zip_url, zip_path) + except Exception as exc: + print(f" DOWNLOAD ERROR {zip_filename}: {exc}", file=sys.stderr) + return [] + + # Build decoded entry once (basic field URL-decoding) + base_entry = {**entry} + if "basic" in base_entry: + base_entry["basic"] = decode_basic(base_entry["basic"]) + + # Hash every DSK file found in the zip + results: list[tuple[str, dict]] = [] + try: + with zipfile.ZipFile(zip_path, "r") as zf: + dsk_names = [n for n in zf.namelist() + if Path(n).suffix.lower() == ".dsk" and not n.endswith("/")] + if not dsk_names: + print(f" NO DSK in zip {zip_filename}", file=sys.stderr) + return [] + for name in dsk_names: + try: + data = zf.read(name) + sha1 = sha1_of_bytes(data) + results.append((sha1, base_entry)) + except Exception as exc: + print(f" READ ERROR {zip_filename}/{name}: {exc}", file=sys.stderr) + except Exception as exc: + print(f" ZIP ERROR {zip_filename}: {exc}", file=sys.stderr) + return [] + + return results + + +def flatten_zip(zip_path: Path) -> bool: + """Rewrite zip so all DSK files are at root (no subdirectory prefix). + Returns True if the zip was modified, False if already flat or no DSKs found.""" + try: + with zipfile.ZipFile(zip_path, "r") as zf: + names = zf.namelist() + dsk_names = [n for n in names + if Path(n).suffix.lower() == ".dsk" and not n.endswith("/")] + if not dsk_names: + return False + # Check if any DSK is nested, has directory entries, or is not deflated + nested = [n for n in dsk_names if "/" in n] + has_dir_entries = any(n.endswith("/") for n in names) + not_deflated = any(i.compress_type != zipfile.ZIP_DEFLATED + for i in zf.infolist() if not i.filename.endswith("/")) + if not nested and not has_dir_entries and not not_deflated: + return False # already flat, clean, and compressed + + # Build new zip in memory: keep non-DSK entries as-is, + # write DSK entries at root (basename only) + buf = io.BytesIO() + with zipfile.ZipFile(buf, "w", compression=zipfile.ZIP_DEFLATED, compresslevel=9) as out: + for info in zf.infolist(): + if info.filename.endswith("/"): + continue # skip directory entries + data = zf.read(info.filename) + if Path(info.filename).suffix.lower() == ".dsk": + flat_name = Path(info.filename).name + out.writestr(flat_name, data) + else: + out.writestr(info, data) + + zip_path.write_bytes(buf.getvalue()) + return True + except Exception as exc: + print(f" FLATTEN ERROR {zip_path.name}: {exc}", file=sys.stderr) + return False + + +def flatten_dir(target_dir: Path) -> None: + """Flatten all zips in target_dir that have DSK files in subdirectories.""" + zips = sorted(target_dir.glob("*.zip")) + print(f"Scanning {len(zips)} zip files in {target_dir} …", file=sys.stderr) + modified = 0 + for zip_path in zips: + if flatten_zip(zip_path): + modified += 1 + print(f" Flattened: {zip_path.name}", file=sys.stderr) + print(f"\nDone. {modified}/{len(zips)} zips flattened.", file=sys.stderr) + + +def main(): + parser = argparse.ArgumentParser(description="Fetch CoCo disk archive and build SHA1 map.") + parser.add_argument("--flatten", metavar="DIR", nargs="?", const=str(CACHE_DIR), + help="Flatten DSK files to zip root in DIR (default: cache dir). " + "Does not fetch or hash — just rewrites zips.") + args = parser.parse_args() + + if args.flatten is not None: + flatten_dir(Path(args.flatten)) + return + + CACHE_DIR.mkdir(exist_ok=True) + + # Collect all entries from all URLs + all_entries: list[tuple[str, dict]] = [] # (base_url, entry) + for url in URLS: + print(f"Fetching {url} …", file=sys.stderr) + try: + html = fetch_text(url) + files = extract_xroar_config(html) + except Exception as exc: + print(f" ERROR: {exc}", file=sys.stderr) + continue + print(f" {len(files)} entries", file=sys.stderr) + for entry in files: + if entry.get("disk0"): + all_entries.append((url, entry)) + else: + print(f" SKIP (no disk0): {entry.get('filename', '?')}", file=sys.stderr) + + print(f"\nDownloading & hashing {len(all_entries)} zips with {WORKERS} workers …", file=sys.stderr) + + result: dict[str, dict] = {} + conflicts = 0 + done = 0 + + with ThreadPoolExecutor(max_workers=WORKERS) as pool: + futures = {pool.submit(process_entry, base_url, entry): entry + for base_url, entry in all_entries} + for future in as_completed(futures): + done += 1 + if done % 100 == 0 or done == len(all_entries): + print(f" {done}/{len(all_entries)}", file=sys.stderr) + for sha1, entry in future.result(): + if sha1 in result: + conflicts += 1 + print( + f" SHA1 CONFLICT {sha1}: {result[sha1]['filename']!r} vs {entry['filename']!r}", + file=sys.stderr, + ) + else: + result[sha1] = entry + + print(f"\n{len(result)} SHA1 entries ({conflicts} conflict(s)) written to {OUTPUT}", file=sys.stderr) + OUTPUT.write_text(json.dumps(result, indent=2, ensure_ascii=False)) + + +if __name__ == "__main__": + main() diff --git a/package/batocera/core/batocera-configgen/data/mame/messSoftlistMap.json b/package/batocera/core/batocera-configgen/data/mame/messSoftlistMap.json new file mode 100644 index 00000000000..c10de4db5a6 --- /dev/null +++ b/package/batocera/core/batocera-configgen/data/mame/messSoftlistMap.json @@ -0,0 +1,3613 @@ +{ + "_comment": "Maps MAME softlist names to machine/media/autoboot configuration. lua_script paths are relative to the autoboot_script/ subdirectory shipped with this data.", + "32x": { + "machine": "32x", + "media": "cart" + }, + "3do": { + "machine": "3do_fz10", + "media": "cdrom" + }, + "3do_m2": { + "machine": "3do_fz10", + "media": "cdrom" + }, + "a2600": { + "machine": "a2600", + "media": "cart" + }, + "a2600_cass": { + "machine": "a2600", + "media": "cassa" + }, + "a5200": { + "machine": "a5200", + "media": "cart" + }, + "a7800": { + "machine": "a7800", + "media": "cart" + }, + "a800": { + "machine": "a800xl", + "config_args": [ + { + "key": "ramsize", + "type": "int", + "skip_if": 0, + "args": [ + "-ramsize", + "{value}M" + ] + } + ], + "media": "cart" + }, + "a800_cass": { + "machine": "a800xl", + "config_args": [ + { + "key": "ramsize", + "type": "int", + "skip_if": 0, + "args": [ + "-ramsize", + "{value}M" + ] + } + ], + "media": "cass1" + }, + "a800_flop": { + "machine": "a800xl", + "lua_script": "a800_flop.lua", + "config_args": [ + { + "key": "ramsize", + "type": "int", + "skip_if": 0, + "args": [ + "-ramsize", + "{value}M" + ] + } + ], + "media": "flop1" + }, + "abc1600_flop": { + "machine": "abc1600", + "media": "flop1" + }, + "abc1600_hdd": { + "machine": "abc1600", + "media": "hdd" + }, + "abc800_hdd": { + "machine": "abc800c", + "media": "hdd" + }, + "abc806_flop": { + "machine": "abc806", + "media": "flop" + }, + "abc80_cass": { + "machine": "abc80", + "media": "cass1" + }, + "abc80_flop": { + "machine": "abc80", + "media": "flop1" + }, + "abc80_rom": { + "machine": "abc80", + "media": "rom" + }, + "abc830_flop": { + "machine": "abc800c", + "media": "flop" + }, + "abc832_flop": { + "machine": "abc800c", + "media": "flop" + }, + "abc838_flop": { + "machine": "abc800c", + "media": "flop" + }, + "acrnsys_flop": { + "machine": "acrnsys3", + "media": "flop1" + }, + "acrnsys_rom": { + "machine": "acrnsys3", + "media": "rom1" + }, + "adam_cart": { + "machine": "adam", + "extra_args": [ + "-skip_gameinfo" + ], + "media": "cart" + }, + "adam_cass": { + "machine": "adam", + "extra_args": [ + "-skip_gameinfo" + ], + "media": "cass1" + }, + "adam_flop": { + "machine": "adam", + "lua_script": "adam_flop.lua", + "extra_args": [ + "-skip_gameinfo" + ], + "media": "flop1" + }, + "advantage": { + "machine": "no_machine_found", + "media": "flop1" + }, + "advision": { + "machine": "advision", + "media": "cart" + }, + "aim65_cart": { + "machine": "aim65", + "media": "z24" + }, + "aleste": { + "machine": "al520ex", + "media": "flop1" + }, + "alice32": { + "machine": "alice90", + "lua_script": "alice90.lua", + "extra_args": [ + "-skip_gameinfo" + ], + "media": "cass1" + }, + "alice90": { + "machine": "alice90", + "lua_script": "alice90.lua", + "extra_args": [ + "-skip_gameinfo" + ], + "media": "cass" + }, + "alphasmart_kapps": { + "machine": "asma3k", + "media": "kapp" + }, + "alphatro_cart": { + "machine": "alphatro", + "media": "cart" + }, + "alphatro_flop": { + "machine": "alphatro", + "media": "flop1" + }, + "altos5": { + "machine": "altos5", + "media": "flop1" + }, + "altos586": { + "machine": "altos586", + "media": "flop1" + }, + "altos8600": { + "machine": "altos8600", + "media": "flop1" + }, + "amiga_amix": { + "machine": "a3000", + "media": "flop1" + }, + "amiga_apps": { + "machine": "a1200", + "media": "flop1" + }, + "amiga_cd": { + "machine": "cd32", + "media": "cdrom" + }, + "amiga_demos": { + "machine": "a1200", + "media": "flop1" + }, + "amiga_flop": { + "machine": "a1200", + "media": "flop1" + }, + "amiga_hardware": { + "machine": "a1200", + "media": "flop1" + }, + "amiga_hdd": { + "machine": "a1200", + "media": "hdd" + }, + "amiga_workbench": { + "machine": "a1200", + "media": "kickstart" + }, + "amigaaga_flop": { + "machine": "a1200", + "media": "flop1" + }, + "amigaecs_flop": { + "machine": "a1200", + "media": "flop1" + }, + "amigaocs_flop": { + "machine": "a1200", + "media": "flop1" + }, + "ampro": { + "machine": "ampro", + "media": "flop1" + }, + "apc": { + "machine": "apc", + "media": "flop1" + }, + "apexc_cyl": { + "machine": "apexc", + "media": "cyl" + }, + "apfimag_cass": { + "machine": "apfimag", + "media": "cass" + }, + "apfm1000": { + "machine": "apfm1000", + "media": "cart" + }, + "apogee": { + "machine": "apogee", + "lua_script": "apogee.lua", + "media": "cass" + }, + "apollo_ctape": { + "machine": "dn3500", + "media": "cass1" + }, + "apple1": { + "machine": "apple1", + "media": "cass" + }, + "apple2_cass": { + "machine": "apple2p", + "config_args": [ + { + "key": "ramsize", + "type": "int", + "skip_if": 0, + "args": [ + "-ramsize", + "{value}M" + ] + }, + { + "key": "gameio", + "type": "str", + "default": "none", + "skip_if": "none", + "args": [ + "-gameio", + "{value}" + ], + "value_machine_restriction": { + "joyport": [ + "apple2p" + ] + } + } + ], + "media": "cass" + }, + "apple2_flop_clcracked": { + "machine": "apple2", + "media": "flop1" + }, + "apple2_flop_misc": { + "machine": "apple2ee", + "config_args": [ + { + "key": "ramsize", + "type": "int", + "skip_if": 0, + "args": [ + "-ramsize", + "{value}M" + ] + }, + { + "key": "gameio", + "type": "str", + "default": "none", + "skip_if": "none", + "args": [ + "-gameio", + "{value}" + ], + "value_machine_restriction": { + "joyport": [ + "apple2p" + ] + } + } + ], + "rom_ext_args": [ + { + "extensions": [ + ".hdv", + ".2mg", + ".chd", + ".iso", + ".bin", + ".cue" + ], + "args": [ + "-sl7", + "cffa202" + ] + } + ], + "media": "flop1" + }, + "apple2_flop_orig": { + "machine": "apple2", + "media": "flop1" + }, + "apple2gs_flop_clcracked": { + "machine": "apple2gsr3p", + "media": "flop1" + }, + "apple2gs_flop_misc": { + "machine": "apple2gsr3p", + "config_args": [ + { + "key": "ramsize", + "type": "int", + "skip_if": 0, + "args": [ + "-ramsize", + "{value}M" + ] + }, + { + "key": "gameio", + "type": "str", + "default": "none", + "skip_if": "none", + "args": [ + "-gameio", + "{value}" + ], + "value_machine_restriction": { + "joyport": [ + "apple2p" + ] + } + } + ], + "media": "flop1" + }, + "apple2gs_flop_orig": { + "machine": "apple2gsr3p", + "config_args": [ + { + "key": "ramsize", + "type": "int", + "skip_if": 0, + "args": [ + "-ramsize", + "{value}M" + ] + }, + { + "key": "gameio", + "type": "str", + "default": "none", + "skip_if": "none", + "args": [ + "-gameio", + "{value}" + ], + "value_machine_restriction": { + "joyport": [ + "apple2p" + ] + } + } + ], + "media": "flop1" + }, + "apple3": { + "machine": "apple3", + "media": "flop1" + }, + "applix_flop": { + "machine": "applix", + "media": "flop1" + }, + "apricot_flop": { + "machine": "apricot", + "media": "flop1" + }, + "apxen_flop": { + "machine": "apxen", + "media": "flop1" + }, + "aquarius_cart": { + "machine": "aquarius", + "media": "cart1" + }, + "aquarius_cass": { + "machine": "aquarius", + "media": "cass1", + "lua_script": "aquarius_cass.lua" + }, + "arb": { + "machine": "arb", + "media": "cart" + }, + "arcadia": { + "machine": "arcadia", + "media": "cart" + }, + "archimedes": { + "machine": "aa4401", + "config_args": [ + { + "key": "ramsize", + "type": "int", + "skip_if": 0, + "args": [ + "-ramsize", + "{value}M" + ] + } + ], + "media": "flop" + }, + "archimedes_hdd": { + "machine": "aa310", + "media": "hdd" + }, + "archimedes_rom": { + "machine": "aa310", + "media": "rom" + }, + "astrocde": { + "machine": "astrocde", + "media": "cart" + }, + "atom_cass": { + "machine": "atom", + "media": "cass1" + }, + "atom_flop": { + "machine": "atom", + "media": "flop1", + "lua_script": "atom_flop.lua" + }, + "atom_rom": { + "machine": "atom", + "media": "cart" + }, + "attache": { + "machine": "attache", + "media": "flop1" + }, + "aussiebyte": { + "machine": "aussieby", + "media": "flop1" + }, + "b2m": { + "machine": "b2m", + "media": "disk" + }, + "basf7100": { + "machine": "basf7120", + "media": "flop1" + }, + "bbc_cass": { + "machine": "bbcb", + "lua_script": "bbc_cass.lua", + "config_args": [ + { + "key": "sticktype", + "type": "str", + "default": "none", + "skip_if": "none", + "args": [ + "-analogue", + "{value}" + ] + } + ], + "extra_args": [ + "-skip_gameinfo" + ], + "media": "cass" + }, + "bbc_flop_32016": { + "machine": "acw443", + "media": "flop1" + }, + "bbc_flop_6502": { + "machine": "bbcmt", + "media": "flop1" + }, + "bbc_flop_68000": { + "machine": "no_machine_found", + "media": "flop1" + }, + "bbc_flop_80186": { + "machine": "bbcm512", + "media": "flop1" + }, + "bbc_flop_arm": { + "machine": "bbcmarm", + "media": "flop1", + "extra_args": [ + "-tube", + "arm " + ] + }, + "bbc_flop_hybrid": { + "machine": "no_machine_found", + "media": "flop1" + }, + "bbc_flop_torch": { + "machine": "torch725", + "media": "flop1" + }, + "bbc_flop_z80": { + "machine": "abc110", + "media": "flop1" + }, + "bbc_hdd": { + "machine": "bbcb", + "media": "hdd" + }, + "bbc_rom": { + "machine": "bbcb", + "config_args": [ + { + "key": "sticktype", + "type": "str", + "default": "none", + "skip_if": "none", + "args": [ + "-analogue", + "{value}" + ] + } + ], + "media": "rom1" + }, + "bbc_vsm": { + "machine": "bbcb", + "media": "rom" + }, + "bbcb_flop": { + "machine": "bbcb", + "lua_script": "bbcb_flop.lua", + "config_args": [ + { + "key": "sticktype", + "type": "str", + "default": "none", + "skip_if": "none", + "args": [ + "-analogue", + "{value}" + ] + } + ], + "extra_args": [ + "-skip_gameinfo" + ], + "media": "flop1" + }, + "bbcb_flop_orig": { + "machine": "bbcb", + "lua_script": "bbcb_flop_orig.lua", + "config_args": [ + { + "key": "sticktype", + "type": "str", + "default": "none", + "skip_if": "none", + "args": [ + "-analogue", + "{value}" + ] + } + ], + "extra_args": [ + "-skip_gameinfo" + ], + "media": "flop1" + }, + "bbcb_flop_us": { + "machine": "bbcb_us", + "lua_script": "bbcb_flop.lua", + "config_args": [ + { + "key": "sticktype", + "type": "str", + "default": "none", + "skip_if": "none", + "args": [ + "-analogue", + "{value}" + ] + } + ], + "media": "flop1" + }, + "bbcbc": { + "machine": "bbcbc", + "media": "cart" + }, + "bbcm_cart": { + "machine": "bbcm", + "media": "cart" + }, + "bbcm_flop": { + "machine": "bbcm", + "media": "flop1" + }, + "bbcmc_flop": { + "machine": "bbcmc", + "media": "flop1" + }, + "bdesignm_design_cart": { + "machine": "bdesignm", + "media": "cart" + }, + "bdesignm_game_cart": { + "machine": "bdesignm", + "media": "cart" + }, + "bingobear": { + "machine": "bbear", + "media": "cart" + }, + "bk0010": { + "machine": "bk001001", + "lua_script": "bk0010.lua", + "media": "cass" + }, + "bkrankp_cart": { + "machine": "bkrankp", + "media": "cart" + }, + "bmjr_cass": { + "machine": "bmjr", + "media": "cass" + }, + "bml3_cass": { + "machine": "bml3", + "media": "cass" + }, + "bml3_flop": { + "machine": "bml3", + "media": "flop1" + }, + "brother_pn": { + "machine": "pn8800", + "media": "flop1" + }, + "bungo_flop": { + "machine": "mini5sx", + "media": "flop1" + }, + "buzztime_cart": { + "machine": "buzztime", + "media": "cart" + }, + "bw12": { + "machine": "bw12", + "media": "flop1" + }, + "bw14": { + "machine": "bw14", + "media": "flop1" + }, + "bw2": { + "machine": "bw2", + "media": "flop1" + }, + "bx256hp_flop": { + "machine": "bx256hp", + "media": "flop1" + }, + "c128_cart": { + "machine": "c128", + "media": "cart" + }, + "c128_flop": { + "machine": "c128", + "media": "flop1" + }, + "c128_rom": { + "machine": "c128", + "media": "rom" + }, + "c2color_cart": { + "machine": "c2color", + "media": "cart" + }, + "c64_cart": { + "machine": "c64", + "media": "cart" + }, + "c64_cass": { + "machine": "c64", + "lua_script": "c64_cass.lua", + "media": "cass1" + }, + "c64_flop_misc": { + "machine": "c64", + "lua_script": "c64_flop.lua", + "media": "flop1" + }, + "c64_flop_orig": { + "machine": "c64", + "lua_script": "c64_flop.lua", + "media": "flop1" + }, + "c65_flop": { + "machine": "c65", + "media": "flop1" + }, + "camplynx_cass": { + "machine": "lynx128k", + "lua_script": "camplynx_cass.lua", + "extra_args": [ + "-skip_gameinfo", + "-ram", + "128k" + ], + "media": "cass" + }, + "camplynx_flop": { + "machine": "lynx128k", + "media": "flop1" + }, + "carbeena": { + "machine": "beena", + "media": "rom" + }, + "casio_rompack": { + "machine": "casiorom", + "media": "cart" + }, + "casloopy": { + "machine": "casloopy", + "media": "cart" + }, + "cassvisn_cart": { + "machine": "cassvisn", + "media": "cart" + }, + "cbm2_cart": { + "machine": "b256", + "media": "cart" + }, + "cbm2_flop": { + "machine": "b256", + "media": "flop1" + }, + "cbm8096_flop": { + "machine": "cbm8096", + "media": "flop1" + }, + "cbm8296_flop": { + "machine": "cbm8296", + "media": "flop1" + }, + "cc40_cart": { + "machine": "cc40", + "media": "cart" + }, + "cd32": { + "machine": "cd32", + "media": "cdrom" + }, + "cdi": { + "machine": "cdimono1", + "lua_script": "cdi.lua", + "media": "cdrm" + }, + "cdtv": { + "machine": "cdtv", + "media": "cdrom1" + }, + "cecflop": { + "machine": "cec2000", + "media": "flop1" + }, + "cgenie_cass": { + "machine": "cgenie", + "lua_script": "cgenie_cass.lua", + "extra_args": [ + "-ram", + "32k" + ], + "media": "cass" + }, + "cgenie_flop_rom": { + "machine": "cgenie_fdc", + "media": "rom" + }, + "challenge_gear_cart": { + "machine": "chalgear", + "media": "cart" + }, + "channelf": { + "machine": "channelf", + "media": "cart" + }, + "chessking_cart": { + "machine": "chesskng", + "media": "cart" + }, + "chessmstdm": { + "machine": "chessmstdm", + "media": "cart" + }, + "chip8_quik": { + "machine": "d6800", + "media": "quik" + }, + "clickstart_cart": { + "machine": "clikstrt", + "media": "cart" + }, + "clipper_flop": { + "machine": "clipper", + "media": "flop1" + }, + "coco_cart": { + "machine": "coco3", + "media": "cart" + }, + "coco_cass": { + "machine": "coco3", + "lua_script": "trs80_cass.lua", + "config_args": [ + { + "key": "ramsize", + "type": "int", + "skip_if": 0, + "args": [ + "-ramsize", + "{value}M" + ] + } + ], + "media": "cass" + }, + "coco_flop": { + "machine": "coco3", + "lua_script": "trs80_flop.lua", + "config_args": [ + { + "key": "ramsize", + "type": "int", + "skip_if": 0, + "args": [ + "-ramsize", + "{value}M" + ] + } + ], + "media": "flop1" + }, + "coleco": { + "machine": "coleco", + "media": "cart" + }, + "coleco_homebrew": { + "machine": "coleco", + "media": "cart" + }, + "compclr2_flop": { + "machine": "compclr2", + "media": "flop1" + }, + "compis": { + "machine": "compis", + "media": "flop1" + }, + "comx35_flop": { + "machine": "comx35p", + "media": "flop1" + }, + "conchess": { + "machine": "conc", + "media": "cart" + }, + "copera": { + "machine": "copera", + "media": "cart" + }, + "cpc_cass": { + "machine": "cpc464", + "lua_script": "cpc_cass.lua", + "config_args": [ + { + "key": "ramsize", + "type": "int", + "skip_if": 0, + "args": [ + "-ramsize", + "{value}M" + ] + } + ], + "media": "cass1" + }, + "cpc_flop": { + "machine": "cpc6128", + "lua_script": "cpc_flop.lua", + "config_args": [ + { + "key": "ramsize", + "type": "int", + "skip_if": 0, + "args": [ + "-ramsize", + "{value}M" + ] + } + ], + "media": "flop1" + }, + "crvision": { + "machine": "crvision", + "lua_script": "crvision.lua", + "media": "cart" + }, + "ctvboy": { + "machine": "ctvboy", + "media": "cart" + }, + "cubieboard4": { + "machine": "cubibrd4", + "media": "image" + }, + "cx3000tc": { + "machine": "cx3000tc", + "media": "cart" + }, + "cz1_cart": { + "machine": "cz1", + "media": "cart" + }, + "dai_cass": { + "machine": "dai", + "lua_script": "dai_cass.lua", + "media": "cass1" + }, + "database": { + "machine": "database", + "media": "cart" + }, + "dc": { + "machine": "dc", + "media": "cdrom" + }, + "dgnalpha_flop": { + "machine": "dgnalpha", + "media": "flop1" + }, + "dgnbeta_flop": { + "machine": "dgnbeta", + "media": "flop1" + }, + "digiblast_cart": { + "machine": "digiblst", + "media": "cart" + }, + "digilog320": { + "machine": "digilog320", + "media": "flop1" + }, + "dim68k": { + "machine": "dim68k", + "media": "flop1" + }, + "dmv": { + "machine": "dmv", + "media": "flop" + }, + "dps1": { + "machine": "dps1", + "media": "flop1" + }, + "dragon_cart": { + "machine": "dragon64", + "config_args": [ + { + "key": "ramsize", + "type": "int", + "skip_if": 0, + "args": [ + "-ramsize", + "{value}M" + ] + } + ], + "media": "cart" + }, + "dragon_cass": { + "machine": "dragon64", + "lua_script": "dragon_cass.lua", + "config_args": [ + { + "key": "ramsize", + "type": "int", + "skip_if": 0, + "args": [ + "-ramsize", + "{value}M" + ] + } + ], + "media": "cass" + }, + "dragon_flex": { + "machine": "dragon64", + "media": "flop1" + }, + "dragon_flop": { + "machine": "dragon64", + "config_args": [ + { + "key": "ramsize", + "type": "int", + "skip_if": 0, + "args": [ + "-ramsize", + "{value}M" + ] + } + ], + "media": "flop1" + }, + "dragon_os9": { + "machine": "dragon64", + "media": "flop1" + }, + "duelmast_cart": { + "machine": "duelmast", + "media": "cart" + }, + "easy_karaoke_cart": { + "machine": "easykara", + "media": "cart" + }, + "ec1841": { + "machine": "ec1841", + "media": "flop1" + }, + "einstein": { + "machine": "einst256", + "lua_script": "einstein.lua", + "media": "flop1" + }, + "einstein_rom": { + "machine": "einstein", + "media": "rom" + }, + "ekara_cart": { + "machine": "ekara", + "media": "cart" + }, + "electron_cart": { + "machine": "electron", + "extra_args": [ + "-skip_gameinfo" + ], + "media": "cart" + }, + "electron_cass": { + "machine": "electron", + "lua_script": "electron_cass.lua", + "extra_args": [ + "-skip_gameinfo" + ], + "media": "cass" + }, + "electron_flop": { + "machine": "electron", + "lua_script": "electron_flop.lua", + "extra_args": [ + "-skip_gameinfo" + ], + "media": "flop1" + }, + "electron_rom": { + "machine": "electron", + "media": "rom" + }, + "entex_sag": { + "machine": "sag", + "media": "cart" + }, + "ep64_cart": { + "machine": "ep64", + "media": "cart" + }, + "ep64_cass": { + "machine": "ep64", + "media": "cass1" + }, + "ep64_flop": { + "machine": "ep64", + "media": "flop1" + }, + "epson_cpm": { + "machine": "ehx20", + "media": "flop1" + }, + "eti660_quik": { + "machine": "eti660", + "media": "quik" + }, + "evio": { + "machine": "evio", + "media": "cart" + }, + "ews286_flop": { + "machine": "ews286", + "media": "flop1" + }, + "excalibur64": { + "machine": "excali64", + "media": "flop1" + }, + "exl100": { + "machine": "exl100", + "media": "cart" + }, + "famibox": { + "machine": "famicom", + "media": "cart" + }, + "famicom_cass": { + "machine": "famicom", + "lua_script": "famicom_cass.lua", + "media": "cass" + }, + "famicom_flop": { + "machine": "famicom", + "media": "flop1" + }, + "fidel_msc": { + "machine": "miniscc", + "media": "cart" + }, + "fidel_sc6": { + "machine": "fscc6", + "media": "cart" + }, + "fidel_scc": { + "machine": "feag", + "media": "cart" + }, + "fm77av": { + "machine": "fm77av", + "media": "flop1" + }, + "fm7_cass": { + "machine": "fm7", + "lua_script": "fm7_cass.lua", + "media": "cass" + }, + "fm7_disk": { + "machine": "fm7", + "media": "flop1" + }, + "fm8_cass": { + "machine": "fm8", + "media": "cass1" + }, + "fmtowns_cd": { + "machine": "fmtowns", + "media": "cdrm" + }, + "fmtowns_flop_cracked": { + "machine": "fmtowns", + "media": "flop1" + }, + "fmtowns_flop_misc": { + "machine": "fmtowns", + "media": "flop1" + }, + "fmtowns_flop_orig": { + "machine": "fmtowns", + "media": "flop1" + }, + "fp1100_cass": { + "machine": "fp1100", + "media": "cass1" + }, + "gaelco_ds5002fp_rom": { + "machine": "gaelcods", + "media": "cart" + }, + "galaxy": { + "machine": "galaxy", + "lua_script": "galaxy.lua", + "media": "cass1" + }, + "gamate": { + "machine": "gamate", + "media": "cart" + }, + "gameboy": { + "machine": "gameboy", + "media": "cart" + }, + "gamecom": { + "machine": "gamecom", + "media": "cart1" + }, + "gamegear": { + "machine": "gamegear", + "media": "cart" + }, + "gameking": { + "machine": "gameking", + "media": "cart" + }, + "gameking3": { + "machine": "gamekin3", + "media": "cart" + }, + "gamepock": { + "machine": "gamepock", + "media": "cart" + }, + "gba": { + "machine": "gba", + "media": "cart" + }, + "gbcolor": { + "machine": "gbcolor", + "media": "cart" + }, + "gcslottv": { + "machine": "gcslottv", + "media": "cart" + }, + "generic_cdrom": { + "machine": "generic", + "media": "cdrom" + }, + "generic_flop_525": { + "machine": "generic", + "media": "flop1" + }, + "ggm": { + "machine": "ggm", + "media": "cart" + }, + "gimix": { + "machine": "gimix", + "media": "flop1" + }, + "gj4000": { + "machine": "gj4000", + "media": "rom" + }, + "gjmovie": { + "machine": "gjmovie", + "media": "rom" + }, + "gjrstar": { + "machine": "gjrstar", + "media": "rom" + }, + "gl2000": { + "machine": "gl2000", + "media": "rom" + }, + "gl6000sl": { + "machine": "gl6000sl", + "media": "rom" + }, + "glcolor": { + "machine": "glcolor", + "media": "rom" + }, + "glcx": { + "machine": "gl6600cx", + "media": "rom" + }, + "gln": { + "machine": "gln", + "media": "rom" + }, + "gls": { + "machine": "gls", + "media": "rom" + }, + "gmaster": { + "machine": "gmaster", + "media": "cart" + }, + "gp32": { + "machine": "gp32", + "media": "memc", + "extra_args": [ + "-bios", + "166m" + ] + }, + "guab": { + "machine": "guab", + "media": "flop1" + }, + "gx4000": { + "machine": "gx4000", + "media": "cart" + }, + "h21": { + "machine": "h21", + "media": "cart" + }, + "h88_cass": { + "machine": "h88", + "media": "cass" + }, + "hikara": { + "machine": "hikara", + "media": "cart" + }, + "horizon": { + "machine": "nshrz2mhz", + "media": "flop1" + }, + "hp85_rom": { + "machine": "hp85", + "media": "rom" + }, + "hp86_rom": { + "machine": "hp86b", + "media": "rom" + }, + "hp9825_rom": { + "machine": "hp9825b", + "media": "rom" + }, + "hp9831_rom": { + "machine": "hp9831", + "media": "rom" + }, + "hp9835a_rom": { + "machine": "hp9835a", + "media": "rom" + }, + "hp9845a_rom": { + "machine": "hp9845a", + "media": "rom" + }, + "hp9845b_rom": { + "machine": "hp9845b", + "media": "rom" + }, + "hp98x6_rom": { + "machine": "hp9836c", + "media": "rom" + }, + "hp9k3xx_cdrom": { + "machine": "hp9k380", + "media": "cdrom1" + }, + "hp9k3xx_flop": { + "machine": "hp9k380", + "media": "bas4sys" + }, + "hp9k3xx_hdd": { + "machine": "hp9k380", + "media": "hdd" + }, + "hp_ipc": { + "machine": "hp_ipc", + "media": "flop1" + }, + "hp_ipc_rom": { + "machine": "hp_ipc", + "media": "rom" + }, + "ht68k": { + "machine": "ht68k", + "media": "flop1" + }, + "hx20_rom": { + "machine": "ehx20", + "media": "rom" + }, + "hyperscan": { + "machine": "hyprscan", + "media": "cdrom" + }, + "hyperscan_card": { + "machine": "hyprscan", + "media": "card" + }, + "i7000_card": { + "machine": "i7000", + "media": "card" + }, + "ibm5140": { + "machine": "ibm5140", + "media": "flop1" + }, + "ibm5150": { + "machine": "ibm5150", + "media": "flop1" + }, + "ibm5150_cass": { + "machine": "ibm5150", + "media": "cass1" + }, + "ibm5150_hdd": { + "machine": "ibm5150", + "media": "hdd" + }, + "ibm5170": { + "machine": "ibm5170", + "media": "flop1" + }, + "ibm5170_cdrom": { + "machine": "ibm5170", + "media": "cdrom1" + }, + "ibm5170_hdd": { + "machine": "ibm5170", + "media": "hdd" + }, + "ibm6580": { + "machine": "ibm6580", + "media": "flop1" + }, + "ibmpcjr_cart": { + "machine": "ibmpcjr", + "media": "cart" + }, + "ibmpcjr_flop": { + "machine": "ibmpcjr", + "media": "flop1" + }, + "ibmpcjx": { + "machine": "ibmpcjx", + "media": "flop1" + }, + "icanguit": { + "machine": "icanguit", + "media": "cart" + }, + "icanpian": { + "machine": "icanpian", + "media": "cart" + }, + "intellect02": { + "machine": "intel02", + "media": "cart" + }, + "interact": { + "machine": "interact", + "media": "cass" + }, + "interpro": { + "machine": "ip6800", + "media": "disc1" + }, + "intv": { + "machine": "intv", + "media": "cart" + }, + "intvecs": { + "machine": "intvecs", + "media": "cart" + }, + "iq128": { + "machine": "iq128", + "media": "rom" + }, + "iq151_cart": { + "machine": "iq151", + "media": "cart" + }, + "iq151_flop": { + "machine": "iq151", + "media": "flop1" + }, + "ique": { + "machine": "no_machine_found", + "media": "cart" + }, + "iqunlim_cart": { + "machine": "iqunlim", + "media": "cart" + }, + "itt3030": { + "machine": "itt3030", + "media": "flop1" + }, + "jaguar": { + "machine": "jaguar", + "media": "cart" + }, + "jakks_gamekey_dp": { + "machine": "jak_dpr", + "media": "cart" + }, + "jakks_gamekey_dy": { + "machine": "jak_disf", + "media": "cart" + }, + "jakks_gamekey_mv": { + "machine": "jak_spdm", + "media": "cart" + }, + "jakks_gamekey_nk": { + "machine": "jak_dora", + "media": "cart" + }, + "jakks_gamekey_nm": { + "machine": "jak_mpac", + "media": "cart" + }, + "jakks_gamekey_sw": { + "machine": "jak_sith", + "media": "cart" + }, + "jaminator": { + "machine": "jaminator", + "media": "cart" + }, + "jazz": { + "machine": "mmr4000be", + "media": "hdd" + }, + "jb3000_flop": { + "machine": "jb3000", + "media": "flop1" + }, + "juicebox": { + "machine": "juicebox", + "extra_args": [ + "-skip_gameinfo" + ], + "media": "memc" + }, + "juku": { + "machine": "juku", + "lua_script": "juku.lua", + "media": "flop1" + }, + "jupace_cass": { + "machine": "jupace", + "lua_script": "jupace_cass.lua", + "extra_args": [ + "-ram", + "48k" + ], + "media": "cass1" + }, + "jupace_snap": { + "machine": "jupace", + "lua_script": "jupace_snap.lua", + "extra_args": [ + "-ram", + "48k" + ], + "media": "snap" + }, + "k28": { + "machine": "k28", + "media": "cart" + }, + "k28o": { + "machine": "k28o", + "media": "cart" + }, + "kaypro": { + "machine": "kaypro484", + "media": "flop1" + }, + "kc_cart": { + "machine": "kc85_2", + "media": "cart" + }, + "kc_cass": { + "machine": "kc85_2", + "media": "cass" + }, + "kc_flop": { + "machine": "kc85_2", + "media": "flop" + }, + "kim1_cass": { + "machine": "kim1", + "media": "cass" + }, + "kisssite_cd": { + "machine": "kisssite", + "media": "cdrom" + }, + "korvet_flop": { + "machine": "korvet", + "media": "flop1" + }, + "kpython2": { + "machine": "gtrfrkv3j", + "media": "dvdrom1" + }, + "lanteach": { + "machine": "lanteach", + "media": "cart" + }, + "lantrans": { + "machine": "lantrans", + "media": "cart" + }, + "laser2001_cart": { + "machine": "lasr2001", + "media": "cart" + }, + "laser2001_flop": { + "machine": "lasr2001", + "media": "flop1" + }, + "leapfrog_didj_cart": { + "machine": "didj", + "media": "cart" + }, + "leapfrog_iquest_cart": { + "machine": "iquest", + "media": "cart" + }, + "leapfrog_leappad_cart": { + "machine": "leappad", + "media": "cart" + }, + "leapfrog_ltleappad_cart": { + "machine": "ltleappad", + "media": "cart" + }, + "leapfrog_mfleappad_cart": { + "machine": "mfleappad", + "media": "cart" + }, + "leapfrog_zippity_cart": { + "machine": "zippity", + "media": "cart" + }, + "leapster": { + "machine": "leapster", + "media": "cart" + }, + "leapster_explorer_cart": { + "machine": "leapexpr", + "media": "cart" + }, + "lisa": { + "machine": "lisa", + "media": "flop1" + }, + "lisa2": { + "machine": "no_machine_found", + "media": "flop1" + }, + "lk3000": { + "machine": "lk3000", + "media": "cart" + }, + "lnux4004": { + "machine": "lnux4004", + "media": "linux4004" + }, + "lviv": { + "machine": "lviv", + "media": "cass1" + }, + "lynx": { + "machine": "lynx", + "media": "cart" + }, + "m20": { + "machine": "m20", + "media": "flop1" + }, + "m24": { + "machine": "m24", + "media": "flop1" + }, + "m3": { + "machine": "m3", + "media": "flop1" + }, + "m5_cart": { + "machine": "m5", + "media": "cart" + }, + "m5_cass": { + "machine": "m5", + "lua_script": "m5_cass.lua", + "extra_args": [ + "-cart1", + "basici" + ], + "media": "cass1" + }, + "m5_flop": { + "machine": "m5p_brno", + "media": "flop1", + "extra_args": [ + "-cart1", + "basici" + ] + }, + "mac_cdrom": { + "machine": "maclc475", + "media": "cdrom1" + }, + "mac_flop": { + "machine": "macsefd", + "config_args": [ + { + "key": "ramsize", + "type": "int", + "skip_if": 0, + "args": [ + "-ramsize", + "{value}M" + ], + "only_machines": [ + "maciix", + "maclc3" + ], + "machine_overrides": { + "maclc3": { + "value_map": { + "2": 4 + }, + "max": 80 + }, + "maciix": { + "value_map": { + "16": 32, + "48": 64 + } + } + } + } + ], + "media": "flop1" + }, + "mac_flop_clcracked": { + "machine": "mac", + "media": "flop1" + }, + "mac_flop_orig": { + "machine": "mac", + "media": "flop1" + }, + "mac_hdd": { + "machine": "maclc3", + "config_args": [ + { + "key": "ramsize", + "type": "int", + "skip_if": 0, + "args": [ + "-ramsize", + "{value}M" + ], + "only_machines": [ + "maciix", + "maclc3" + ], + "machine_overrides": { + "maclc3": { + "value_map": { + "2": 4 + }, + "max": 80 + }, + "maciix": { + "value_map": { + "16": 32, + "48": 64 + } + } + } + } + ], + "media": "hdd" + }, + "mac_hdflop": { + "machine": "maclc3", + "media": "flop1" + }, + "mbc200": { + "machine": "mbc200", + "media": "flop1" + }, + "mbc55x": { + "machine": "mbc55x", + "media": "flop1" + }, + "mbee_cart": { + "machine": "mbee", + "media": "cart" + }, + "mbee_cass": { + "machine": "mbee", + "media": "cass" + }, + "mbee_flop": { + "machine": "mbee256", + "media": "flop1" + }, + "mbee_quik": { + "machine": "mbee", + "media": "quik" + }, + "mc10": { + "machine": "mc10", + "lua_script": "mc10_cass.lua", + "extra_args": [ + "-ram", + "20k" + ], + "media": "cass" + }, + "mc1000_cass": { + "machine": "mc1000", + "media": "cass1" + }, + "mc1502_flop": { + "machine": "mc1502", + "media": "flop1" + }, + "md2_flop": { + "machine": "md2", + "media": "flop1" + }, + "megacd": { + "machine": "megacd", + "lua_script": "megacd.lua", + "media": "cdrom" + }, + "megadriv": { + "machine": "megadriv", + "media": "cart" + }, + "megaduck": { + "machine": "megaduck", + "media": "cart" + }, + "megapc": { + "machine": "megapc", + "media": "flop1" + }, + "megatech": { + "machine": "no_machine_found", + "media": "cart" + }, + "mephisto_mm1": { + "machine": "mm1", + "media": "cart" + }, + "mephisto_mm2": { + "machine": "mm2", + "media": "cart" + }, + "mephisto_mm4": { + "machine": "mm4", + "media": "cart" + }, + "mephisto_mm5": { + "machine": "mm5", + "media": "cart" + }, + "mephisto_smondial2": { + "machine": "smondial2", + "media": "cart" + }, + "microbox2_flop": { + "machine": "microbx2", + "media": "flop1" + }, + "microvision": { + "machine": "microvsn", + "media": "cart" + }, + "midi_flop": { + "machine": "a486sv1", + "media": "flop1" + }, + "mikro80": { + "machine": "mikro80", + "media": "cass1" + }, + "mikrosha_cart": { + "machine": "mikrosha", + "media": "cart" + }, + "mikrosha_cass": { + "machine": "mikrosha", + "media": "cass" + }, + "mindset_flop": { + "machine": "mindset", + "media": "flop1" + }, + "misterx": { + "machine": "misterx", + "media": "cart" + }, + "mk14_quik": { + "machine": "mk14", + "media": "quik" + }, + "mm1_flop": { + "machine": "mm1m7", + "media": "flop1" + }, + "mm2_flop": { + "machine": "mm2m35d", + "media": "flop1" + }, + "mo5_cart": { + "machine": "mo5", + "media": "cart" + }, + "mo5_cass": { + "machine": "mo5", + "lua_script": "mo5_cass.lua", + "media": "cass1" + }, + "mo5_flop": { + "machine": "mo5", + "media": "flop1" + }, + "mo5_qd": { + "machine": "mo5", + "media": "cart" + }, + "mo6_cass": { + "machine": "mo6", + "lua_script": "mo6_cass.lua", + "media": "cass1" + }, + "mo6_flop": { + "machine": "mo6", + "media": "flop1" + }, + "mobigo_cart": { + "machine": "mobigo", + "media": "cart" + }, + "monon_color": { + "machine": "mononcol", + "media": "cart" + }, + "mpc3000_flop": { + "machine": "mpc3000", + "media": "disk1" + }, + "mpf1_rom": { + "machine": "mpf1", + "media": "rom" + }, + "mpu1000": { + "machine": "mpu1000", + "media": "cart" + }, + "mpz80": { + "machine": "mpz80", + "media": "flop" + }, + "msx1_bee_card": { + "machine": "hx10", + "media": "cart2", + "extra_args": [ + "-cartslot1", + "beepack" + ] + }, + "msx1_cart": { + "machine": "hx10", + "media": "cart1" + }, + "msx1_cass": { + "machine": "hx10", + "media": "cass1", + "lua_script": "msx1_cass.lua" + }, + "msx1_flop": { + "machine": "nms8250", + "media": "flop1" + }, + "msx1_flop_525": { + "machine": "hx10", + "media": "flop1", + "extra_args": [ + "-cart1", + "cdx2" + ] + }, + "msx2_cart": { + "machine": "nms8250", + "media": "cart1" + }, + "msx2_cass": { + "machine": "nms8250", + "media": "cass1", + "lua_script": "msx1_cass.lua" + }, + "msx2_flop": { + "machine": "nms8250", + "media": "flop1" + }, + "msx2p_cart": { + "machine": "fsa1gt", + "media": "cart1" + }, + "msx2p_flop": { + "machine": "fsa1gt", + "media": "flop1" + }, + "msx_softcard": { + "machine": "no_machine_found", + "media": "cart" + }, + "msx_yamaha_minicart": { + "machine": "cx5m128", + "media": "cart" + }, + "msxr_cart": { + "machine": "fsa1gt", + "media": "cart" + }, + "msxr_flop": { + "machine": "fsa1gt", + "media": "flop1" + }, + "mt65_cass": { + "machine": "mt65", + "media": "cass1" + }, + "mt65_rom": { + "machine": "mt65", + "media": "rom1" + }, + "mt65_snap": { + "machine": "mt65", + "media": "snap" + }, + "mtu130_flop": { + "machine": "mtu130", + "media": "flop1" + }, + "mtx_cart": { + "machine": "mtx512", + "media": "cart" + }, + "mtx_cass": { + "machine": "mtx512", + "lua_script": "mtx_cass.lua", + "extra_args": [ + "-skip_gameinfo" + ], + "media": "cass1" + }, + "mtx_flop": { + "machine": "mtx512", + "media": "flop" + }, + "mtx_hdd": { + "machine": "mtx512", + "media": "hdd" + }, + "mtx_rom": { + "machine": "mtx512", + "media": "rom" + }, + "myvision": { + "machine": "myvision", + "media": "cart" + }, + "mz2000_cass": { + "machine": "mz2000", + "lua_script": "mz2000_cass.lua", + "media": "cass" + }, + "mz2000_flop": { + "machine": "mz2000", + "media": "flop1" + }, + "mz2000_snap": { + "machine": "mz2000", + "media": "snap" + }, + "mz2500_flop": { + "machine": "mz2500", + "media": "flop1" + }, + "mz5500_flop": { + "machine": "mz5500", + "media": "flop1" + }, + "mz700_cass": { + "machine": "mz700", + "lua_script": "mz700_cass.lua", + "media": "cass" + }, + "mz800_cass": { + "machine": "mz800", + "lua_script": "mz800_cass.lua", + "media": "cass" + }, + "mz800_rom": { + "machine": "no_machine_found", + "media": "rom" + }, + "mz80b_cass": { + "machine": "mz80b", + "media": "cass1" + }, + "mz80b_flop": { + "machine": "mz80b", + "media": "flop1" + }, + "mz80k_cass": { + "machine": "mz80k", + "lua_script": "mz80k_cass.lua", + "media": "cass" + }, + "n64": { + "machine": "n64", + "media": "cart" + }, + "n64_lodgenet": { + "machine": "n64_lodgenet", + "media": "cart" + }, + "n64dd": { + "machine": "n64dd", + "media": "disk" + }, + "nascom_flop": { + "machine": "nascom2", + "media": "flop1" + }, + "nascom_snap": { + "machine": "nascom2", + "media": "snap" + }, + "nascom_socket": { + "machine": "nascom2", + "media": "socket" + }, + "neocd": { + "machine": "neocd", + "media": "cdrom" + }, + "neogeo": { + "machine": "aes", + "media": "cart" + }, + "nes": { + "machine": "nes", + "media": "cart" + }, + "nes_ade": { + "machine": "nes", + "media": "cart" + }, + "nes_datach": { + "machine": "nes", + "media": "cart" + }, + "nes_kstudio": { + "machine": "nes", + "media": "cart" + }, + "nes_ntbrom": { + "machine": "nes", + "media": "cart" + }, + "nes_vt_cart": { + "machine": "timetp25", + "media": "cart" + }, + "next": { + "machine": "nextst", + "media": "flop1" + }, + "next_cdrom": { + "machine": "next", + "media": "cdrom" + }, + "next_hdd": { + "machine": "next", + "media": "hdd" + }, + "ngp": { + "machine": "ngp", + "lua_script": "ngp.lua", + "media": "cart" + }, + "ngpc": { + "machine": "ngpc", + "lua_script": "ngp.lua", + "media": "cart" + }, + "nimbus": { + "machine": "nimbus", + "media": "flop1" + }, + "novag_ssensor4": { + "machine": "ssensor4", + "media": "exrom" + }, + "nuon": { + "machine": "n501", + "media": "dvdrom" + }, + "octopus": { + "machine": "octopus", + "media": "flop1" + }, + "ondra": { + "machine": "ondrat", + "media": "cass" + }, + "orao": { + "machine": "orao", + "media": "cass1" + }, + "oric1_cass": { + "machine": "oric1", + "lua_script": "oric1_cass.lua", + "media": "cass" + }, + "orina_stylish_plus_cart": { + "machine": "orinasp", + "media": "cart" + }, + "orion_cart": { + "machine": "orion128", + "media": "cart" + }, + "orion_cass": { + "machine": "orionzms", + "lua_script": "orion_cass.lua", + "extra_args": [ + "-cart", + "romdisk" + ], + "media": "cass1" + }, + "orion_flop": { + "machine": "orion128", + "media": "disk" + }, + "orionpro_flop": { + "machine": "orionpro", + "media": "disk" + }, + "osborne1": { + "machine": "osborne1", + "media": "flop1" + }, + "osborne2": { + "machine": "osbexec", + "media": "flop1" + }, + "p500_flop": { + "machine": "p500", + "media": "flop1" + }, + "partner_cass": { + "machine": "partner", + "media": "cass" + }, + "partner_flop": { + "machine": "partner", + "media": "disk" + }, + "pasogo": { + "machine": "pasogo", + "media": "cart" + }, + "pasopia7_cass": { + "machine": "pasopia7", + "media": "cass1" + }, + "pasopia_cass": { + "machine": "pasopia", + "media": "cass1" + }, + "pb2000c": { + "machine": "pb2000c", + "media": "cart" + }, + "pc1000": { + "machine": "pc1000", + "media": "rom" + }, + "pc100_flop": { + "machine": "pc100", + "media": "flop1" + }, + "pc1512_flop": { + "machine": "pc1512", + "media": "flop1" + }, + "pc1512_hdd": { + "machine": "pc1512", + "media": "hdd" + }, + "pc1640_flop": { + "machine": "pc1640", + "media": "flop1" + }, + "pc1640_hdd": { + "machine": "pc1640", + "media": "hdd" + }, + "pc200": { + "machine": "pc200", + "media": "flop1" + }, + "pc6001_cart": { + "machine": "pc6001", + "media": "cart1" + }, + "pc6001_cass": { + "machine": "pc6001", + "lua_script": "pc6001_cass.lua", + "media": "cart" + }, + "pc6001mk2_cass": { + "machine": "pc6001mk2", + "lua_script": "pc6001mk2_cass.lua", + "media": "cart" + }, + "pc8001_flop": { + "machine": "pc8001", + "media": "flop1" + }, + "pc8001mk2_flop": { + "machine": "pc8001mk2", + "media": "flop1" + }, + "pc8001mk2sr_flop": { + "machine": "pc8001mk2sr", + "media": "flop1" + }, + "pc8201": { + "machine": "pc8201", + "media": "cart" + }, + "pc8801_cass": { + "machine": "pc8801", + "media": "cass1" + }, + "pc8801_cdrom": { + "machine": "pc8801mc", + "media": "cdrom" + }, + "pc8801_flop": { + "machine": "pc8801mk2sr", + "media": "flop1" + }, + "pc88va": { + "machine": "pc88va", + "media": "flop1" + }, + "pc98": { + "machine": "pc9821", + "media": "flop1" + }, + "pc98_cd": { + "machine": "pc9821", + "media": "cdrom" + }, + "pc98_flop_orig": { + "machine": "pc9821", + "media": "flop1" + }, + "pc98_hdd": { + "machine": "pc9821", + "media": "hdd" + }, + "pcd_flop": { + "machine": "pcd", + "media": "flop1" + }, + "pce": { + "machine": "pce", + "media": "cart" + }, + "pce_tourvision": { + "machine": "no_machine_found", + "media": "cart" + }, + "pcecd": { + "machine": "pce", + "lua_script": "pcecd.lua", + "media": "cdrom" + }, + "pcfx": { + "machine": "pcfx", + "media": "cdrom" + }, + "pcw": { + "machine": "pcw8256", + "media": "flop" + }, + "pcw16": { + "machine": "pcw16", + "media": "flop1" + }, + "pcx_flop": { + "machine": "pcx", + "media": "flop1" + }, + "pda600": { + "machine": "pda600", + "media": "card" + }, + "pdp1_ptp": { + "machine": "pdp1", + "media": "ptp" + }, + "pecom_cass": { + "machine": "pecom64", + "media": "cass1", + "lua_script": "pecom_cass.lua" + }, + "pegasus_cart": { + "machine": "pegasus", + "media": "cart" + }, + "pencil2": { + "machine": "pencil2", + "media": "cart" + }, + "pencil2_cass": { + "machine": "pencil2", + "media": "cass1" + }, + "pentagon_cass": { + "machine": "pentagon", + "media": "cass" + }, + "pet_cass": { + "machine": "cbm8296", + "media": "cass" + }, + "pet_flop": { + "machine": "pet", + "media": "flop1" + }, + "pet_hdd": { + "machine": "cbm8296", + "media": "hdd" + }, + "pet_quik": { + "machine": "cbm8296", + "media": "quik" + }, + "pet_rom": { + "machine": "cbm8296", + "media": "rom" + }, + "phc25_cass": { + "machine": "phc25", + "media": "cass" + }, + "photo_cd": { + "machine": "cd32", + "media": "cdrom" + }, + "pi_storyreader_cart": { + "machine": "pi_stry", + "media": "cart" + }, + "pi_storyreader_v2_cart": { + "machine": "pi_stry2", + "media": "cart" + }, + "picno": { + "machine": "picno", + "media": "cart" + }, + "pico": { + "machine": "pico", + "media": "cart" + }, + "pippin": { + "machine": "pippin", + "media": "cdrom" + }, + "pippin_flop": { + "machine": "no_machine_found", + "media": "flop1" + }, + "pixter_cart": { + "machine": "pixtermu", + "media": "cart" + }, + "playmaker": { + "machine": "playmaker", + "media": "cart" + }, + "pls1000_cart": { + "machine": "pls1000", + "media": "cart" + }, + "plus4_cart": { + "machine": "plus4", + "media": "cart" + }, + "plus4_cass": { + "machine": "plus4", + "media": "cass1" + }, + "plus4_flop": { + "machine": "plus4", + "media": "flop1" + }, + "pmd85_cass": { + "machine": "pmd851", + "media": "cass1" + }, + "pockchalv2": { + "machine": "pockchv2", + "media": "cart" + }, + "pockchalw": { + "machine": "pockchal", + "media": "cart" + }, + "pofo": { + "machine": "pofo", + "media": "card" + }, + "poisk1_flop": { + "machine": "poisk1", + "media": "flop1" + }, + "pokemini": { + "machine": "pokemini", + "media": "cart" + }, + "poly_flop": { + "machine": "poly1", + "media": "flop1" + }, + "polysix": { + "machine": "polysix", + "media": "a" + }, + "princ": { + "machine": "princ", + "media": "rom" + }, + "pro128_cart": { + "machine": "pro128", + "media": "cart" + }, + "pro128_cass": { + "machine": "pro128", + "media": "cass1" + }, + "pro128_flop": { + "machine": "pro128", + "media": "flop1" + }, + "pro128s_flop": { + "machine": "pro128s", + "media": "flop1" + }, + "prof180": { + "machine": "prof180x", + "media": "flop1" + }, + "prof80": { + "machine": "prof80", + "media": "flop1" + }, + "psi98": { + "machine": "psi98", + "media": "flop1" + }, + "psion1": { + "machine": "psion1", + "media": "cart" + }, + "psion2": { + "machine": "psioncm", + "media": "cart" + }, + "psion_flop": { + "machine": "mc600", + "media": "flop1" + }, + "psion_ssd": { + "machine": "psion3", + "media": "cart" + }, + "psx": { + "machine": "psj", + "media": "cdrom" + }, + "pt68k2": { + "machine": "pt68k2", + "media": "flop1" + }, + "pv1000": { + "machine": "pv1000", + "media": "cart" + }, + "pv2000": { + "machine": "pv2000", + "media": "cart" + }, + "px4_cart": { + "machine": "px4", + "media": "cart" + }, + "px8_cart": { + "machine": "px8", + "media": "cart1" + }, + "pyl601": { + "machine": "pyl601", + "media": "disk" + }, + "ql_cart": { + "machine": "ql", + "media": "cart" + }, + "ql_cass": { + "machine": "ql", + "media": "cass1" + }, + "ql_flop": { + "machine": "ql", + "media": "flop1" + }, + "quizwiz": { + "machine": "quizwizc", + "media": "cart" + }, + "qx10_flop": { + "machine": "qx10", + "media": "flop1" + }, + "r8_card": { + "machine": "r8", + "media": "card" + }, + "r9751": { + "machine": "r9751", + "media": "flop1" + }, + "radio86_cart": { + "machine": "radiorom", + "media": "cart" + }, + "radio86_cass": { + "machine": "radio86", + "lua_script": "radio86_cass.lua", + "media": "cass" + }, + "rainbow": { + "machine": "rainbow", + "media": "flop1" + }, + "ramstar": { + "machine": "ramstar", + "media": "rom" + }, + "roland_tnsc1": { + "machine": "rlndtnsc1", + "media": "cart" + }, + "roland_tnsc2": { + "machine": "rlndtnsc2", + "media": "cart" + }, + "rwtrntcs": { + "machine": "rwtrntcs", + "media": "cart" + }, + "rx78_cart": { + "machine": "rx78", + "media": "cart" + }, + "rx78_cass": { + "machine": "rx78", + "lua_script": "rx78_cass.lua", + "media": "cass" + }, + "rz1_cass": { + "machine": "rz1", + "media": "sidea" + }, + "s2000_flop": { + "machine": "s2000", + "media": "disk1" + }, + "s3000_cdrom": { + "machine": "s3000", + "media": "cdrom1" + }, + "sagafox": { + "machine": "sagafox", + "media": "flop1" + }, + "sage2": { + "machine": "sage2", + "media": "flop1" + }, + "saitek_egr": { + "machine": "corona", + "media": "extrom" + }, + "saitek_kso": { + "machine": "tstar432", + "media": "extrom" + }, + "saitek_schess": { + "machine": "schess", + "media": "cart" + }, + "samcoupe_cass": { + "machine": "samcoupe", + "media": "cass" + }, + "samcoupe_flop": { + "machine": "samcoupe", + "lua_script": "samcoupe_flop.lua", + "config_args": [ + { + "key": "ramsize", + "type": "int", + "skip_if": 0, + "args": [ + "-ramsize", + "{value}M" + ] + } + ], + "extra_args": [ + "-skip_gameinfo" + ], + "media": "flop1" + }, + "sat_cart": { + "machine": "saturn", + "media": "cart" + }, + "sat_vccart": { + "machine": "no_machine_found", + "media": "cart" + }, + "saturn": { + "machine": "saturn", + "lua_script": "saturn.lua", + "media": "cdrom" + }, + "sawatte": { + "machine": "sawatte", + "media": "cart" + }, + "sbrain": { + "machine": "sbrain", + "media": "flop1" + }, + "sc3000_cart": { + "machine": "sc3000", + "media": "cart" + }, + "sc3000_cass": { + "machine": "sc3000", + "lua_script": "sc3000_cass.lua", + "extra_args": [ + "-cart", + "basic3e" + ], + "media": "cass" + }, + "scv": { + "machine": "scv", + "media": "cart" + }, + "sd132_flop": { + "machine": "sd132", + "media": "flop" + }, + "sd1_flop": { + "machine": "sd1", + "media": "flop" + }, + "sdk85": { + "machine": "sdk85", + "media": "prom" + }, + "sega_beena_cart": { + "machine": "beena", + "media": "cart" + }, + "segaai": { + "machine": "segaai", + "media": "card" + }, + "sf7000": { + "machine": "sf7000", + "media": "flop1" + }, + "sg1000": { + "machine": "sg1000", + "media": "cart" + }, + "sgi_mips": { + "machine": "indy_4610", + "media": "cdrom" + }, + "sgi_mips_hdd": { + "machine": "indy_4610", + "media": "hdd" + }, + "sgx": { + "machine": "sgx", + "media": "cart" + }, + "singingstarkaraoke_cart": { + "machine": "sstarkar", + "media": "cart" + }, + "sitcom": { + "machine": "sitcom", + "media": "san" + }, + "smartcycle_cart": { + "machine": "smartcyc", + "media": "cart" + }, + "smarttv_cart": { + "machine": "smartvad", + "media": "cart" + }, + "smc777": { + "machine": "smc777", + "media": "flop1" + }, + "sms": { + "machine": "sms", + "media": "cart" + }, + "snes": { + "machine": "snes", + "media": "cart" + }, + "snes_bspack": { + "machine": "snes", + "media": "cart" + }, + "snes_strom": { + "machine": "snes", + "media": "cart" + }, + "snes_vkun": { + "machine": "no_machine_found", + "media": "cdrom" + }, + "snotec": { + "machine": "snotec", + "media": "rom" + }, + "snread": { + "machine": "snread", + "media": "cart" + }, + "snspell": { + "machine": "snspell", + "media": "cart" + }, + "socrates": { + "machine": "socrates", + "media": "cart" + }, + "softbox": { + "machine": "softbox", + "media": "flop1" + }, + "sol20_cass": { + "machine": "sol20", + "media": "cass1" + }, + "sony_news": { + "machine": "nws5000x", + "media": "hdd" + }, + "sorcerer_cart": { + "machine": "sorcerer2", + "media": "cart" + }, + "sorcerer_cass": { + "machine": "sorcerer2", + "lua_script": "sorcerer_cass.lua", + "extra_args": [ + "-cart", + "basicpac" + ], + "media": "cass" + }, + "sorcerer_flop": { + "machine": "sorcererd", + "media": "flop1" + }, + "spc1000_cass": { + "machine": "spc1000", + "media": "cass1" + }, + "spc1500_cass": { + "machine": "spc1500", + "media": "cass" + }, + "special_cass": { + "machine": "special", + "media": "cass" + }, + "special_flop": { + "machine": "specimx", + "media": "disk" + }, + "specnext_sd": { + "machine": "tbblue", + "media": "card" + }, + "specpls3_flop": { + "machine": "specpls3", + "lua_script": "specpls3_flop.lua", + "media": "flop1" + }, + "spectrum_betadisc_flop": { + "machine": "tsconf", + "media": "flop1" + }, + "spectrum_cart": { + "machine": "no_machine_found", + "media": "cart" + }, + "spectrum_cass": { + "machine": "spectrum", + "lua_script": "spectrum_cass.lua", + "config_args": [ + { + "key": "ramsize", + "type": "int", + "skip_if": 0, + "args": [ + "-ramsize", + "{value}M" + ] + } + ], + "media": "cass" + }, + "spectrum_flop_opus": { + "machine": "no_machine_found", + "media": "flop1" + }, + "spectrum_mgt_flop": { + "machine": "no_machine_found", + "media": "flop1" + }, + "spectrum_microdrive": { + "machine": "no_machine_found", + "media": "cass" + }, + "spectrum_wafadrive": { + "machine": "no_machine_found", + "media": "cass" + }, + "sprachmg": { + "machine": "sprachmg", + "media": "rom" + }, + "squale_cart": { + "machine": "squale", + "media": "cart" + }, + "ssem_quik": { + "machine": "ssem", + "media": "quik" + }, + "st_cart": { + "machine": "st", + "media": "cart" + }, + "st_flop": { + "machine": "st", + "media": "flop1" + }, + "st_flop_demos": { + "machine": "st", + "media": "flop1" + }, + "stepone_flop": { + "machine": "stepone", + "media": "flop1" + }, + "studio2": { + "machine": "studio2", + "media": "cart" + }, + "stv": { + "machine": "no_machine_found", + "media": "cart" + }, + "sun_sparc": { + "machine": "e250", + "media": "cdrom1" + }, + "super6": { + "machine": "super6", + "media": "flop1" + }, + "super80_cass": { + "machine": "super80", + "lua_script": "super80_cass.lua", + "media": "cass" + }, + "super80_flop": { + "machine": "super80v", + "extra_args": [ + "-skip_gameinfo" + ], + "media": "flop1" + }, + "super_tv_pc_cart": { + "machine": "suprtvpc", + "media": "cart" + }, + "superpet_flop": { + "machine": "superpet", + "media": "flop1" + }, + "supracan": { + "machine": "supracan", + "media": "cart" + }, + "sv8000": { + "machine": "sv8000", + "media": "cart" + }, + "svi318_cart": { + "machine": "svi318", + "media": "cart" + }, + "svi318_cass": { + "machine": "svi318", + "lua_script": "svi318_cass.lua", + "media": "cass1" + }, + "svi318_flop": { + "machine": "no_machine_found", + "media": "flop1" + }, + "svision": { + "machine": "svision", + "media": "cart" + }, + "svmu": { + "machine": "svmu", + "media": "quik" + }, + "t1000": { + "machine": "t1000", + "media": "flop1" + }, + "takara_daigunder_dx_cart": { + "machine": "tak_daig", + "media": "cart" + }, + "tandy200": { + "machine": "tandy200", + "media": "cart" + }, + "tandy2k": { + "machine": "tandy2k", + "media": "flop1" + }, + "tandy6k": { + "machine": "no_machine_found", + "media": "flop1" + }, + "tc4": { + "machine": "tc4", + "media": "cart" + }, + "tdv2324": { + "machine": "tdv2324", + "media": "flop" + }, + "tek4052_cart": { + "machine": "tek4052a", + "media": "cart" + }, + "telestory_cart": { + "machine": "telestry", + "media": "cart" + }, + "teradrive_flop": { + "machine": "teradrive", + "media": "flop1" + }, + "teradrive_hdd": { + "machine": "teradrive", + "media": "hdd" + }, + "tg16": { + "machine": "tg16", + "media": "cart" + }, + "thinkpad8xx": { + "machine": "tpad850", + "media": "cdrom1" + }, + "ti74_cart": { + "machine": "ti74", + "media": "cart" + }, + "ti95_cart": { + "machine": "ti95", + "media": "cart" + }, + "ti99_cart": { + "machine": "ti99_4a", + "extra_args": [ + "-ioport", + "peb" + ], + "config_args": [ + { + "key": "ti99_32kram", + "type": "bool", + "default": true, + "if_true": [ + "-ioport:peb:slot2", + "32kmem" + ] + }, + { + "key": "ti99_speech", + "type": "bool", + "default": true, + "if_true": [ + "-ioport", + "speechsyn" + ] + } + ], + "media": "cart" + }, + "tiki100": { + "machine": "tiki100", + "media": "flop1" + }, + "timex_cass": { + "machine": "ts2068", + "lua_script": "timex_cass.lua", + "media": "cass" + }, + "timex_dock": { + "machine": "ts2068", + "media": "cart" + }, + "tmc600_quik": { + "machine": "tmc600s2", + "media": "quik" + }, + "tntell": { + "machine": "tntell", + "media": "cart" + }, + "to770_cart": { + "machine": "to770", + "media": "cart" + }, + "to770a_cart": { + "machine": "to770a", + "media": "cart" + }, + "to7_cart": { + "machine": "to7", + "media": "cart" + }, + "to7_cass": { + "machine": "to7", + "media": "cass1" + }, + "to7_qd": { + "machine": "to7", + "media": "flop1" + }, + "to8_cass": { + "machine": "to8", + "media": "cass1" + }, + "to8_qd": { + "machine": "to8", + "media": "flop1" + }, + "to_flop": { + "machine": "to8", + "lua_script": "to_flop.lua", + "media": "flop1" + }, + "triton_rom": { + "machine": "triton72", + "media": "rom" + }, + "trs80_cass": { + "machine": "trs80m3", + "lua_script": "trs80_cass.lua", + "media": "cass" + }, + "trs80_flop": { + "machine": "trs80m3", + "media": "flop1" + }, + "trs80_quik": { + "machine": "trs80m3", + "config_args": [ + { + "key": "ramsize", + "type": "int", + "skip_if": 0, + "args": [ + "-ramsize", + "{value}M" + ] + } + ], + "media": "quik" + }, + "trs80m2": { + "machine": "trs80m2", + "media": "flop1" + }, + "trsm100": { + "machine": "trsm100", + "media": "cart" + }, + "tsconf_betadisc_flop": { + "machine": "tsconf", + "media": "flop1" + }, + "ttwist_brainquest_cart": { + "machine": "ttwistbq", + "media": "cart" + }, + "turboextreme_cart": { + "machine": "turboex", + "media": "cart" + }, + "tutor": { + "machine": "tutor", + "media": "cart" + }, + "tvc_cart": { + "machine": "tvc64p", + "media": "cart" + }, + "tvc_cass": { + "machine": "tvc64p", + "lua_script": "tvc_cass.lua", + "media": "cass" + }, + "tvc_flop": { + "machine": "tvc64p", + "media": "flop1", + "lua_script": "tvc_flop.lua", + "extra_args": [ + "-exp1", + "hbf" + ] + }, + "tvdear": { + "machine": "tvdear", + "media": "cart" + }, + "tvgogo": { + "machine": "tvgogo", + "media": "cart" + }, + "tvochken": { + "machine": "tvochken", + "media": "card1" + }, + "tx0_ptp": { + "machine": "tx0_8kw", + "media": "ptp" + }, + "u110_card": { + "machine": "cm32p", + "media": "card" + }, + "unichamp": { + "machine": "unichamp", + "media": "cart" + }, + "ut88": { + "machine": "ut88", + "media": "cass" + }, + "uzebox": { + "machine": "uzebox", + "media": "cart" + }, + "v1050_flop": { + "machine": "v1050", + "media": "flop1" + }, + "v1050_hdd": { + "machine": "v1050", + "media": "hdd" + }, + "vbaby_cart": { + "machine": "vbaby", + "media": "cart" + }, + "vboy": { + "machine": "vboy", + "media": "cart" + }, + "vc4000": { + "machine": "vc4000", + "media": "cart" + }, + "vector06_cart": { + "machine": "vector06", + "media": "cart" + }, + "vector06_flop": { + "machine": "vector06", + "lua_script": "vector06_flop.lua", + "media": "flop1" + }, + "vectrex": { + "machine": "vectrex", + "media": "cart" + }, + "vfxsd_flop": { + "machine": "vfxsd", + "media": "flop" + }, + "vg5k": { + "machine": "vg5k", + "lua_script": "vg5k.lua", + "extra_args": [ + "-skip_gameinfo" + ], + "media": "cass" + }, + "vgmplay": { + "machine": "vgmplay", + "media": "quik" + }, + "vic10": { + "machine": "vic10", + "media": "cart" + }, + "vic1001_cart": { + "machine": "vic1001", + "media": "cart" + }, + "vic1001_cass": { + "machine": "vic1001", + "media": "cass1" + }, + "vic1001_flop": { + "machine": "vic1001", + "media": "flop1" + }, + "victor9k_flop": { + "machine": "victor9k", + "media": "flop1" + }, + "vidbrain": { + "machine": "vidbrain", + "media": "cart" + }, + "videoart": { + "machine": "videoart", + "media": "cart" + }, + "videopac": { + "machine": "videopac", + "media": "cart" + }, + "vii": { + "machine": "vii", + "media": "cart" + }, + "vip": { + "machine": "vip", + "media": "cass1" + }, + "vis": { + "machine": "vis", + "media": "cdrom" + }, + "visicom": { + "machine": "visicom", + "media": "cart" + }, + "vixen": { + "machine": "vixen", + "media": "flop1" + }, + "vsmile_cart": { + "machine": "vsmile", + "media": "cart" + }, + "vsmile_cd": { + "machine": "vsmilpro", + "media": "cdrom" + }, + "vsmileb_cart": { + "machine": "vsmileb", + "media": "cart" + }, + "vsmilem_cart": { + "machine": "vsmilem", + "media": "cart" + }, + "vtech2_cass": { + "machine": "laser700", + "lua_script": "vtech2_cass.lua", + "media": "cass1" + }, + "vtech_innotab_cart": { + "machine": "innotab2", + "media": "cart" + }, + "vtech_innotv_cart": { + "machine": "innotv", + "media": "cart" + }, + "vtech_storio_cart": { + "machine": "vreader", + "media": "cart" + }, + "vz_cass": { + "machine": "laser310", + "media": "cass1" + }, + "vz_snap": { + "machine": "laser310", + "media": "snap" + }, + "wangpc": { + "machine": "wangpc", + "media": "flop1" + }, + "waveterm": { + "machine": "waveterm", + "media": "flop1" + }, + "wicat": { + "machine": "wicat", + "media": "flop1" + }, + "wizard_cart": { + "machine": "iq7000", + "media": "cart" + }, + "wmbullet": { + "machine": "wmbullet", + "media": "flop1" + }, + "wren_flop": { + "machine": "wren", + "media": "flop1" + }, + "wscolor": { + "machine": "wscolor", + "media": "cart" + }, + "wswan": { + "machine": "wswan", + "media": "cart" + }, + "x07_card": { + "machine": "x07", + "media": "card" + }, + "x07_cass": { + "machine": "x07", + "media": "cass" + }, + "x1_cass": { + "machine": "x1", + "media": "cass" + }, + "x1_flop": { + "machine": "x1", + "media": "flop1" + }, + "x37_flop": { + "machine": "x37", + "media": "flop1" + }, + "x68k_flop": { + "machine": "x68000", + "media": "flop1" + }, + "xegs": { + "machine": "xegs", + "media": "cart" + }, + "xerox820": { + "machine": "bigboard", + "media": "flop1" + }, + "xerox820ii": { + "machine": "x820ii", + "media": "flop1" + }, + "z80clock": { + "machine": "z80clock", + "media": "u12" + }, + "z80ne_cass": { + "machine": "z80net", + "media": "cass1" + }, + "z80ne_flop": { + "machine": "z80netf", + "media": "flop1" + }, + "z88_cart": { + "machine": "z88", + "media": "cart1" + }, + "zorba": { + "machine": "zorba", + "media": "flop1" + }, + "zx80_cass": { + "machine": "zx80", + "media": "cass1" + }, + "zx81_cass": { + "machine": "zx81", + "lua_script": "zx81_cass.lua", + "media": "cass1" + } +} diff --git a/package/batocera/core/batocera-configgen/data/mame/messSystems.csv b/package/batocera/core/batocera-configgen/data/mame/messSystems.csv deleted file mode 100644 index e9593b158c4..00000000000 --- a/package/batocera/core/batocera-configgen/data/mame/messSystems.csv +++ /dev/null @@ -1,65 +0,0 @@ -adam;adam;cass; -advision;advision;cart; -beena;beena;cart; -apfm1000;apfm1000;cart; -apple2;apple2ee;flop1; -apple2gs;apple2gs;flop3; -arcadia;arcadia;cart; -archimedes;aa4401;flop; -astrocade;astrocde;cart; -atom;atom;flop1;'\n*DOS\n*DIR\n*CAT\n*RUN"' -bbcmicro;bbcb;flop1; -pcw;pcw8256;flop; -bk;bk001001;cass;'LOAD\n' -camplynx;lynx48k;cass;'mload""' -loopy;casloopy;cart; -cdi;cdimono1;cdrm; -cgenie;cgenie;cass;'\n\nCLOAD\n' -coco;coco3;cart; -dragon64;dragon64;cart; -mc10;mc10;cass;'CLOAD\n' -crvision;crvision;cart; -ctvboy;ctvboy;cart; -electron;electron64;cass;'*T.\nCH.""\n' -enterprise;ep128;flop; -tvc;tvc64p;cass;'LOAD\n' -fm7;fm7;flop1; -fmtowns;fmtmarty;cdrm; -gamate;gamate;cart; -gameandwatch;;; -gamecom;gamecom;cart1; -gamepock;gamepock;cart; -gmaster;gmaster;cart; -gp32;gp32;memc; -laser310;laser310;dump; -lcdgames;;; -macintosh;maclc3;flop1; -megaduck;megaduck;cart; -mz2000;mz2000;cass;'L\n' -mz2500;mz2500;flop1; -mz700;mz700;cass;'L\n' -mz800;mz800;cass;'L\n' -mz80k;mz80k;cass;'L\n' -oricatmos;orica;cass;'\nCLOAD\"\"\n' -pc60;pc6001;cart1; -pdp1;pdp1;ptap1; -pv1000;pv1000;cart; -pv2000;pv2000;cart; -pc80;pc8001mk2sr;flop1; -rx78;rx78;cart; -samcoupe;samcoupe;flop1; -sc3000;sc3000;cart; -segaai;segaai;card; -socrates;socrates;cart; -sv8000;sv8000;cart; -supracan;supracan;cart; -ti99;ti99_4a;cart; -trs80;trs80m3;quik; -tutor;tutor;cart; -tvgames;;; -uzebox;uzebox;cart; -vc4000;vc4000;cart; -vectrex;vectrex;cart; -vgmplay;vgmplay;quik; -vsmile;vsmile;cart; -xegs;xegs;cart; diff --git a/package/batocera/core/batocera-configgen/data/mame/messSystems.json b/package/batocera/core/batocera-configgen/data/mame/messSystems.json new file mode 100644 index 00000000000..b0487876498 --- /dev/null +++ b/package/batocera/core/batocera-configgen/data/mame/messSystems.json @@ -0,0 +1,345 @@ +{ + "adam": { + ".ddp": "adam_cass", + ".dsk": "adam_flop", + "*": "adam" + }, + "advision": { + ".bin": "advision", + "*": "advision" + }, + "apfm1000": { + ".bin": "apfm1000", + ".rom": "apfm1000", + "*": "apfm1000" + }, + "apple2": { + "*": "apple2" + }, + "apple2gs": { + "*": "apple2gs" + }, + "aquarius": { + ".caq": "aquarius_cass", + ".bin": "aquarius_cart", + ".rom": "aquarius_cart", + "*": "aquarius_cart" + }, + "arcadia": { + ".bin": "arcadia", + "*": "arcadia" + }, + "archimedes": { + ".adf": "archimedes", + ".apd": "archimedes", + "*": "archimedes" + }, + "astrocade": { + ".bin": "astrocde", + "*": "astrocde" + }, + "atom": { + ".1dd": "atom_flop", + ".40t": "atom_flop", + ".cqi": "atom_flop", + ".cqm": "atom_flop", + ".d77": "atom_flop", + ".d88": "atom_flop", + ".dfi": "atom_flop", + ".dsk": "atom_flop", + ".hfe": "atom_flop", + ".imd": "atom_flop", + ".mfi": "atom_flop", + ".mfm": "atom_flop", + ".td0": "atom_flop", + "*": "atom" + }, + "bbcmicro": { + ".uef": "bbc_cass", + ".adl": "bbcb_flop", + ".ads": "bbcb_flop", + ".dsd": "bbcb_flop", + ".ssd": "bbcb_flop", + "*": "bbcb_flop" + }, + "beena": { + "*": "beena" + }, + "bk": { + ".wav": "bk0010", + "*": "bk0010" + }, + "camplynx": { + ".tap": "camplynx_cass", + "*": "camplynx_cass" + }, + "cdi": { + "*": "cdi" + }, + "cgenie": { + ".cas": "cgenie_cass", + "*": "cgenie_cass" + }, + "coco": { + ".dsk": "coco_flop", + ".cas": "coco_cass", + "*": "coco_cart" + }, + "crvision": { + ".bin": "crvision", + "*": "crvision" + }, + "ctvboy": { + "*": "ctvboy" + }, + "dragon64": { + ".cas": "dragon_cass", + ".wav": "dragon_cass", + ".dmk": "dragon_flop", + ".dsk": "dragon_flop", + ".vdk": "dragon_flop", + ".1": "dragon_cart", + ".2": "dragon_cart", + ".ic1": "dragon_cart", + ".ic2": "dragon_cart", + ".rom": "dragon_cart", + "*": "dragon_cart" + }, + "electron": { + ".uef": "electron_cass", + "*": "electron_cass" + }, + "enterprise": { + "*": "ep128" + }, + "fm7": { + ".t77": "fm7_cass", + "*": "fm7_flop" + }, + "fmtowns": { + "*": "fmtowns_cd" + }, + "gamate": { + ".bin": "gamate", + "*": "gamate" + }, + "gamecom": { + ".bin": "gamecom", + "*": "gamecom" + }, + "gamepock": { + ".bin": "gamepock", + "*": "gamepock" + }, + "gmaster": { + ".bin": "gmaster", + "*": "gmaster" + }, + "gp32": { + ".smc": "gp32", + "*": "gp32" + }, + "laser310": { + "*": "laser310" + }, + "loopy": { + "*": "loopy" + }, + "macintosh": { + ".image": "mac_flop", + ".img": "mac_flop", + "*": "mac_flop" + }, + "mc10": { + ".cas": "mc10", + ".c10": "mc10", + "*": "mc10" + }, + "megaduck": { + ".bin": "megaduck", + "*": "megaduck" + }, + "msx1": { + ".cas": "msx1_cass", + ".wav": "msx1_cass", + ".dmk": "msx1_flop", + ".dsk": "msx1_flop", + ".bin": "msx1_cart", + ".mx1": "msx1_cart", + ".rom": "msx1_cart", + "*": "msx1_cart" + }, + "msx2": { + ".cas": "msx2_cass", + ".dmk": "msx2_flop", + ".dsk": "msx2_flop", + ".bin": "msx2_cart", + ".mx2": "msx2_cart", + ".rom": "msx2_cart", + "*": "msx2_cart" + }, + "msx2+": { + ".dsk": "msx2p_flop", + ".bin": "msx2p_cart", + ".rom": "msx2p_cart", + "*": "msx2p_cart" + }, + "msxturbor": { + ".dsk": "msxr_flop", + ".bin": "msxr_cart", + ".rom": "msxr_cart", + "*": "msxr_cart" + }, + "mtx512": { + ".wav": "mtx_cass", + ".imd": "mtx_flop", + ".rom": "mtx_rom", + "*": "mtx_cass" + }, + "mz2000": { + ".flac": "mz2000_cass", + ".mzt": "mz2000_cass", + ".wav": "mz2000_cass", + "*": "mz2000_cass" + }, + "mz2500": { + ".d88": "mz2500_flop", + ".hdm": "mz2500_flop", + ".mfi": "mz2500_flop", + "*": "mz2500_flop" + }, + "mz700": { + ".mzf": "mz700_cass", + ".wav": "mz700_cass", + "*": "mz700_cass" + }, + "mz800": { + ".mzf": "mz800_cass", + "*": "mz800_cass" + }, + "mz80k": { + ".m12": "mz80k_cass", + ".mzf": "mz80k_cass", + ".wav": "mz80k_cass", + "*": "mz80k_cass" + }, + "oricatmos": { + ".mp3": "oric1_cass", + ".tap": "oric1_cass", + ".wav": "oric1_cass", + "*": "oric1_cass" + }, + "pc60": { + ".12": "pc6001_cart", + ".bin": "pc6001_cart", + ".rom": "pc6001_cart", + "*": "pc6001_cart" + }, + "pc80": { + ".d80": "pc8801_flop", + ".d88": "pc8801_flop", + ".img": "pc8801_flop", + ".mfm": "pc8801_flop", + "*": "pc8801_flop" + }, + "pcw": { + ".dsk": "pcw", + "*": "pcw" + }, + "pdp1": { + "*": "pdp1" + }, + "pv1000": { + ".bin": "pv1000", + ".rom": "pv1000", + "*": "pv1000" + }, + "pv2000": { + ".bin": "pv2000", + ".rom": "pv2000", + "*": "pv2000" + }, + "rx78": { + ".bin": "rx78_cart", + "*": "rx78_cart" + }, + "samcoupe": { + ".dsk": "samcoupe_flop", + ".mgt": "samcoupe_flop", + "*": "samcoupe_flop" + }, + "sc3000": { + ".bin": "sc3000_cart", + ".sg": "sc3000_cart", + ".wav": "sc3000_cass", + "*": "sc3000_cart" + }, + "segaai": { + ".bin": "segaai", + ".flac": "segaai", + "*": "segaai" + }, + "socrates": { + ".u1": "socrates", + "*": "socrates" + }, + "supracan": { + ".bin": "supracan", + "*": "supracan" + }, + "sv8000": { + ".bin": "sv8000", + "*": "sv8000" + }, + "ti99": { + ".rpk": "ti99_cart", + "*": "ti99_cart" + }, + "trs80": { + ".cas": "trs80_cass", + ".wav": "trs80_cass", + ".cmd": "trs80_quik", + "*": "trs80_quik" + }, + "tutor": { + ".bin": "tutor", + ".ic1": "tutor", + ".u1": "tutor", + "*": "tutor" + }, + "tvc": { + ".cas": "tvc_cass", + ".dsk": "tvc_flop", + "*": "tvc_cass" + }, + "uzebox": { + ".bin": "uzebox", + ".uze": "uzebox", + "*": "uzebox" + }, + "vc4000": { + ".bin": "vc4000", + "*": "vc4000" + }, + "vectrex": { + ".bin": "vectrex", + "*": "vectrex" + }, + "vgmplay": { + ".vgm": "vgmplay", + ".vgz": "vgmplay", + ".txt": "vgmplay", + "*": "vgmplay" + }, + "vis": { + "*": "vis" + }, + "vsmile": { + "*": "vsmile" + }, + "xegs": { + ".bin": "xegs", + ".rom": "xegs", + "*": "xegs" + } +} diff --git a/package/batocera/emulationstation/batocera-es-system/es_systems.yml b/package/batocera/emulationstation/batocera-es-system/es_systems.yml index 8e534ee30bd..622b59cd1c6 100644 --- a/package/batocera/emulationstation/batocera-es-system/es_systems.yml +++ b/package/batocera/emulationstation/batocera-es-system/es_systems.yml @@ -625,9 +625,9 @@ sc3000: theme: sc-3000 emulators: libretro: - mame: { requireAnyOf: [BR2_PACKAGE_LIBRETRO_MAME] } + mess: { requireAnyOf: [BR2_PACKAGE_LIBRETRO_MAME] } mame: - mame: { requireAnyOf: [BR2_PACKAGE_MAME] } + mess: { requireAnyOf: [BR2_PACKAGE_MAME] } comment_en: | SC-3000 cartridges are compatible with SG-1000. Cassette tapes (.wav, .cas, .bit) are SC-3000 exclusive. @@ -642,9 +642,9 @@ segaai: theme: segaai emulators: libretro: - mame: { requireAnyOf: [BR2_PACKAGE_LIBRETRO_MAME] } + mess: { requireAnyOf: [BR2_PACKAGE_LIBRETRO_MAME] } mame: - mame: { requireAnyOf: [BR2_PACKAGE_MAME] } + mess: { requireAnyOf: [BR2_PACKAGE_MAME] } comment_en: | BIOS: segaai.zip in bios/mame/ Software on Sega My Cards (.bin) or cassette tapes (.wav, .flac, .cas). @@ -659,9 +659,9 @@ beena: theme: beena emulators: libretro: - mame: { requireAnyOf: [BR2_PACKAGE_LIBRETRO_MAME] } + mess: { requireAnyOf: [BR2_PACKAGE_LIBRETRO_MAME] } mame: - mame: { requireAnyOf: [BR2_PACKAGE_MAME] } + mess: { requireAnyOf: [BR2_PACKAGE_MAME] } comment_en: | BIOS: beena.zip in bios/mame/ Successor to the Sega Pico. Cartridge-based educational console. @@ -1135,9 +1135,9 @@ xegs: group: atari8bit emulators: libretro: - mame: { requireAnyOf: [BR2_PACKAGE_LIBRETRO_MAME] } + mess: { requireAnyOf: [BR2_PACKAGE_LIBRETRO_MAME] } mame: - mame: { requireAnyOf: [BR2_PACKAGE_MAME] } + mess: { requireAnyOf: [BR2_PACKAGE_MAME] } comment_en: | Requires MAME BIOS file xegs.zip Software list mode recommended @@ -1484,9 +1484,9 @@ vectrex: emulators: libretro: vecx: { requireAnyOf: [BR2_PACKAGE_LIBRETRO_VECX] } - mame: { requireAnyOf: [BR2_PACKAGE_LIBRETRO_MAME] } + mess: { requireAnyOf: [BR2_PACKAGE_LIBRETRO_MAME] } mame: - mame: { requireAnyOf: [BR2_PACKAGE_MAME] } + mess: { requireAnyOf: [BR2_PACKAGE_MAME] } lutro: @@ -1604,9 +1604,9 @@ pcw: extensions: [mfi, dfi, mfm, td0, imd, 86f, d77, d88, 1dd, cqm, cqi, dsk, zip, 7z] emulators: libretro: - mame: { requireAnyOf: [BR2_PACKAGE_LIBRETRO_MAME] } + mess: { requireAnyOf: [BR2_PACKAGE_LIBRETRO_MAME] } mame: - mame: { requireAnyOf: [BR2_PACKAGE_MAME] } + mess: { requireAnyOf: [BR2_PACKAGE_MAME] } comment_en: | Requires MAME BIOS files pcw8256.zip (and pcw9512.zip for the 9512 model). Games are floppy disk images (.dsk). Most games are self-booting and don't require a separate system disk. @@ -1626,6 +1626,8 @@ msx1: openmsx: { requireAnyOf: [BR2_PACKAGE_OPENMSX], incompatible_extensions: [m3u] } clk: clk: { requireAnyOf: [BR2_PACKAGE_CLK], incompatible_extensions: [ 7z ] } + mame: + mess: { requireAnyOf: [BR2_PACKAGE_MAME], incompatible_extensions: [ogv, openmsx } comment_en: | ## BIOS ## @@ -1825,6 +1827,8 @@ msx2+: fmsx: { requireAnyOf: [BR2_PACKAGE_LIBRETRO_FMSX], incompatible_extensions: [openmsx] } openmsx: openmsx: { requireAnyOf: [BR2_PACKAGE_OPENMSX], incompatible_extensions: [m3u] } + mame: + mess: { requireAnyOf: [BR2_PACKAGE_MAME], incompatible_extensions: [ogv, openmsx } comment_en: | ## BIOS ## @@ -1922,6 +1926,8 @@ msxturbor: bluemsx: { requireAnyOf: [BR2_PACKAGE_LIBRETRO_BLUEMSX], incompatible_extensions: [openmsx] } openmsx: openmsx: { requireAnyOf: [BR2_PACKAGE_OPENMSX] } + mame: + mess: { requireAnyOf: [BR2_PACKAGE_MAME], incompatible_extensions: [ogv, openmsx } mz2000: name: Sharp MZ-2000 @@ -1931,9 +1937,9 @@ mz2000: extensions: [mzf, mzt, m12, wav, d88, dsk, zip, 7z] emulators: libretro: - mame: { requireAnyOf: [BR2_PACKAGE_LIBRETRO_MAME] } + mess: { requireAnyOf: [BR2_PACKAGE_LIBRETRO_MAME] } mame: - mame: { requireAnyOf: [BR2_PACKAGE_MAME] } + mess: { requireAnyOf: [BR2_PACKAGE_MAME] } comment_en: | Requires MAME BIOS file mz2000.zip Using software list mode is recommended. @@ -1946,9 +1952,9 @@ mz2500: extensions: [d88, dsk, mfi, dfi, hfe, mfm, td0, imd, d77, 1dd, cqm, cqi, zip, 7z] emulators: libretro: - mame: { requireAnyOf: [BR2_PACKAGE_LIBRETRO_MAME] } + mess: { requireAnyOf: [BR2_PACKAGE_LIBRETRO_MAME] } mame: - mame: { requireAnyOf: [BR2_PACKAGE_MAME] } + mess: { requireAnyOf: [BR2_PACKAGE_MAME] } comment_en: | Requires MAME BIOS file mz2500.zip Using software list mode is recommended. @@ -1961,9 +1967,9 @@ mz700: extensions: [mzf, mzt, m12, wav, zip, 7z] emulators: libretro: - mame: { requireAnyOf: [BR2_PACKAGE_LIBRETRO_MAME] } + mess: { requireAnyOf: [BR2_PACKAGE_LIBRETRO_MAME] } mame: - mame: { requireAnyOf: [BR2_PACKAGE_MAME] } + mess: { requireAnyOf: [BR2_PACKAGE_MAME] } comment_en: | Requires MAME BIOS file mz700.zip Using software list mode is recommended. @@ -1976,9 +1982,9 @@ mz800: extensions: [mzf, mzt, m12, wav, zip, 7z] emulators: libretro: - mame: { requireAnyOf: [BR2_PACKAGE_LIBRETRO_MAME] } + mess: { requireAnyOf: [BR2_PACKAGE_LIBRETRO_MAME] } mame: - mame: { requireAnyOf: [BR2_PACKAGE_MAME] } + mess: { requireAnyOf: [BR2_PACKAGE_MAME] } comment_en: | Requires MAME BIOS file mz800.zip Using software list mode is recommended. @@ -1991,9 +1997,9 @@ mz80k: extensions: [mzf, mzt, m12, wav, zip, 7z] emulators: libretro: - mame: { requireAnyOf: [BR2_PACKAGE_LIBRETRO_MAME] } + mess: { requireAnyOf: [BR2_PACKAGE_LIBRETRO_MAME] } mame: - mame: { requireAnyOf: [BR2_PACKAGE_MAME] } + mess: { requireAnyOf: [BR2_PACKAGE_MAME] } comment_en: | Requires MAME BIOS file mz80k.zip Using software list mode is recommended. @@ -2046,9 +2052,9 @@ bk: emulators: libretro: bk: { requireAnyOf: [BR2_PACKAGE_LIBRETRO_BK] } - mame: { requireAnyOf: [BR2_PACKAGE_LIBRETRO_MAME] } + mess: { requireAnyOf: [BR2_PACKAGE_LIBRETRO_MAME] } mame: - mame: { requireAnyOf: [BR2_PACKAGE_MAME] } + mess: { requireAnyOf: [BR2_PACKAGE_MAME] } comment_en: | BK-0010/0011 was a Soviet home computer series from Elektronika. Supports BK-0010, BK-0010.01, BK-0011(M) and Terak 8510/a emulation. @@ -2143,10 +2149,10 @@ apple2: extensions: [nib, do, po, dsk, mfi, dfi, rti, edd, woz, wav, zip, 7z, chd, hdv, 2mg] emulators: libretro: - mame: { requireAnyOf: [BR2_PACKAGE_LIBRETRO_MAME] } + mess: { requireAnyOf: [BR2_PACKAGE_LIBRETRO_MAME] } applewin: { requireAnyOf: [BR2_PACKAGE_APPLEWIN] } mame: - mame: { requireAnyOf: [BR2_PACKAGE_MAME] } + mess: { requireAnyOf: [BR2_PACKAGE_MAME] } gsplus: gsplus: { requireAnyOf: [BR2_PACKAGE_GSPLUS], incompatible_extensions: [mfi, dfi, rti, edd, woz, wav, zip, 7z, chd, hdv, 2mg] } applewin: @@ -2622,9 +2628,9 @@ fmtowns: platform: fmtowns emulators: libretro: - mame: { requireAnyOf: [BR2_PACKAGE_LIBRETRO_MAME], incompatible_extensions: [xdf, m3u] } + mess: { requireAnyOf: [BR2_PACKAGE_LIBRETRO_MAME], incompatible_extensions: [xdf, m3u] } mame: - mame: { requireAnyOf: [BR2_PACKAGE_MAME], incompatible_extensions: [xdf, m3u] } + mess: { requireAnyOf: [BR2_PACKAGE_MAME], incompatible_extensions: [xdf, m3u] } tsugaru: tsugaru: { requireAnyOf: [BR2_PACKAGE_TSUGARU], incompatible_extensions: [chd, toc, nrg, gdi, cdr, mfi, dfi, hfe, mfm, td0, imd, 1dd, cqm, cqi, dsk, zip, 7z] } @@ -3296,9 +3302,9 @@ uzebox: emulators: libretro: uzem: { requireAnyOf: [BR2_PACKAGE_LIBRETRO_UZEM], incompatible_extensions: [bin, zip] } - mame: { requireAnyOf: [BR2_PACKAGE_LIBRETRO_MAME] } + mess: { requireAnyOf: [BR2_PACKAGE_LIBRETRO_MAME] } mame: - mame: { requireAnyOf: [BR2_PACKAGE_MAME] } + mess: { requireAnyOf: [BR2_PACKAGE_MAME] } cdi: name: CD-i @@ -3309,9 +3315,9 @@ cdi: emulators: libretro: same_cdi: { requireAnyOf: [BR2_PACKAGE_LIBRETRO_SAME_CDI], incompatible_extensions: [cue, toc, nrg, gdi, cdr] } - mame: { requireAnyOf: [BR2_PACKAGE_LIBRETRO_MAME] } + mess: { requireAnyOf: [BR2_PACKAGE_LIBRETRO_MAME] } mame: - mame: { requireAnyOf: [BR2_PACKAGE_MAME] } + mess: { requireAnyOf: [BR2_PACKAGE_MAME] } comment_en: | Requires MAME BIOS file cdimono1.zip comment_br: | @@ -3325,9 +3331,9 @@ advision: extensions: [bin, zip, 7z] emulators: libretro: - mame: { requireAnyOf: [BR2_PACKAGE_LIBRETRO_MAME] } + mess: { requireAnyOf: [BR2_PACKAGE_LIBRETRO_MAME] } mame: - mame: { requireAnyOf: [BR2_PACKAGE_MAME] } + mess: { requireAnyOf: [BR2_PACKAGE_MAME] } comment_en: | Requires MAME BIOS file advision.zip comment_br: | @@ -3353,10 +3359,10 @@ megaduck: extensions: [bin, zip, 7z] emulators: libretro: - mame: { requireAnyOf: [BR2_PACKAGE_LIBRETRO_MAME] } + mess: { requireAnyOf: [BR2_PACKAGE_LIBRETRO_MAME] } sameduck: { requireAnyOf: [BR2_PACKAGE_LIBRETRO_SAMEDUCK] } mame: - mame: { requireAnyOf: [BR2_PACKAGE_MAME] } + mess: { requireAnyOf: [BR2_PACKAGE_MAME] } pv1000: name: PV-1000 @@ -3366,9 +3372,9 @@ pv1000: extensions: [bin, zip, 7z] emulators: libretro: - mame: { requireAnyOf: [BR2_PACKAGE_LIBRETRO_MAME] } + mess: { requireAnyOf: [BR2_PACKAGE_LIBRETRO_MAME] } mame: - mame: { requireAnyOf: [BR2_PACKAGE_MAME] } + mess: { requireAnyOf: [BR2_PACKAGE_MAME] } pv2000: name: PV-2000 @@ -3378,9 +3384,9 @@ pv2000: extensions: [bin, cas, zip, 7z] emulators: libretro: - mame: { requireAnyOf: [BR2_PACKAGE_LIBRETRO_MAME] } + mess: { requireAnyOf: [BR2_PACKAGE_LIBRETRO_MAME] } mame: - mame: { requireAnyOf: [BR2_PACKAGE_MAME] } + mess: { requireAnyOf: [BR2_PACKAGE_MAME] } comment_en: | Requires MAME BIOS file pv2000.zip comment_br: | @@ -3394,9 +3400,9 @@ pc80: extensions: [d77, d88, 1dd, dsk, n80, bin, zip, 7z] emulators: libretro: - mame: { requireAnyOf: [BR2_PACKAGE_LIBRETRO_MAME] } + mess: { requireAnyOf: [BR2_PACKAGE_LIBRETRO_MAME] } mame: - mame: { requireAnyOf: [BR2_PACKAGE_MAME] } + mess: { requireAnyOf: [BR2_PACKAGE_MAME] } comment_en: | Requires MAME BIOS file pc8001mk2sr.zip comment_br: | @@ -3410,9 +3416,9 @@ gamate: extensions: [bin, zip, 7z] emulators: libretro: - mame: { requireAnyOf: [BR2_PACKAGE_LIBRETRO_MAME] } + mess: { requireAnyOf: [BR2_PACKAGE_LIBRETRO_MAME] } mame: - mame: { requireAnyOf: [BR2_PACKAGE_MAME] } + mess: { requireAnyOf: [BR2_PACKAGE_MAME] } comment_en: | Requires MAME BIOS file gamate.zip comment_br: | @@ -3427,9 +3433,9 @@ crvision: extensions: [bin, rom, zip, 7z] emulators: libretro: - mame: { requireAnyOf: [BR2_PACKAGE_LIBRETRO_MAME] } + mess: { requireAnyOf: [BR2_PACKAGE_LIBRETRO_MAME] } mame: - mame: { requireAnyOf: [BR2_PACKAGE_MAME] } + mess: { requireAnyOf: [BR2_PACKAGE_MAME] } comment_en: | Requires MAME BIOS file crvision.zip comment_br: | @@ -3443,9 +3449,9 @@ ctvboy: extensions: [bin, zip, 7z] emulators: libretro: - mame: { requireAnyOf: [BR2_PACKAGE_LIBRETRO_MAME] } + mess: { requireAnyOf: [BR2_PACKAGE_LIBRETRO_MAME] } mame: - mame: { requireAnyOf: [BR2_PACKAGE_MAME] } + mess: { requireAnyOf: [BR2_PACKAGE_MAME] } comment_en: | MAME 0.262+ required @@ -3457,9 +3463,9 @@ laser310: extensions: [vz, wav, cas, zip, 7z] emulators: libretro: - mame: { requireAnyOf: [BR2_PACKAGE_LIBRETRO_MAME] } + mess: { requireAnyOf: [BR2_PACKAGE_LIBRETRO_MAME] } mame: - mame: { requireAnyOf: [BR2_PACKAGE_MAME] } + mess: { requireAnyOf: [BR2_PACKAGE_MAME] } comment_en: | Requires MAME BIOS file laser310.zip Using snapshot (.vz) files is recommended. @@ -3475,9 +3481,9 @@ socrates: extensions: [bin, zip, 7z] emulators: libretro: - mame: { requireAnyOf: [BR2_PACKAGE_LIBRETRO_MAME] } + mess: { requireAnyOf: [BR2_PACKAGE_LIBRETRO_MAME] } mame: - mame: { requireAnyOf: [BR2_PACKAGE_MAME] } + mess: { requireAnyOf: [BR2_PACKAGE_MAME] } comment_en: | Requires MAME BIOS file socrates.zip Software list mode recommended @@ -3509,9 +3515,9 @@ supracan: extensions: [bin, zip, 7z] emulators: libretro: - mame: { requireAnyOf: [BR2_PACKAGE_LIBRETRO_MAME] } + mess: { requireAnyOf: [BR2_PACKAGE_LIBRETRO_MAME] } mame: - mame: { requireAnyOf: [BR2_PACKAGE_MAME] } + mess: { requireAnyOf: [BR2_PACKAGE_MAME] } comment_en: | Sound support is preliminary comment_br: | @@ -3525,9 +3531,9 @@ gamecom: extensions: [bin, tgc, zip, 7z] emulators: libretro: - mame: { requireAnyOf: [BR2_PACKAGE_LIBRETRO_MAME] } + mess: { requireAnyOf: [BR2_PACKAGE_LIBRETRO_MAME] } mame: - mame: { requireAnyOf: [BR2_PACKAGE_MAME] } + mess: { requireAnyOf: [BR2_PACKAGE_MAME] } comment_en: | Requires MAME BIOS file gamecom.zip comment_br: | @@ -3541,9 +3547,9 @@ gamepock: extensions: [bin, zip, 7z] emulators: libretro: - mame: { requireAnyOf: [BR2_PACKAGE_LIBRETRO_MAME] } + mess: { requireAnyOf: [BR2_PACKAGE_LIBRETRO_MAME] } mame: - mame: { requireAnyOf: [BR2_PACKAGE_MAME] } + mess: { requireAnyOf: [BR2_PACKAGE_MAME] } comment_en: | Requires MAME BIOS file gamepock.zip comment_br: | @@ -3557,9 +3563,9 @@ fm7: extensions: [wav, t77, mfi, dfi, hfe, mfm, td0, imd, d77, d88, 1dd, cqm, cqi, dsk, zip, 7z] emulators: libretro: - mame: { requireAnyOf: [BR2_PACKAGE_LIBRETRO_MAME] } + mess: { requireAnyOf: [BR2_PACKAGE_LIBRETRO_MAME] } mame: - mame: { requireAnyOf: [BR2_PACKAGE_MAME] } + mess: { requireAnyOf: [BR2_PACKAGE_MAME] } comment_en: | Requires MAME BIOS file fm7.zip, and optionally fm77av.zip for FM-77AV support. comment_br: | @@ -3574,9 +3580,9 @@ archimedes: extensions: [mfi, dfi, hfe, mfm, td0, imd, d77, d88, 1dd, cqm, cqi, dsk, ima, img, ufi, 360, ipf, adf, apd, jfd, ads, adm, adl, ssd, bbc, dsd, st, msa, chd, zip, 7z] emulators: libretro: - mame: { requireAnyOf: [BR2_PACKAGE_LIBRETRO_MAME] } + mess: { requireAnyOf: [BR2_PACKAGE_LIBRETRO_MAME] } mame: - mame: { requireAnyOf: [BR2_PACKAGE_MAME] } + mess: { requireAnyOf: [BR2_PACKAGE_MAME] } clk: clk: { requireAnyOf: [BR2_PACKAGE_CLK], incompatible_extensions: [ 7z ] } comment_en: | @@ -3596,9 +3602,9 @@ atom: extensions: [wav, tap, csw, uef, mfi, dfi, hfe, mfm, td0, imd, d77, d88, 1dd, cqm, cqi, dsk, 40t, atm, bin, rom, zip, 7z] emulators: libretro: - mame: { requireAnyOf: [BR2_PACKAGE_LIBRETRO_MAME] } + mess: { requireAnyOf: [BR2_PACKAGE_LIBRETRO_MAME] } mame: - mame: { requireAnyOf: [BR2_PACKAGE_MAME] } + mess: { requireAnyOf: [BR2_PACKAGE_MAME] } comment_en: | Requires MAME BIOS file atom.zip Using software list mode is recommended. Software list cassettes don't work but floppies do. @@ -3616,9 +3622,9 @@ electron: extensions: [wav, csw, uef, mfi, dfi, hfe, mfm, td0, imd, d77, d88, 1dd, cqm, cqi, dsk, ssd, bbc, img, dsd, adf, ads, adm, adl, rom, bin, zip, 7z] emulators: libretro: - mame: { requireAnyOf: [BR2_PACKAGE_LIBRETRO_MAME] } + mess: { requireAnyOf: [BR2_PACKAGE_LIBRETRO_MAME] } mame: - mame: { requireAnyOf: [BR2_PACKAGE_MAME] } + mess: { requireAnyOf: [BR2_PACKAGE_MAME] } clk: clk: { requireAnyOf: [BR2_PACKAGE_CLK], incompatible_extensions: [ 7z ] } comment_en: | @@ -3638,9 +3644,9 @@ apfm1000: extensions: [bin, zip, 7z] emulators: libretro: - mame: { requireAnyOf: [BR2_PACKAGE_LIBRETRO_MAME] } + mess: { requireAnyOf: [BR2_PACKAGE_LIBRETRO_MAME] } mame: - mame: { requireAnyOf: [BR2_PACKAGE_MAME] } + mess: { requireAnyOf: [BR2_PACKAGE_MAME] } comment_en: | Requires MAME BIOS file apfm1000.zip comment_br: | @@ -3655,9 +3661,9 @@ bbcmicro: extensions: [mfi, dfi, hfe, mfm, td0, imd, d77, d88, 1dd, cqm, cqi, dsk, ima, img, ufi, 360, ipf, ssd, bbc, dsd, adf, ads, adm, adl, fsd, wav, tap, bin, zip, 7z] emulators: libretro: - mame: { requireAnyOf: [BR2_PACKAGE_LIBRETRO_MAME] } + mess: { requireAnyOf: [BR2_PACKAGE_LIBRETRO_MAME] } mame: - mame: { requireAnyOf: [BR2_PACKAGE_MAME] } + mess: { requireAnyOf: [BR2_PACKAGE_MAME] } clk: clk: { requireAnyOf: [BR2_PACKAGE_CLK] } comment_en: | @@ -3676,9 +3682,9 @@ camplynx: extensions: [wav, tap, ldf, zip, 7z] emulators: libretro: - mame: { requireAnyOf: [BR2_PACKAGE_LIBRETRO_MAME] } + mess: { requireAnyOf: [BR2_PACKAGE_LIBRETRO_MAME] } mame: - mame: { requireAnyOf: [BR2_PACKAGE_MAME] } + mess: { requireAnyOf: [BR2_PACKAGE_MAME] } comment_en: | Requires MAME BIOS files lynx128k.zip, lynx96k.zip, lynx48k.zip Using software list mode is recommended. @@ -3694,9 +3700,9 @@ adam: extensions: [wav, ddp, mfi, dfi, hfe, mfm, td0, imd, d77, d88, 1dd, cqm, cqi, dsk, rom, col, bin, zip, 7z] emulators: libretro: - mame: { requireAnyOf: [BR2_PACKAGE_LIBRETRO_MAME] } + mess: { requireAnyOf: [BR2_PACKAGE_LIBRETRO_MAME] } mame: - mame: { requireAnyOf: [BR2_PACKAGE_MAME] } + mess: { requireAnyOf: [BR2_PACKAGE_MAME] } comment_en: | Requires the following MAME BIOS files: adam.zip, adam_ddp.zip, adam_fdc.zip, adam_kb.zip, & adam_prn.zip @@ -3712,9 +3718,9 @@ arcadia: extensions: [bin, zip, 7z] emulators: libretro: - mame: { requireAnyOf: [BR2_PACKAGE_LIBRETRO_MAME] } + mess: { requireAnyOf: [BR2_PACKAGE_LIBRETRO_MAME] } mame: - mame: { requireAnyOf: [BR2_PACKAGE_MAME] } + mess: { requireAnyOf: [BR2_PACKAGE_MAME] } sv8000: name: Super Vision 8000 @@ -3724,9 +3730,9 @@ sv8000: extensions: [bin, zip, 7z] emulators: libretro: - mame: { requireAnyOf: [BR2_PACKAGE_LIBRETRO_MAME] } + mess: { requireAnyOf: [BR2_PACKAGE_LIBRETRO_MAME] } mame: - mame: { requireAnyOf: [BR2_PACKAGE_MAME] } + mess: { requireAnyOf: [BR2_PACKAGE_MAME] } gmaster: name: Game Master @@ -3736,9 +3742,9 @@ gmaster: extensions: [bin, zip, 7z] emulators: libretro: - mame: { requireAnyOf: [BR2_PACKAGE_LIBRETRO_MAME] } + mess: { requireAnyOf: [BR2_PACKAGE_LIBRETRO_MAME] } mame: - mame: { requireAnyOf: [BR2_PACKAGE_MAME] } + mess: { requireAnyOf: [BR2_PACKAGE_MAME] } comment_en: | Requires MAME BIOS file gmaster.zip comment_br: | @@ -3753,9 +3759,9 @@ astrocade: extensions: [bin, zip, 7z] emulators: libretro: - mame: { requireAnyOf: [BR2_PACKAGE_LIBRETRO_MAME] } + mess: { requireAnyOf: [BR2_PACKAGE_LIBRETRO_MAME] } mame: - mame: { requireAnyOf: [BR2_PACKAGE_MAME] } + mess: { requireAnyOf: [BR2_PACKAGE_MAME] } comment_en: | Requires MAME BIOS file astrocde.zip comment_br: | @@ -3769,9 +3775,9 @@ ti99: extensions: [rpk, wav, zip, 7z] emulators: libretro: - mame: { requireAnyOf: [BR2_PACKAGE_LIBRETRO_MAME] } + mess: { requireAnyOf: [BR2_PACKAGE_LIBRETRO_MAME] } mame: - mame: { requireAnyOf: [BR2_PACKAGE_MAME] } + mess: { requireAnyOf: [BR2_PACKAGE_MAME] } comment_en: | Requires MAME BIOS file ti99_4a.zip Cartridges need to be in .rpk format and unzipped if not using software lists. @@ -3787,9 +3793,9 @@ tutor: extensions: [bin, wav, zip, 7z] emulators: libretro: - mame: { requireAnyOf: [BR2_PACKAGE_LIBRETRO_MAME] } + mess: { requireAnyOf: [BR2_PACKAGE_LIBRETRO_MAME] } mame: - mame: { requireAnyOf: [BR2_PACKAGE_MAME] } + mess: { requireAnyOf: [BR2_PACKAGE_MAME] } comment_en: | Requires MAME BIOS file tutor.zip To get through the loading menu, R1 will move down and A/East will select. @@ -3805,9 +3811,9 @@ coco: extensions: [wav, cas, dsk, ccc, rom, zip, 7z] emulators: libretro: - mame: { requireAnyOf: [BR2_PACKAGE_LIBRETRO_MAME] } + mess: { requireAnyOf: [BR2_PACKAGE_LIBRETRO_MAME] } mame: - mame: { requireAnyOf: [BR2_PACKAGE_MAME] } + mess: { requireAnyOf: [BR2_PACKAGE_MAME] } xroar: xroar: { requireAnyOf: [BR2_PACKAGE_XROAR] } comment_en: | @@ -3841,9 +3847,9 @@ cgenie: extensions: [cas, wav, zip, 7z] emulators: libretro: - mame: { requireAnyOf: [BR2_PACKAGE_LIBRETRO_MAME] } + mess: { requireAnyOf: [BR2_PACKAGE_LIBRETRO_MAME] } mame: - mame: { requireAnyOf: [BR2_PACKAGE_MAME] } + mess: { requireAnyOf: [BR2_PACKAGE_MAME] } comment_en: | MAME requires BIOS file cgenie.zip comment_br: | @@ -3857,9 +3863,9 @@ dragon64: extensions: [wav, cas, dsk, dmk, ccc, rom, bin, zip, 7z] emulators: libretro: - mame: { requireAnyOf: [BR2_PACKAGE_LIBRETRO_MAME] } + mess: { requireAnyOf: [BR2_PACKAGE_LIBRETRO_MAME] } mame: - mame: { requireAnyOf: [BR2_PACKAGE_MAME] } + mess: { requireAnyOf: [BR2_PACKAGE_MAME] } xroar: xroar: { requireAnyOf: [BR2_PACKAGE_XROAR] } comment_en: | @@ -3890,9 +3896,9 @@ mc10: extensions: [wav, cas, rom, bin, zip, 7z] emulators: libretro: - mame: { requireAnyOf: [BR2_PACKAGE_LIBRETRO_MAME] } + mess: { requireAnyOf: [BR2_PACKAGE_LIBRETRO_MAME] } mame: - mame: { requireAnyOf: [BR2_PACKAGE_MAME] } + mess: { requireAnyOf: [BR2_PACKAGE_MAME] } xroar: xroar: { requireAnyOf: [BR2_PACKAGE_XROAR] } comment_en: | @@ -3909,9 +3915,9 @@ trs80: extensions: [cmd, cas, dsk, dmk, bas, wav, zip, 7z] emulators: libretro: - mame: { requireAnyOf: [BR2_PACKAGE_LIBRETRO_MAME] } + mess: { requireAnyOf: [BR2_PACKAGE_LIBRETRO_MAME] } mame: - mame: { requireAnyOf: [BR2_PACKAGE_MAME] } + mess: { requireAnyOf: [BR2_PACKAGE_MAME] } comment_en: | MAME requires BIOS file trs80.zip (Model I), trs80m3.zip (Model III), or trs80m4.zip (Model 4) @@ -3938,9 +3944,9 @@ vc4000: extensions: [bin, rom, pgm, tvc, zip, 7z] emulators: libretro: - mame: { requireAnyOf: [BR2_PACKAGE_LIBRETRO_MAME] } + mess: { requireAnyOf: [BR2_PACKAGE_LIBRETRO_MAME] } mame: - mame: { requireAnyOf: [BR2_PACKAGE_MAME] } + mess: { requireAnyOf: [BR2_PACKAGE_MAME] } vgmplay: name: Video Game Music Player @@ -3950,9 +3956,9 @@ vgmplay: extensions: [vgm, vgz, zip, 7z] emulators: libretro: - mame: { requireAnyOf: [BR2_PACKAGE_LIBRETRO_MAME] } + mess: { requireAnyOf: [BR2_PACKAGE_LIBRETRO_MAME] } mame: - mame: { requireAnyOf: [BR2_PACKAGE_MAME] } + mess: { requireAnyOf: [BR2_PACKAGE_MAME] } comment_en: | Software lists recommended. QSound can be CPU-intensive. Use the File Manager in the MAME menu to change tracks. @@ -4154,9 +4160,9 @@ samcoupe: samcoupe: samcoupe: { requireAnyOf: [BR2_PACKAGE_SIMCOUPE] } libretro: - mame: { requireAnyOf: [BR2_PACKAGE_LIBRETRO_MAME] } + mess: { requireAnyOf: [BR2_PACKAGE_LIBRETRO_MAME] } mame: - mame: { requireAnyOf: [BR2_PACKAGE_MAME] } + mes: { requireAnyOf: [BR2_PACKAGE_MAME] } abuse: name: Abuse @@ -4225,9 +4231,9 @@ macintosh: emulators: libretro: minivmac: { requireAnyOf: [BR2_PACKAGE_LIBRETRO_MINIVMAC], incompatible_extensions: [7z, mfi, dfi, hfe, mfm, td0, imd, d77, d88, 1dd, cqm, cqi, dsk, ima, img, ufi, ipf, dc42, woz, 2mg, 360, chd, cue, toc, nrg, gdi, iso, cdr, hd, hdv, 2mg, hdi] } - mame: { requireAnyOf: [BR2_PACKAGE_LIBRETRO_MAME] } + mess: { requireAnyOf: [BR2_PACKAGE_LIBRETRO_MAME] } mame: - mame: { requireAnyOf: [BR2_PACKAGE_MAME] } + mess: { requireAnyOf: [BR2_PACKAGE_MAME] } clk: clk: { requireAnyOf: [BR2_PACKAGE_CLK], incompatible_extensions: [ 7z ] } comment_en: | @@ -4528,9 +4534,9 @@ pdp1: extensions: [zip, 7z, tap, rim, drm] emulators: libretro: - mame: { requireAnyOf: [BR2_PACKAGE_LIBRETRO_MAME] } + mess: { requireAnyOf: [BR2_PACKAGE_LIBRETRO_MAME] } mame: - mame: { requireAnyOf: [BR2_PACKAGE_MAME] } + mess: { requireAnyOf: [BR2_PACKAGE_MAME] } comment_en: | Use Control-Enter or Start to start after loading. Loading may take some time, if the green lights are blinking, it is loading. @@ -4606,9 +4612,9 @@ gp32: extensions: [smc, zip, 7z] emulators: libretro: - mame: { requireAnyOf: [BR2_PACKAGE_LIBRETRO_MAME] } + mess: { requireAnyOf: [BR2_PACKAGE_LIBRETRO_MAME] } mame: - mame: { requireAnyOf: [BR2_PACKAGE_MAME] } + mess: { requireAnyOf: [BR2_PACKAGE_MAME] } comment_en: | Requires MAME BIOS file gp32.zip comment_br: | @@ -5295,7 +5301,9 @@ vis: extensions: [chd, cue, toc, nrg, gdi, iso, cdr] emulators: libretro: - mame: { requireAnyOf: [BR2_PACKAGE_LIBRETRO_MAME] } + mess: { requireAnyOf: [BR2_PACKAGE_LIBRETRO_MAME] } + mame: + mess: { requireAnyOf: [BR2_PACKAGE_MAME] } vpinball: name: Visual Pinball X @@ -5757,9 +5765,9 @@ rx78: extensions: [bin, zip, 7z] emulators: libretro: - mame: { requireAnyOf: [BR2_PACKAGE_LIBRETRO_MAME] } + mess: { requireAnyOf: [BR2_PACKAGE_LIBRETRO_MAME] } mame: - mame: { requireAnyOf: [BR2_PACKAGE_MAME] } + mess: { requireAnyOf: [BR2_PACKAGE_MAME] } commanderx16: name: Commander X16 @@ -5973,9 +5981,9 @@ oricatmos: clk: clk: { requireAnyOf: [BR2_PACKAGE_CLK] } libretro: - mame: { requireAnyOf: [BR2_PACKAGE_LIBRETRO_MAME] } + mess: { requireAnyOf: [BR2_PACKAGE_LIBRETRO_MAME] } mame: - mame: { requireAnyOf: [BR2_PACKAGE_MAME] } + mess: { requireAnyOf: [BR2_PACKAGE_MAME] } comment_en: | Emulator for Oric Atmos (and Oric 1) @@ -6168,9 +6176,9 @@ loopy: theme: loopy emulators: libretro: - mame: { requireAnyOf: [BR2_PACKAGE_LIBRETRO_MAME] } + mess: { requireAnyOf: [BR2_PACKAGE_LIBRETRO_MAME] } mame: - mame: { requireAnyOf: [BR2_PACKAGE_MAME] } + mess: { requireAnyOf: [BR2_PACKAGE_MAME] } comment_en: | Requires MAME BIOS file: bios/mame/casloopy.zip @@ -6189,9 +6197,9 @@ pc60: extensions: [bin, cas, p6, d77, d88, dsk, 1dd, mfi, dfi, mfm, td0, imd, cqm, cqi, xdf, hdm, 2hd, fdi, zip, 7z] emulators: mame: - mame: { requireAnyOf: [BR2_PACKAGE_MAME] } + mess: { requireAnyOf: [BR2_PACKAGE_MAME] } libretro: - mame: { requireAnyOf: [BR2_PACKAGE_LIBRETRO_MAME] } + mess: { requireAnyOf: [BR2_PACKAGE_LIBRETRO_MAME] } comment_en: | Requires MAME BIOS files (depending on model): bios/pc6001.zip (PC-6001) @@ -6218,9 +6226,9 @@ enterprise: clk: { requireAnyOf: [BR2_PACKAGE_CLK], incompatible_extensions: [img,dsk,tap,dtf,trn,128,cas,cdt,tzx] } libretro: ep128emu-core: { requireAnyOf: [BR2_PACKAGE_LIBRETRO_EP128EMU_CORE] } - mame: { requireAnyOf: [BR2_PACKAGE_LIBRETRO_MAME] } + mess: { requireAnyOf: [BR2_PACKAGE_LIBRETRO_MAME] } mame: - mame: { requireAnyOf: [BR2_PACKAGE_MAME] } + mess: { requireAnyOf: [BR2_PACKAGE_MAME] } comment_en: | Requires ep128emu BIOS files: bios/ep128emu/roms/exos21.rom (Expandible OS 2.1) @@ -6262,9 +6270,9 @@ tvc: emulators: libretro: ep128emu-core: { requireAnyOf: [BR2_PACKAGE_LIBRETRO_EP128EMU_CORE] } - mame: { requireAnyOf: [BR2_PACKAGE_LIBRETRO_MAME] } + mess: { requireAnyOf: [BR2_PACKAGE_LIBRETRO_MAME] } mame: - mame: { requireAnyOf: [BR2_PACKAGE_MAME] } + mess: { requireAnyOf: [BR2_PACKAGE_MAME] } comment_en: | Requires ep128emu BIOS files: bios/ep128emu/roms/tvc22_sys.rom (TVC system BIOS)