One of the early requests for games to add to the Penultimate Cartridge was a game called Torndao (from TFW8b himself). It's quite a nice Defender clone (or is it Scramble, I get confused)
The only problem is, it is broken.
Apparently it was like that from day one.
It has been broken since 1982.
It has always been broken.
What happens is it locks up. Usually after a few seconds.
But it is not consistent. Sometimes it plays fine, other times it locks up.
That's easy to fix then, wait until it locks up and then debug what is going on.
Well, no, you see rather frustratingly, the other problem is that is always works in Vice.
I have tried all sorts of things to make it break, initialising memory to 00, to FF, to random, different models, memory settings, ROM sets. etc. All work flawlessly every time.
Whenever we look at adding new things to the Penultimate Cartridge, we draw up a list of potential candidates to be removed. Tornado is always on there, but always gets saved because when it works it's not a bad little game.
That's where we are now. For me, it had been failing pretty much every time, but TFW8b had a bit of a run of it working. It is not consistent.
As part of some other testing, TFW8b asked the team to run through loading it multiple times, and keep a note of how many times it worked, so we could judge if we could justify keeping it.
One of the testers took things a bit further, and tried all sorts of combinations. I was getting the results second hand, but I was a little dubious of their findings.
It sounded to me a bit like "it only works when I wear my red jumper". Correlation does not imply causation.
The theory was, it always fails after running the game Final Orbit (and later several others were added to this list).
I was dubious because I could not see how the two things could affect each other. Between playing Final Orbit and loading Tornado, you would have gone through the menu. That means two reset cycles and various changes of memory mapping and memory been overwritten.
That can't have any affect, can it?
TFW8b did some testing and it seemed that there might be something in the theory - Tornado always crashed after you had loaded Final Orbit.
OK, I thought, let's try this out
I loaded up Final Orbit in Vice and played that for a bit.
I then changed the cartridge image for Tornado and tried that.
I selected 1 player and it started up.
As usual, it started moving.
Scrolling along for a bit.
And then it stopped.
It had locked up.
In Vice.
This was the first time I had seen it lock up in the emulator.
OK. Do not touch anything! Let's find out what is going on.
.C:18e0 AD 14 91 LDA $9114
.C:18e3 29 0F AND #$0F
.C:18e5 A8 TAY
.C:18e6 B9 4A 19 LDA $194A,Y
.C:18e9 C5 36 CMP $36
.C:18eb D0 F3 BNE $18E0
It seems to be stuck in this loop, going around and around.
It loads VIA #1, Timer 1 low byte from $9114. Masks off the upper nibble to leave a number from $00-$0F. It then uses that to index a table at $194A.
>C:194a 01 02 01 02 01 02 ff ff ff ff ff 00 00 00 00 ea
It compares this with the value at $36 and if it does not match, it keeps going around the loop.
And it never matches.
The value from the timer seems to go through the same cycle of even numbers $0E, $0C, $0A, $08, $06, $04, $02, $00 etc. (OK, no matter how much I know it is true, I refuse to accept A, C or E are even, they just look like they should be odd numbers, probably since I am used to thinking of them as the first, third and fifth letters of the alphabet.)
The value of $36 remains constant at $02. The only time this would trigger is if the value of the counter was $01, $03, or $05. That would return $02 from the lookup table and there would be a match.
So there it sits in its loop. What is going on?
There are four registers in the VIA that refer to timer 1.
- $9114 - Timer 1 low order counter
- $9115 - Timer 1 high order counter
- $9116 - Timer 1 low order latch
- $9117 - Timer 1 high order latch
The 16 bit value in $9114 and $9115 counts down until it reaches zero, then it loads the values from $9116 and $9117 and starts counting down again. It counts at the CPU clock rate, 1.023MHz on NTSC systems, 1.108Mhz for PAL. The maximum count if $FFFF, so a maximum cycle time of 64ms (NTSC) or 59ms (PAL).
The values in the version that had locked up were:
>C:9114 08 27 00 aa
The timer was currently at $2708, and would count down to $0000, then be reset to $AA00.
In a separate window, I opened another Vice session and loaded Tornado without having first loaded Final Orbit (or put on a red jumper).
This worked, as it normally does, so I checked the values of the timer.
>C:9114 ac 64 ff df
That one was currently at $64AC, and would reset to $DFFF
I went back to the locked up version and changed the value of $9116 to $FF and kept watching. After a cycle or two, the value of the timer came back as $05. The value from the lookup table was $02. That matched the value at $36 and away it went!
Why were the values different?
I looked through the code, and I couldn't find anywhere that it actually set those values. Ah, is that it?
But surely they get reset by the VIC20 startup code?
Well, no. I thought they did. Most of the other registers are set to appropriate values, but the timer is not initialised.
It seems that Tornado is relying on those values being something reasonable. But what is reasonable?
.C:18e0 AD 14 91 LDA $9114 (4 cycles)
.C:18e3 29 0F AND #$0F (2 cycles)
.C:18e5 A8 TAY (2 cycles)
.C:18e6 B9 4A 19 LDA $194A,Y (4 cycles)*
.C:18e9 C5 36 CMP $36 (3 cycles)
.C:18eb D0 F3 BNE $18E0 (3 cycles)*
Looking at the timing, it is taking 18 cycles going around that loop (it would be more cycles if either of the * functions crossed a page boundary, but neither do). That makes sense, if the first sample was at $00, next time it was checked, it would have counted down by 18 decimal, $12, so would be $EE. 18 more cycles and it would be at $DC, then $CA, $B8, $A6, $94, $82, $70, $5E etc. When the top nibble is masked off you get the sequence we saw previously $0E, $0C, $0A, $08, $06, $04, $02, $00 etc. on and on.
I think it is always going to get stuck in that same pattern, going up two at a time, always even, never odd.
Over on the copy of vice where it was working, it would get to $0000, following that pattern, and then would reload to $DFFF. The next time it checked, it would get $ED, then $DB, $C9, $B7, $A5 etc. Bingo. Odd numbers. The value would match the lookup table and the game would continue.
So it turns out, the "reasonable" value we need to initialise it to is "anything odd" (and probably greater than 15). When the number is odd, it will cycle between odd and even numbers and so it will never get stuck with all even numbers. If the reset value is even, all the cycles will be even, so it will get stuck in the loop.
At power on, those registers contains some unknown values. If you run Tornado it will lock up if that value is even, run fine if it is odd. 50:50 change of success in an ideal world, in practice it seems some machines may be more likely to settle on a certain value, maybe odd, maybe even. It may work, it may not.
If a game (i.e. Final Orbit) sets those values to something different it may always work, or always fail, depending on what those values are.
I couldn't find anything in the manufacturers datasheet saying what those values were initialised to. Other than the WDC version which states
Reset clears all internal registers (except T1 and T2 counters and latches, and the SR).
So the power on values are random, probably most likely to float high.
I had a look at the vice source code, and it resets those values to $FF at power on. Seems reasonable.
Rather than make any changes to Tornado itself, I thought it would be better to add some code to the Penultimate Cartridge menu. That will load those registers with $FF, in case any previously loaded games have changed them.
The counters cycles through tens of thousand times a second, so the value when a game loads is dependent on when the user navigates the menus. Effectively random, just in case any of them use that to seed their random number generators.
I have tested loading Tornado from the updated Penultimate Cartridge dozens of times now from cold starts and with various games loaded first (including Final Orbit) and it has always worked. Even with my red jumper on.
Thanks to Tim for persistence in testing, apologies if I initially doubted your theory.
I have sent the updated ROM to TFW8b, and that is being sent on to the testers. As far as I can see, I have finally fixed Tornado after 43 years, and hopefully haven't broken anything else in the process.
Rod has made a video playing through Tornado on the updated cartridge and everything is working nicely.
Advertisements
The new Penultimate +3 with built-in SD2IEC drive and Turboload (and a working Tornado) is shipping now from TFW8b.com.
Tindie
Minstrel 2 and 3 kits are available from my Tindie store, with worldwide shipping. Versions avaiable for ZX81 case or standalone with keyboard, and also Misntrel 3 with ZXpand microSD card interface (andn now available as PCB only).
I will slowly be moving things over there from my SellMyRetro store, so if there is anything that you want, let me know and I'll add it.
Patreon
You can support me via Patreon, and get access to advance previews of posts like this and behind the scenes updates. Patreon supporters had advance notice of this new feature, and are currently getting a run of posts as I catch up after spending the last few months testing Turboload. This also includes access to my Patreon only Discord server for even more regular updates.