The following warnings occurred:
Warning [2] Undefined property: MyLanguage::$archive_pages - Line: 2 - File: printthread.php(287) : eval()'d code PHP 8.1.34 (Linux)
File Line Function
/inc/class_error.php 153 errorHandler->error
/printthread.php(287) : eval()'d code 2 errorHandler->error_callback
/printthread.php 287 eval
/printthread.php 117 printthread_multipage



Freeplaytech Forum
Xenon 2 Megablast Port - Printable Version

+- Freeplaytech Forum (https://forum.freeplaytech.com)
+-- Forum: Neo Geo Pocket (https://forum.freeplaytech.com/forumdisplay.php?fid=1)
+--- Forum: Software Development (https://forum.freeplaytech.com/forumdisplay.php?fid=4)
+--- Thread: Xenon 2 Megablast Port (/showthread.php?tid=5513)

Pages: 1 2 3


RE: Xenon 2 Megablast Port - sodthor - 08-08-2026

great, even my mistakes help Smile


RE: Xenon 2 Megablast Port - sodthor - 08-08-2026

some roms by mic_ to measure cpu fetch and data bus: http://jiggawatt.org/badc0de/console.htm#ngp


RE: Xenon 2 Megablast Port - Ahchay - 08-08-2026

I'll admit to struggling with dealing with Claude, I don't know how you have the patience, but hats off to you


RE: Xenon 2 Megablast Port - Napomex - 08-08-2026

Well, where there's a will, there's a way. And I have to be honest, I kept starting over on so many platforms with small implementations, whether it was Gamebuino, MakeCode Arcade, or Arduino, I always lost motivation pretty quickly since my programming skills are really poor. Then last year I gave it a shot with GPT, but I never got much further than a flying ship, since GPT was pretty rough back then.

Then I built a Dreamcast collection manager for SegaCity.de to manage PAL games, and that went so well that I decided to take another shot at porting Xenon to my favorite handheld, or at least attempting it. I never would have thought I'd get this far, but you learn how to work with an AI that's sometimes pretty dumb, and thanks to your help, you also manage to solve the last remaining problems.

Though without Tixul's MCP I probably would've lost it two weeks ago already and threatened to delete Claude and replace it with GPT. Which wouldn't have made things any better anyway, if you threaten one AI with another, it just hallucinates even more trying to deliver an answer no matter what.


RE: Xenon 2 Megablast Port - sodthor - 08-10-2026

@Ahchay & @Napomex

my sprite multiplexing test: https://drive.google.com/file/d/10-HhxP6YcsKTZY_L6ahyhtzQfIWglAjk/view?usp=sharing
(based on one on my sample, it's just a test...)
It works on vdmgr & ngpcraft emu & real hw

128 sprites: 64 on each half of the screen, the switch is controlled by hbl

sprites def are stored in ram and then sent to sprites bus during vbl for top sprites and hbl for lower sprites [using ldirw (sprites def) and ldir (sprites color)]


RE: Xenon 2 Megablast Port - Ahchay - 08-10-2026

Resetting during the vbl is a good call.

I've been struggling to think of uses for this, but vertical scrollers where the ship is always gong to be in the bottom half of the screen is a good example. That's at least four sprites freed up - in the case of xenon2, probably many more


RE: Xenon 2 Megablast Port - sodthor - 08-12-2026

@Napomex, some other idea for small optimizations (to test): https://drive.google.com/file/d/1DcLjQvwSYWKPd9o1cl3qjo6H3syM_KnM/view?usp=drive_link
mostly function parameter formats to 16bit, inlining either by macro (RECTS_OVERLAP_CACHED) or use of "__inlne" directive for iabs16

+ update august 13: some __asm("ldir") added (terr_sec_step & oam_pool_alloc_run)


RE: Xenon 2 Megablast Port - Napomex - 09-20-2026

Claude's reply: I read through it, but I only understand 50% of it.
Thanks anyway for the great support.


The hardware confirmation is the real news here — thanks for that. I ran your
first demo through the emulator and read the OAM alongside it; the fact that it
now holds up on real hardware and on the vdmgr as well turns it from a stunt
into a technique.

On the multiplexing itself for us it fails on RAM, and only just.

I worked the numbers so it doesn't stay a guess. Our free RAM gap (from the
.map, not estimated) is 401 bytes; the stack needs 162 of those on the deepest
path we've measured, leaving about 190. A full OAM mirror in RAM costs 256 bytes
of sprite definitions plus 64 bytes of colour = 320 bytes. Your demo holds two
of them, so 640.

So we can't fit even one set, never mind two. That's not an objection to your
method, that's our project: we've been at the RAM ceiling for weeks, and the
last 198 bytes we gained came purely from letting the shop state and the level
state share a union.

The only memory we'd have left is the sound RAM shared with the Z80 — and that
runs straight into something we've already measured. In mic_'s NGP_BUS, for the
case where the TLCS is working on the shared RAM, the Z80 shows a throughput
figure of 002 against 0FC — it's effectively starved. Our music driver lives
there. Two 320-byte block copies per frame through exactly that RAM would tear
the music apart; we already mute it across level setup because the bus is
hammering there.

For us that makes this a technique for static screens (our shop: dealer on top,
item grid below, nothing crossing the split) and not for the scrolling level.

Two questions I'd genuinely like answered:

1. The one from last time is still open: have you ever measured the block copy
against the ordinary path? Not "128 sprites vs 64", but "a full ldirw run vs
individual sprite writes" at a realistic 40-60 moving objects. Today we only
write the sprites that actually changed — in a quiet frame, very few. Your
way pays the bus for all 128, including the 100 that are standing still.
The two curves cross somewhere, and I don't know where.

2. Does the second set have to be a complete 64-entry mirror? Or could the HBL
handler write back only the entries that differ between the upper and lower
half? That would cut both RAM and bus traffic down to what actually changes —
and that would fit our situation.

On the optimisations — we've already measured three of them.

Short version. HW_BENCH pair, same scene, same session, real hardware:

ROM VBlanks per 30 frames
without the changes 117
with u16 params + __inline + macro 118

One VBlank slower, not faster. We kept it anyway: behaviour is identical over
4000 frames, and the ROM got 146 bytes smaller. Today iabs16 is __inline here
and rects_overlap_cached takes u16 — only the macro is switched off (ROC_FCT 0).

The reason for that is also why I think your new suggestion is the best of the
four. NGP_IQ shows a prefetch queue of length 4 on hardware; our emulator core
models none at all. Our working thesis since then: on this silicon, instruction
fetch dominates so heavily that compact code wins — which is why the inlining
lost despite saving work. It makes the code longer.

ldir/ldirw is the only one of your suggestions that goes with that grain instead
of against it: one instruction is fetched once, and after that it's pure bus,
with no fetch cost per iteration. It ought to win, for exactly the reason the
other one lost.

On your two spots, after looking at them:

- terr_sec_step — there's a loop there that pushes 8 words from ROM into tile
memory, for every changed tile, roughly 6 per frame. That's the textbook case
for ldirw, and I'm happy to measure it. Honest upper bound: the whole
"reloading" block costs us 22 out of 796 scanlines per frame (2.8%), there's
no more in there than that. It is at least one of the few blocks where
emulator and hardware agree to a factor of 1.00.

- oam_pool_alloc_run — here I'm sceptical, and that's after reading the code,
not on principle. The expensive part is two nested searches over the free
list; the only thing that can be moved as a block is closing the list up by
one slot, at most 54 bytes, and the function is only called when spawning
metasprites. Our laser beam deliberately doesn't use it — the requirement for
contiguous slots used to cost us the beam entirely in 12% of frames. I'll
measure it along with the rest, but I expect zero.

One thing I don't know and you might: is a long ldirw interruptible? We build
our HUD bar with a raster split driven by two MicroDMA channels, and its table
is built in the VBlank handler. If a block copy during VBlank can shift the
split, that's a visible bug for us, not a measurement question.