Tuesday 28 February 2023

The sound of silence

Quick update; added sound hooks and osd stub functions for them - but nothing else!

Have been doing a little more RE of the SOUND CPU and it has helped a little, although I won't be needing (or wanting) to complete it. It's a bit more complicated than I first imagined, with lookup tables for which sounds to silence when certain other sounds are playing, for example. I don't think we'll go that far into replicating the behaviour - it has probably more to do with hardware limitations than anything else anyway.

Still yet to look for that last gameplay bug...

...but I was playing it tonight and when the Bacura appeared I was waiting for the bug to manifest itself. It wasn't happening when it should have... and then I realised I had actually fired up the arcade original to check out some of the sounds! I guess it's a good thing when you can't tell the difference between your port and the arcade game (ignoring the obvious aural hints!)

Monday 27 February 2023

Another gameplay bug squashed - potentially only one to go!

Getting really close now - fixed another game play bug! Only one confirmed, and one unconfirmed to go now... (that I know of!)

I fixed the bug where you would suddenly start to earn an extra Solvalou for every hit. I had suspected it was something to do with the dipswitch settings and bonus life calculation; what I didn't suspect was that it actually comprised about 4 different bugs! 😮

The dipswitches on the Neo Geo were a bit of a mess TBH. And for some reason, I was negating the bits when reading them which only confused matters - including debugging - further. The trick to avoiding CPU-consuming bit operations is to order the Neo Geo settings to match the bit patterns of the arcade dips, rather than preserving a nice 'natural order' for each option. It's better that the options listed for lives is "5, 2, 1, 3" (you'll likely only ever look at them once) than requiring an extra 12 instructions every VBLANK just to read the dipswitches - or a few lookup tables!

Suffice it to say, as well as incongruencies in the Neo Geo dipswitch settings, there were a few bugs in the bonus calculation routine as well (comprising 16-bit BCD addition), which meant around 64,000 you started getting Solvalou every hit. And because it worked fine at 20K and 60K, it went unnoticed for a long time, until I started patching the starting score to test the high score table and load/save.

So now I'm left the randomly exploding Solvalou, seemingly (only) when Bacura are present. At least I believe I can reliably recreate the issue every time, so I'm hoping that won't be too difficult to fix either. I'm a bit surprised that it might be related to the olde Bacura hit-box issue again (which was a bit of a bugger to find), but no solid theories just yet.

The only other issue I'm aware of is something I noticed early on when working on the scrolling - when ignoring attribute settings for tiles (can't remember fg or bg) there was a curious black square scrolling down the left-hand side of the screen. It could have been related to the Xevious sprites - which have since undergone a complete overhaul - so I'm half-expecting the issue to longer exist. But I will see if I can recreate that one as well, just for good measure.

That should cover everything, except for the purely cosmetic issue of 'sparklies' on the screen, mosty down the left-hand side. Absolute worst case, they don't affect the game at all and are easily ignored, but again it wold be nice to know what it is, and whether it can be eliminated.

I suspect it is Neo Geo sprite code running outside the context of VBLANK. It may even be the foreground layer tiles being updated - they are always outside VBLANK. If that's the case, I can look at using dirty squares and deferring update until VBLANK, or just leave them as-is. Not a show-stopper by any means, and akin to 'sprite flicker' you see on a lot of platforms being pushed to their limit.

Now, those pesky Bacura...

Saturday 25 February 2023

Running on real NGCD hardware!

One step forward, two steps backwards perhaps?

Firstly, I burned a CDR and tested Xevious on my NGCD. Works as expected! I can also confirm that high score load/save (to/from virtual memory card) works fine on the NGCD hardware as well - yay!

Xevious running on real NGCD hardware!

During play testing however, I noticed another bug today. I also confirmed one of the two existing gameplay bugs - extra Solvalou suddenly starting to appear every hit - is definitely still an issue.

The new bug, which I haven't really noticed before, is the Solvalou suddenly exploding for no reason! In hindsight I may have seen a hint only a few days ago, after fixing the "hanging bullets" issue, but at the time I thought it was due to a teleporting Zakato. Not so it seems...

So I have 3 outstanding issues, one yet to be re-confirmed.

Another observation; I do get "sparklies" on the screen. I noticed this on my AES, but couldn't discount the fact that it was crappy output/cabling. Now that I get the same issue on the NGCD, I'm forced to concede that it may well be my programming (although I'm using the same cables). Not a show-stopper by any means, but would be nice to find the cause and eliminate it...

And as a reminder to myself; I also need to generate the proper FIX layer for the NGCD.

UPDATE: Fix layer for NGCD done (just needed to copy the cart FIX ROM and rename).

A bit more play testing. High score load/save working on NGCD. The random explosion of the Solvalou seems to be related to the Bacura; at the very least it happens more often with Bacura on the screen. Hopefully a good clue. Sparklies my issue; my splash screen is rock-solid.

Friday 24 February 2023

Xevious for the NGCD

No progress on the corrupt title screen after high score entry. I suspected it was related to the scrolling routines, as there are 3 distinct cases handled differently; in this case it would be an arbitrary scroll value which requires the entire background layer to be re-constructed. I did find what I think was a bug in that routine, but I also believe the case affected never actually happens!

However I've gone cold on that theory now; it only happens after high score entry, and there's no difference to the sequence of scroll register values written. I'm at a loss TBH...

Onto some good news. My inbox has been flooded with emails begging me to include a CD build of Xevious when I release the beta (by 'flooded' I mean I got one message and by 'begging' I mean asking me if Xevious could possibly be made to run on the NGCD).

And so I attempted to build - for the first time - the CD version of Xevious. Not having built a CD in probably 10 years, the build failed miserably. But after some refreshing of memory and re-visiting my old Donkey Kong project I updated the Xevious build tools to produce the .SPR file and managed to create a bootable CD.

Easy enough to run in NeoCD/SDL; running it in MAME is another story altogether. That was a learning curve but I finally worked it out, and added the necessary ISO to CHD conversion to my makefile.

The game loaded and ran fine. The graphics, however, weren't. It looked like the palette wasn't initialised. I could see the Xevious sprites, but just about everything else was invisible and/or black. I could see the forground tiles for example, as the Xevious sprites flew under them.

It's running in sttract mode!

So I went back and checked all the locations in the code that differed for a CD build - only one (OK). I checked the header file for the CD (OK), I checked the Donkey Kong code for differences in a CD build (none), I checked that I wasn't running out of RAM (OK). I perused the NeoGeoDevWiki for any clues... but I was starting to run out of ideas...

In fact I was typing up a post for one of the dev boards when I thought to re-check the format of the sprite data in the .SPR file. Referencing the NeoGeoDevWiki again, I noticed that the bitplane ordering was reversed wrt what I had just copied from the old Donkey Kong project...

A quick code hack and re-build and it confirmed the issue; Xevious looks just great on the NGCD!

I will burn a CD and try it on real hardware this weekend though.

That begs the question about how Donkey Kong was working. One possibility is that DK is using less bitplanes. Another is that I hacked the palette becase I couldn't work out what was wrong? It was 10 years ago... In any case, it's all fixed now and I even went back and fixed the original DK tool in the process.

Now back to that corruption bug...

UPDATE: A good day - not only did I get the NGCD build going, but I also fixed the title screen corruption bug!

Single-stepping through the code that updates the background layer after the high sore entry screen, it got interrupted by code that jumped into the BIOS. It then hit me that the hardware scroll update code wasn't actually being called within the VBLANK interrupt context - and thus potentially having the VRAM address register being changed mid-stream.

Right now I can't explain why the routine is being interrupted - it should be executing well before the next VBLANK interrupt - but that's for another day. What matters is that disabling interrupts around the scroll routine fixes the problem!

That now leaves just two (2) "bugs"; both of which I need to confirm are actual bugs. Just recreating them may be a challenge in itself. If I'm not successful in doing so within a couple of sessions, I might just release a beta and see if anyone else encounters them. Getting close now...

Wednesday 22 February 2023

Transcode audit finished & bullet bug fixed!

Significant day today - finished the audit of the SUB ROM, and also fixed a long-standing bug!

I'm reasonably happy with the state of the transcode, though I may still need to tweak some of the execution flow to better accomodate the single-CPU architecture with respect to the VBLANK. I don't think it's an issue on the Neo Geo, but the graphics-intensive Amiga port may be another matter.

I've fixed the bug where some bullets just "hang" in the air. As happens way too often, you "fix" it and it still doesn't work - because it actually turns out to be a combination of multiple (in this case 2) bugs. In both cases, it was about register usage; one case of a register not being preserved around a subroutine, the other a case of a subroutine called from two places but parameters in different registers.

And only now that the bug is fixed, have I noticed that the Brag Zakato firing patterns look right!

Brag Zakato spraying 5 bullets

I also confirmed that one 'bug' was an artifact of skipping areas when debugging - it happens on the original version as well. In fact I don't really understand why it's happening, and suspect it's a bug. Regardless, it doesn't happen during normal gameplay.

That leaves me with three (3) outstanding bugs now; corrupt title screen after high score entry, extra Solvalou every hit, and a glitch that I noticed when debugging the graphics routine. The first is almost certainly a bug in the Neo Geo (OSD) layer, the 2nd may be related to dipswitch settings, and the 3rd I'm not even sure is a bug!

There is also the question of transparency in the Xevious sprites; jotd has pointed out that a whole bunch of sprite CLUTs have multiple transparent colour entries. I do know the crosshairs use such CLUTs, but was not aware of the others. I certainly haven't noticed anything "wrong" whilst testing... but I haven't been looking either. To be investigated...

Hopefully I can knock these off in the next few days and I can move onto sound!

I may also release a beta before adding sound for people to test the gameplay...

Tuesday 21 February 2023

Audit of the MAIN CPU ROM complete!

A couple of big sessions today and I've finally finished the audit of the Xevious MAIN CPU ROM!!!

No more bugfixes - in fact I flagged a possible 5th outstanding bug - but a substantial task out of the way.

Next up is the audit of the SUB CPU ROM, but it is a lot smaller than the MAIN; only 1/6th the number of lines and almost half of that is data. The code tends to be a little simpler as well, so I'm not expecting it to take more than a couple of sessions to complete.

That'll then leave bug fixes, and adding sound!

jotd is progessing well with the Amiga port, it's all-but-playable as-is. He has released a video in the last few days on YouTube. I'm trying to knock over the audit ASAP so I can assist with some of the details.

Monday 20 February 2023

A bug fixed, high score load/save finalised, and not much else.

As usual not a lot of progress over the weekend due to Real Life.

jotd did find another bug that I'd never actually noticed before; on the attract mode gameplay screen the ground objects were misaligned with respect to the map - how I missed that I have no idea! 😮

Upon closer inspection I noticed that it was the map that was offset (not the objects) and only after the title screen - not after the high score table screen. This made it easy to compare the values of the inputs to the map-rendering routine in each case, since the attract mode always plays Area 1 from the start. And it was only a handful of minutes before the difference was revealed and less than a minute later that the culprit was found.

Nothing even remotely interesting - just an unintialised register used as the index into the table that specifies the offset in the map data for each area. The Z80 code was clearing A to initialise a few other variables, and then using that as the index. In the transcode I was using the CLR instruction instead, then using an uninitialised D0 as the index.

Since this code is not time-critical at all, I reverted to clearing and then using D0 as per the Z80 code, keeper it closer to the original source.

The only other update was finalising the high score load/save on the Neo Geo. The only system to use BRAM (as BRAM) is the MVS; the other two systems (AES/NGCD) support memory card I/O, even though the AES implements a "virtual" memory card in BRAM. I decided to first read from BRAM on MVS systems, and then attempt to read from memory card (on all systems), overwriting any BRAM data if found. On saving high scores, MVS systems write to BRAM, and then all systems attempt to write to memory card.

Probably not how it's supposed to be done, but that's my preference.

Since the "known" list of bugs had grown by 2 (from 8 to 10), and I have fixed one of those, the outstanding list now stands at 4 (with 1 still yet to be confirmed as an actual bug). But for the week heading forward, I'll be spending my lunchtimes at work trying to finish the audit of the MAIN ROM.

Friday 17 February 2023

Making a splash (screen)!

A diversion today - adding a splash screen to the Neo Geo target.

One of the reasons I want to add a splash screen - aside from advertising - was to display some sort of software version. After the first beta gets out into the wild, I want people to know exactly which version they're running. The plan is for the ultimate version to be released as v1.0.

The first task was rendering the Xevious foreground character set as Neo Geo FIX layer tiles. That was relatively straightforward, but I realised the character set as-is wasn't very convenient for rendering generic text. So I rendered a 2nd set with the alphanumeric and punctuation characters with their correct ASCII ordinals so I could simply use ASCII text in the source code.

Being 1 bit-per-pixel characters, they are nominally rendered with a transparent background. That may come in handy, but I also wanted the option for setting the background colour on a per-character basis. So I rendered a 3rd set - again in ASCII order - with non-transparent background pixels.

That's the beauty of the Neo Geo - oodles of characters/tiles/sprites to go around!

I've done up a quick splash screen. I'm not entirely happy with the aesthetics of it, and I actually can't reconcile the colours with the ones I thought I chose from the Xevious foreground palette, but it's a start.

Crude but conveys what I need to convey...

Back to the audit next session.

Thursday 16 February 2023

Load/save to/from battery-backed RAM

Curiosity got the better of me and I started looking at saving to battery-backed RAM (aka BRAM, aka NVRAM). FTR MAME saves a copy for every ROM to the nvram directory.

Turned out to be trivial to implement.

First thing I discovered is that the BIOS loads the game data as specified in the header even before the cartridge executes any code. That means by simply setting the right header data the BIOS will automatically load save data from BRAM into my buffer (long before I hook to load from memory card). Half of the solution right there!

By using the same buffer as the memory card, and the same format - including the header - I can determine whether or not valid save data has been loaded, and skip patching the high scores if not. As for saving, I just copy data to my memcard buffer, and call $C12322 instead of saving to memory card. And that's all it takes!

I still need to work out a priority for reading/writing to BRAM/memory card, but that's also trivial.

UPDATE: seems not quite as trivial as I thought... calling the BRAM save routine is corrupting the title screen that follows the GAME OVER screen. I just can't see why... it's not calling any other function, and the registers are all saved in my routine...

UPDATE: the bug has nothing to do with the BRAM save routine; comment out the entire high score load/save routines and it still happens... another known bug (4)

Attract mode fixed, only to uncover more bugs in the process!

Well that opened a can of worms!

This morning I decided to fix the bug where the Solvalou moves very quickly in attract mode.

What I discovered wasn't great; I was treating the _dX,_dY values as bytes in some cases, and (correctly) as a word in other places! So first order of business was fixing all of those instances, and converting a dX,dY table for Solvalou movement from byte values to words.

That fixed the movement in attract mode, but I noticed it wasn't firing at all! Tracking that down uncovered another two (2) bugs; in some places I was reading the attract mode stage as a byte instead of a word, and in the case of generating a random shot to be fired, the random number was being treated as a byte instead of a word. So again, multiple instances that had to be fixed.

All byte/word issues. Makes me wonder how many more of these issues are waiting to be discovered?

Anyway, 5 from 8 bugs, 3 remaining. One of them may/may not be a bug - it was an observation when I was debugging the Neo Geo sprite implementation. I'll have to go back and recreate the conditions to see if it persists. The remaining 2 will be a bit more problematic I think; occasional bullets that just hang in the air, and extra Solvalou being awarded every hit.

But for now, on with the audit...

UPDATE: I've finished the 3rd device in the MAIN ROM, just one more to go...

Wednesday 15 February 2023

Half-way through the MAIN program audit

Slow progress due to Real Life commitments, but progress none-the-less.

I'm now half-way through the MAIN program, working on all the flying object handlers now.

It has become apparent that the few small blocks of random data throughout the dump are simply padding to allow code to be modified/patched within a single device. Obvious now that I've noticed said blocks are right at the end of each device.

One of the remaining known bugs that I've noted in my note book is the observation that Jara morph into Toroids when they change direction. The problem is, I've just finished auditing the Jara handler code and I can't see any way this could happen. I originally suspected a simple transcode bug with the incorrect sprite code being set in the handler routine. It's not that though - in fact the Jara sprites come from a look-up table based off a timer value to animate them - and it's correct.

It's very unlikely that it's data corruption; once a handler is installed the object type in the object table is ignored. It would have to be the handler address being changed every time the Jara change direction, which isn't very likely.

Unfortunately the Jara aren't very common, so some creative patching might be required.

Something for lunchtime today...

UPDATE: Only 10 minutes to fix the Jara bug! Since Jara only appear later in the game, I simply patched the object table for the Toroids to jump to the Jara handler, so they appear immediately. Fortunately the morph bug was readily apparent; on Jara that started out moving to the left, then changed direction to move to the right (though curiously, not on Jara that started out moving right).

A simple transcode bug that went unnoticed during my audit - I was storing the sprite colour of any right-moving Jara in the _CODE offset rather then the _COLOUR offset in the object table.

So that's 3 of 8 down, 5 (known) bugs to go...

UPDATE: Another big fixed! Actually, it was 2 bugs which had the same ultimate effect - the Zakato wouldn't appear before it exploded. There are actually 4 different Zakato variants with slightly different behaviour. On one variant, the delta X value was inadvertantly written into the high byte, so it disappeared off the screen in 1 frame. On another variant, the instruction to set the sprite code was omitted completely.

So now that's 4 down, 4 (known) bugs to go...

Friday 10 February 2023

High Score load/save!

Had an idea in the car on the way to work this morning - high score load/save.

At lunchtime I did more of the transcode audit; up to all the object routines now. They should be pretty quick to audit, except for the few which have known bugs.

Tonight at home I read up on the Neo Geo memory card routines. Fortunately they're pretty straightforward to understand and easy to use - it was harder working out how to use them with MAME.

Anyway the load/save has been implemented and tested! On startup the high scores will be read from the memory card (if the card is inserted and there is Xevious data on there of course). And whenever a new high score has been entered on the table, the table is written back to memory card. Easy as!

That's fine if you have a memory card, but some MVS systems supported battery-backed RAM instead. I can see MAME has automatically created it. I'll look at falling back to NVRAM if there is no memory card present.

Wednesday 8 February 2023

The Bacura Hit-Box Mystery!

Since I've been inundated* with questions about the Bacura hit-box fix, I thought I'd write up a blog entry on it...

Xevious maintains a table of object variables including X & Y "world" coordinates, stored as 16-bit values. These values are scaled up from their sprite coordinates for various reasons I won't go into here.

Once a frame, the SUB CPU program iterates through the object table and updates a shadow copy of the hardware sprite registers in RAM. These shadow registers can then be efficiently block-copied to the sprite hardware registers during VBLANK. It goes without saying that the format of the these registers are the same as the hardware registers; in Xevious, the X register is 9 bits, and the Y register 8 bits.

During the course of the game, Xevious must calculate collisions between objects - bullets, bombs, enemies and the Solvalou - to determine which objects need to be destroyed. For efficiency reasons, (ie. to eliminate scaling and allow the use of 8 bit calculations), Xevious uses the sprite shadow registers in the hit-box calculations. In fact it shifts down the X coordinate to 8 bits in each calculation routine.

The problem with the Neo Geo transcode is that the sprite hardware X & Y registers are themselves shifted (up), not conducive to efficient calculations. Additionally, the X coordinate is adjusted for 2x2-tile sprites due to the way the sprites are implemented on the Neo Geo, which breaks the calculations (I found that out the hard way!)

As a result, the 68K transcode actually maintains 2 copies of the "shadow" registers (at least for X & Y); the 2nd being the same (actually, similar) format to the original registers. To make the hit-box calculations a little more efficient, I pre-shift the X coordinate down into 8 bits so it need not be done in every hit-box routine. Thus the hit-box calculation algorithms - magic numbers and all - are preserved in the transcode.

After checking the routine for the Bacura more than a few times, I was stumped as to where things were going wrong!?!

My intention was to compare values side-by-side between the original Xevious and the transcode. Patching the code to start on Area 3 and starting a game before the attract mode ran meant that each Bacura would appear in exactly the same spot at exactly the same time on each version.

First up - the original Xevious. With 4 Bacura on the screen I halted execution and looked at the Y coordinates for objects #16-19 in the object table. Since the screen is rotated, and Bacura move straight down the screen, the Y coordinate for each never changes, which makes things easy! I took note of those and then set a breakpoint in the routine that checked if the Solvalou has been hit by a Bacura.

Somewhat surprisingly, the value in the sprite shadow Y registers did not correspond to the Y coordinates in the object table (even taking the scaling into consideration)! And this was actually my first clue. But it was late, so I went to bed with a theory, but still guessing.

Next morning I had time to have a quick look at it again. Initially I was confused, as the update sprite shadow register routine appeared to just store the scaled value directly to shadow RAM. But then I noticed it was calling a routine to handle the screen flipping for a cocktail cabinet. The transcode does not support flipping - for obvious reasons - but I took another look at the Z80 routine. I did annotate that some time ago, but forgot that it adjusts both X & Y values even in the case that the screen ISN'T flipped for cocktail mode! And although the X value is simply incremented by 4 - which won't affect hit-box calculations - the Y value is actually SUBTRACTED from $EF, which certainly WILL affect the calculations!!! Eureka!

So I updated the transcode to apply both X & Y adjustments (for the sake of completeness) to the 2nd copy of the X & Y registers and now the hit-box for the Bacura is correct - yay!

Note that for 1x1-tile sprites, the calculations were still technically incorrect, but the difference was too subtle to notice during gameplay. I DID think that they were perhaps a pixel off as it's possible to bomb two adjacent objects at once if you aim just right, but then thought my memory was playing tricks. But with the 2x1-tile Bacura sprites, the difference was an entire sprite tile width - 16 pixels - impossible to miss!

Today with the 2nd of 8 known bugs fixed I forged ahead with the audit - mainly more micro-optimisations. FTR these micro-optimisations are things like using MOVEQ/ADDQ/SUBQ where possible, LEA instead of MOVEA.L, and small (byte) branches etc etc. I'm now exactly halfway through the 2nd ROM device (of four).

* this may not be the same meaning of the word you're used to!

Monday 6 February 2023

Audit #7 - only 7 bugs remain (plus the others)

I've managed to complete a number of audit sessions since the last post, mainly micro-optimisations and annotation updates, but some bug fixes as well.

I have a list of eight (8) known transcode bugs in my notebook, and tonight I finally fixed one of them. The high score entry was working except in the case where the new score was the highest in the table; it wouldn't copy the new score or clear the name from the 1st entry (and in the process I added a build option to specify the starting score). Now there are 7 known bugs...

In my audit I'm currently looking at the routine that determines whether the Solvalou has been hit by a Bacura, which is another known bug - the hit box seemed to extend off the left-hand side of the Bacura. Upon closer inspection, after some experimentation, it is apparent that the hit box is shifted to the left. You can actually fly through the right-hand side of a Bacura.

I've checked the transcode of the routine several times now, and it looks OK. What's unique about the Bacura though is that it's the only flying object that is double height (width). I'd suspect a placement issue with the sprite (as - unlike ground objects - there's no reference to where it should be) but I don't seem to have placement or hit box issues on double height ground objects! Weird...

Because my intention is to perfect the transcode in a single audit pass, rather than press on with the audit I'm going to persist until I solve this mystery.

jotd is still working on the Amiga port, but bogged down with palette limitations on the machine. He's also unable to trigger a bomb, which is a bit odd; although I haven't had issues I can't rule out a bug on my part just yet?!?

UPDATE: Have to admit, the Bacura hit box issue is doing my head in. So I thought I'd post on my blog and hope for a Eureka moment?!? Hmm... waiting.... waiting....

Both the Solvalou and bullets have the same issue (different routines) so that would suggest a sprite placement issue. However there are 2 reasons this can't be the case: (a) the 2x2 sprites for ground-based objects are in the correct positions and (b) running the original Xevious and mine side-by-side shows the Bacura in exactly the same place at the same time. On mine, the Solvalou is destroyed by the 2nd Bacura on level 3, whilst on the original, it's destroyed by the 4th.

The Solvalou about to be destroyed by a Bacura that misses
- and rightly so - on the original arcade game.

At least the enemy placement is completely deterministic; starting a game from power-on before any demo can run will result in exactly the same sequence of enemies. This is because the pseudo-random number routine is never called outside a demo/game. This then means I can halt execution and inspect and compare values of the Bacura objects and sprite registers on both versions.

Fiddly but seemingly necessary now that I have no theory whatsoever...

UPDATE: Fixed the Bacura hit-box bug!!! 6 to go...