Launch arguments
← POPSLoader docs · view on GitHub ↗
POPSLoader accepts NHDDL-style launch arguments so a front-end (OSDMenu /
HOSDMenu, wLaunchELF, a forwarder, etc.) can boot it straight to a device
page or auto-launch a specific game. Arguments are parsed in
src/main.cpp parseLaunchArgs(), exposed to Lua via
System.getLaunchArgs(), and acted on in bin/POPSLDR/system.lua.
-bdma=<mode> — pin the BDM Assault driver variant
-bdma=fat32 | usbexfat | mx4sio | mmce | ata stages that BDMA variant for the
launch, overriding Adaptive BDMA's per-device choice. hddexfat is accepted
as an alias for ata.
Adaptive BDMA is on by default (changed 2026-07-28), so the drivers for the
device you launch are staged automatically and this argument is only needed when
you want a specific variant regardless of the device — for example forcing the
exFAT-USB pair on a FAT32 stick, or staging ata from a launcher that does not
route through the exFAT page.
The value is normalised by NormalizeBdmaModeKey. An unrecognised value is
ignored, falling back to the normal adaptive choice, rather than pinning
something bogus and staging the wrong driver pair.
The arguments
| Argument | Effect |
|---|---|
-page=<device> |
Boot straight into that device's game list (when used without -game). |
-mode=<device> |
NHDDL-compatible alias for -page= (identical behavior, same values). |
-game=<selector> |
Auto-launch a game. Must be combined with -page=. |
-bdma=<mode> |
Pin the BDM Assault driver variant for this launch, overriding Adaptive BDMA's per-device choice. |
-retrogem=<val> |
Override RetroGEM Game ID optical emission (1/on/true or 0/off/false). Aliases -retrogem and -noretrogem. |
-debug |
Show an on-screen toast with the parsed args + resolved boot context. |
Each argument is a separate token, exactly like a command line:
mc0:/PS1_POPSLOADER/POPSLOADER.ELF -page=hdd
mc0:/PS1_POPSLOADER/POPSLOADER.ELF -mode=ata
mc0:/PS1_POPSLOADER/POPSLOADER.ELF -page=usb -game=Tekken 3
mc0:/PS1_POPSLOADER/POPSLOADER.ELF -page=hdd -debug
The first token is always POPSLoader's own ELF path; the launcher supplies
it as argv[0]. Pass each flag as its own argument entry — do not join
them into one quoted string (see Front-end notes below).
Accepted -page / -mode values
Values are case-insensitive. Aliases in parentheses resolve to the same
page:
| Page | Accepted values |
|---|---|
| HDD (exFAT) (BDMA ATA) | exfat, ata, ata0, ataN |
| HDD (PFS) (APA) | hdd, hdd0, pfs, apa, apa0 |
| USB | usb, mass |
| MC | mc, memcard (accepted but not navigable — there is no standalone Memory Card page; standard PS2 memory cards are not a separate carousel device) |
| MMCE | mmce |
| MX4SIO | mx4sio, mx4, sdc |
| SMB | smb |
bdma (bare) |
accepted but a no-op — use ata to reach the HDD (exFAT) page; the bare bdma literal maps to no page |
-mode=ata / -page=ata is provided for NHDDL parity (ata is how NHDDL
selects the internal ATA drive); it opens POPSLoader's HDD (exFAT) page
(the BDMA-ATA backend, scene GBDMHDD). For the classic APA/PFS HDD page use
-page=hdd / -page=pfs.
What each argument actually does
-page= / -mode= (open a device's game list)
Boots straight into the requested device's game list (the same screen
you'd reach by scrolling to that page and pressing X) — it loads the
device and switches to its list automatically. Combine with -game= to go
one step further and auto-launch a specific title (see below); -page=
alone just opens the list so you can pick.
Enterable pages: MMCE, MX4SIO, HDD (exFAT), HDD (PFS), USB, SMB.
-page=ata targets the implemented exFAT (BDMA-ATA) page (scene
GBDMHDD); -page=hdd / -page=pfs targets the classic PFS page.
-page=smb opens the SMB (v1) network page (scene GSMBNET), which
connects to the configured share lazily on entry (implemented, CI + Rolling
green, validating on hardware). The bare bdma literal and the i.Link page
are intentionally not wired, so -page=bdma is accepted but has no
effect. An unrecognized value is ignored and the carousel starts on its
default page (MMCE) with no auto-entry.
-game= (auto-launch)
Boots straight into launching a game. Requires both -page= and
-game=. Auto-launch is wired for these pages:
| Page | -game= selector format |
|---|---|
| HDD | <PARTITION>\|<VCD> — the POPS partition label, a literal \|, then the .VCD filename (e.g. __.POPS\|SLUS_007.42.RAMPAGE.VCD). A bare title fails with an "Invalid HDD game entry" toast. |
| USB | <FILE> relative to mass:/POPS |
| MX4SIO | <FILE> relative to mx4sio:/POPS |
| MMCE | <FILE> relative to mmce0:/POPS |
| HDD (exFAT) | <FILE> relative to mass:/POPS (use -page=ata) |
| SMB | not wired — -page=smb opens the SMB page for browsing, but there is no SMB branch in PLDR.AutoLaunchFromLaunchArgs; -game= on the SMB page toasts "Auto-launch page not supported: SMB" (system.lua:9368-9372) and falls back to the main menu |
If auto-launch fails (game not found, etc.) POPSLoader does not hang —
it falls back to the normal welcome screen + main menu and shows an error
toast describing what happened. SMB: SMB (v1) network browsing is
implemented (CI + Rolling green; browse + launch hardware-confirmed on a
static IP config, DHCP path fixed in bb62f2be and unconfirmed). -page=smb opens
the SMB page (scene GSMBNET), which brings up the network stack lazily and
opens the configured share before scanning its POPS folder for VCD games.
-page=smb -game=... is not implemented: auto-launch has no SMB branch
(system.lua:9368-9372), so it toasts "Auto-launch page not supported: SMB"
and drops to the main menu. Use -page=smb and pick the game manually.
-debug
Queues an on-screen info toast on the first main-menu frame listing:
[debug] boot context
kind: <HDD|USB|MC|MMCE|MX4SIO|SMB|HOST>
boot_path: <argv[0]>
sidecar: <settings sidecar path>
settings: <resolved settings path>
video: req=<n> got=<n> free=<n> <W>x<H>
args.page: <normalized page or <nil>>
args.game: <selector or <nil>>
Use this to confirm the args actually reached POPSLoader. If the toast
shows args.page: HDD you know parsing works and any remaining problem is
downstream; if the toast never appears, the args didn't reach
parseLaunchArgs() at all (front-end delivery or an outdated build).
The video: line is the GS mode readback (requested vs. obtained mode,
free VRAM, and resolution; NTSC=2/PAL=3). It is omitted only if
UI.VIDEO_READBACK was not captured (e.g. the boot video-mode apply was
short-circuited).
Front-end notes (important)
- One flag per argument entry. Front-ends that pass each argument as a
distinctargvtoken (OSDMenu'sarg_OSDSYS_ITEM_<n>lines, NHDDL,
OPL) work directly. Writing two flags on one line — e.g.
-page=hdd -debugas a single value — historically arrived as one token
"-page=hdd -debug". POPSLoader now defends against this (it keeps only
the first word of a page value and still detects a trailing-debug),
but the clean, portable form is two separate argument entries. - Whitespace / quotes are tolerated. Leading/trailing spaces and one
pair of surrounding quotes are stripped from each token (some CNF parsers
leave a trailing space on the line). Internal spaces in-game=values
are preserved (USB/MX4SIO/MMCE.VCDfilenames may contain spaces; the
HDD selector is the<PARTITION>|<VCD>form, not a bare title). - OSDMenu Browser-icon (PATINFO) launches don't read
arg_OSDSYS_ITEM_*; for those, arguments come from the OSDMenu
SYSTEM.CNFargextensions in the partition attribute area. - Build requirement. These arguments shipped in BETA-12 (feature
added 2026-05-25 on theBETA-12-PLAYdev branch). Builds older than
BETA-12 do not have them. All Lua runs from assets
embedded in the ELF at build time, so updating on-card.luafiles has
no effect — you must run a currentPOPSLOADER.ELF. Verify with
-debug.
Example OSDMenu OSDMENU.CNF entry
name_OSDSYS_ITEM_1 = POPSLoader (HDD)
path1_OSDSYS_ITEM_1 = mc0:/PS1_POPSLOADER/POPSLOADER.ELF
arg_OSDSYS_ITEM_1 = -page=hdd
Add games or debug as additional, separate arg_OSDSYS_ITEM_1 lines:
arg_OSDSYS_ITEM_1 = -page=hdd
arg_OSDSYS_ITEM_1 = -game=__.POPS|SLUS_007.42.RAMPAGE.VCD
arg_OSDSYS_ITEM_1 = -debug
Source map (for maintainers)
| Concern | Location |
|---|---|
| Token parse + trim | src/main.cpp parseLaunchArgs(), trimLaunchArgToken() |
| Lua binding | src/luasystem.cpp lua_getLaunchArgs() → System.getLaunchArgs() |
| Normalize + populate | bin/POPSLDR/system.lua NormalizeLaunchPage() + PLDR.LAUNCH_ARGS |
| Carousel navigation | bin/POPSLDR/system.lua page-jump block (raises Carousel.allowOptWrite) |
| Auto-launch | bin/POPSLDR/system.lua PLDR.AutoLaunchFromLaunchArgs() |
| Debug toast | bin/POPSLDR/system.lua PLDR.SurfaceLaunchArgsDebug() |
Why navigation needs allowOptWrite
UI.MainMenu installs a __newindex write-guard that silently drops any
OPT assignment unless Carousel.allowOptWrite is raised, and
MainMenu.Play() re-syncs the carousel from OPT every non-animating
frame. The page-jump block therefore raises that gate around its OPT
write (and sets the carousel indices to match). Without it the navigation
is a silent no-op — that was the 2026-06 "launch args do nothing on
hardware" bug.