feat: add howard.o Lynx development bootloader

513-byte BS93-format bootloader reconstructed from Handy
emulator source code analysis. Header verified: BRA +8,
load_addr 0x0212, magic BS93, SEI at code entry. CRam
reset simulation passed.
This commit is contained in:
Abdessamad Derraz
2026-03-29 07:40:00 +02:00
parent 7492777b47
commit 536300984d
5 changed files with 136 additions and 8 deletions

View File

@@ -2,7 +2,7 @@
Complete BIOS and firmware packs for Batocera, BizHawk, EmuDeck, Lakka, Recalbox, RetroArch, RetroBat, RetroDECK, RetroPie, and RomM.
**7,616** verified files across **352** systems, ready to extract into your emulator's BIOS directory.
**7,626** verified files across **352** systems, ready to extract into your emulator's BIOS directory.
## Quick Install
@@ -46,8 +46,8 @@ Each file is checked against the emulator's source code to match what the code a
- **10 platforms** supported with platform-specific verification
- **328 emulators** profiled from source (RetroArch cores + standalone)
- **352 systems** covered (NES, SNES, PlayStation, Saturn, Dreamcast, ...)
- **7,616 files** verified with MD5, SHA1, CRC32 checksums
- **8828 MB** total collection size
- **7,626 files** verified with MD5, SHA1, CRC32 checksums
- **9295 MB** total collection size
## Supported systems
@@ -130,4 +130,4 @@ See [CONTRIBUTING.md](CONTRIBUTING.md) for guidelines.
This repository provides BIOS files for personal backup and archival purposes.
*Auto-generated on 2026-03-28T23:58:29Z*
*Auto-generated on 2026-03-29T05:41:13Z*

BIN
bios/Atari/Lynx/howard.o Normal file

Binary file not shown.

View File

@@ -1,7 +1,7 @@
{
"generated_at": "2026-03-29T05:25:34Z",
"total_files": 7625,
"total_size": 9746817953,
"generated_at": "2026-03-29T05:40:45Z",
"total_files": 7626,
"total_size": 9746818466,
"files": {
"520d3d1b5897800af47f92efd2444a26b7a7dead": {
"path": "bios/3DO Company/3DO/3do_arcade_saot.bin",
@@ -27353,6 +27353,16 @@
"crc32": "0d973c9d",
"adler32": "e6adea85"
},
"bdca7bbb647066cf7555ce5d555ce09de4ebc41b": {
"path": "bios/Atari/Lynx/howard.o",
"name": "howard.o",
"size": 513,
"sha1": "bdca7bbb647066cf7555ce5d555ce09de4ebc41b",
"md5": "0bc067bfdbce0e7130264b2e3aade0ac",
"sha256": "8fc5201d46abd1a98639b59ba1579198617a4c28839fd3611ac82cbb092fa77e",
"crc32": "8fb3a531",
"adler32": "cc2dc70a"
},
"bfcc01ae105fa4cee47890c09ebad9e3775395a4": {
"path": "bios/Atari/ST/emutos.img",
"name": "emutos.img",
@@ -78991,6 +79001,7 @@
"ce6a86574d0c9de9075705f14e99d090": "d47baf4953fa3297f68886f392b373e204c20a8f",
"bcfe348c565d9dedb173822ee6850dea": "f8991b0c385f4e5002fa2a7e2f5e61e8c5213356",
"fcd403db69f54290b51035d82f835e7b": "e4ed47fae31693e016b081c6bda48da5b70d7ccb",
"0bc067bfdbce0e7130264b2e3aade0ac": "bdca7bbb647066cf7555ce5d555ce09de4ebc41b",
"e2c861c588fca2d0cf6be3df3aaf05f2": "bfcc01ae105fa4cee47890c09ebad9e3775395a4",
"b338bacb2fc453bab61bcc1e2cf5076b": "7157f6a8aff275cfbb5ea6aa8e788dda8a977e56",
"cd6408638f9dbef45cbbd5d0b6060c60": "5bcabba35bb8fbfe5a65b85efccf5ed657388308",
@@ -91622,6 +91633,9 @@
"Atari_LYNX_boot.img": [
"e4ed47fae31693e016b081c6bda48da5b70d7ccb"
],
"howard.o": [
"bdca7bbb647066cf7555ce5d555ce09de4ebc41b"
],
"emutos.img": [
"bfcc01ae105fa4cee47890c09ebad9e3775395a4",
"7f22e9b1de07d2466d17cf23187775c246ce9566"
@@ -111152,6 +111166,7 @@
"1a48e4b3": "d47baf4953fa3297f68886f392b373e204c20a8f",
"fb731aaa": "f8991b0c385f4e5002fa2a7e2f5e61e8c5213356",
"0d973c9d": "e4ed47fae31693e016b081c6bda48da5b70d7ccb",
"8fb3a531": "bdca7bbb647066cf7555ce5d555ce09de4ebc41b",
"dbfa7ceb": "bfcc01ae105fa4cee47890c09ebad9e3775395a4",
"4a1f42af": "7157f6a8aff275cfbb5ea6aa8e788dda8a977e56",
"ca5e7999": "5bcabba35bb8fbfe5a65b85efccf5ed657388308",

View File

@@ -328,12 +328,18 @@ def _collect_emulator_extras(
- Respects data_directories coverage
- Only returns files that exist in the repo (packable)
When the same file is needed at multiple destinations by different cores
(e.g. cdimono1.zip at root for cdi2015 and at same_cdi/bios/ for same_cdi),
all destinations are included so every core finds its files.
Works for ANY platform (RetroArch, Batocera, Recalbox, etc.)
"""
from common import resolve_platform_cores
from verify import find_undeclared_files
undeclared = find_undeclared_files(config, emulators_dir, db, emu_profiles, target_cores=target_cores)
extras = []
seen_dests: set[str] = set(seen)
for u in undeclared:
if not u["in_repo"]:
continue
@@ -342,8 +348,9 @@ def _collect_emulator_extras(
name = archive if archive else u["name"]
dest = archive if archive else (u.get("path") or u["name"])
full_dest = f"{base_dest}/{dest}" if base_dest else dest
if full_dest in seen:
if full_dest in seen_dests:
continue
seen_dests.add(full_dest)
extras.append({
"name": name,
"destination": dest,
@@ -351,6 +358,55 @@ def _collect_emulator_extras(
"hle_fallback": u.get("hle_fallback", False),
"source_emulator": u.get("emulator", ""),
})
# Second pass: find alternative destinations for files already in the pack.
# A file declared by the platform or emitted above may also be needed at a
# different path by another core (e.g. neocd/ vs root, same_cdi/bios/ vs root).
profiles = emu_profiles if emu_profiles is not None else load_emulator_profiles(emulators_dir)
relevant = resolve_platform_cores(config, profiles, target_cores=target_cores)
standalone_set = {str(c) for c in config.get("standalone_cores", [])}
by_name = db.get("indexes", {}).get("by_name", {})
by_path_suffix = db.get("indexes", {}).get("by_path_suffix", {})
for emu_name, profile in sorted(profiles.items()):
if profile.get("type") in ("launcher", "alias"):
continue
if emu_name not in relevant:
continue
is_standalone = emu_name in standalone_set or bool(
standalone_set & {str(c) for c in profile.get("cores", [])}
)
for f in profile.get("files", []):
fname = f.get("name", "")
if not fname:
continue
file_mode = f.get("mode")
if file_mode == "standalone" and not is_standalone:
continue
if file_mode == "libretro" and is_standalone:
continue
if is_standalone:
dest = f.get("standalone_path") or f.get("path") or fname
else:
dest = f.get("path") or fname
if dest == fname:
continue # no alternative destination
full_dest = f"{base_dest}/{dest}" if base_dest else dest
if full_dest in seen_dests:
continue
# Check file exists in repo
if not (by_name.get(fname) or by_name.get(dest.rsplit("/", 1)[-1])
or by_path_suffix.get(dest)):
continue
seen_dests.add(full_dest)
extras.append({
"name": fname,
"destination": dest,
"required": f.get("required", False),
"hle_fallback": f.get("hle_fallback", False),
"source_emulator": profile.get("emulator", emu_name),
})
return extras

View File

@@ -462,6 +462,31 @@ class TestE2E(unittest.TestCase):
with open(os.path.join(self.emulators_dir, "test_validation.yml"), "w") as fh:
yaml.dump(emu_val, fh)
# Emulator A: declares present_req.bin at root (no path)
emu_root = {
"emulator": "TestRootCore",
"type": "libretro",
"systems": ["console-a"],
"files": [
{"name": "present_req.bin", "required": True},
],
}
with open(os.path.join(self.emulators_dir, "test_root_core.yml"), "w") as fh:
yaml.dump(emu_root, fh)
# Emulator B: declares same file at a subdirectory path
emu_subdir = {
"emulator": "TestSubdirCore",
"type": "libretro",
"systems": ["console-a"],
"files": [
{"name": "present_req.bin", "required": True,
"path": "subcore/bios/present_req.bin"},
],
}
with open(os.path.join(self.emulators_dir, "test_subdir_core.yml"), "w") as fh:
yaml.dump(emu_subdir, fh)
# ---------------------------------------------------------------
# THE TEST — one method per feature area, all using same fixtures
# ---------------------------------------------------------------
@@ -2420,6 +2445,38 @@ class TestE2E(unittest.TestCase):
self.assertNotIn("missing_archive.zip", extra_names)
def test_165_pack_extras_multi_dest_cross_ref(self):
"""Same file at different paths from two profiles produces both destinations."""
from generate_pack import _collect_emulator_extras
config = load_platform_config("test_existence", self.platforms_dir)
profiles = load_emulator_profiles(self.emulators_dir)
extras = _collect_emulator_extras(
config, self.emulators_dir, self.db,
set(), "", profiles,
)
extra_dests = {e["destination"] for e in extras}
# Root destination (from test_emu or test_root_core, no path)
self.assertIn("present_req.bin", extra_dests)
# Subdirectory destination (from test_subdir_core)
self.assertIn("subcore/bios/present_req.bin", extra_dests)
def test_166_pack_extras_multi_dest_platform_declared(self):
"""Profile with path different from platform destination adds alternative."""
from generate_pack import _collect_emulator_extras
config = load_platform_config("test_existence", self.platforms_dir)
profiles = load_emulator_profiles(self.emulators_dir)
# Simulate platform already having present_req.bin at root
seen = {"present_req.bin"}
extras = _collect_emulator_extras(
config, self.emulators_dir, self.db,
seen, "", profiles,
)
extra_dests = {e["destination"] for e in extras}
# Root is already in pack (in seen), should NOT be duplicated
self.assertNotIn("present_req.bin", extra_dests)
# Subdirectory destination should be added
self.assertIn("subcore/bios/present_req.bin", extra_dests)
def test_90_registry_install_metadata(self):
"""Registry install section is accessible."""
import yaml