Showing posts with label Game. Show all posts
Showing posts with label Game. Show all posts

Sunday, 9 March 2025

Finally Fixing VIC20 Tornado

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.

Sunday, 12 January 2025

Why does the 1541 disk drive keep spinning?

This is a bit of a strange issue that came up during some recent testing.

It only happens if you load the PRG version of Rodman from a 1541 disk drive onto a VIC20 and run it immediately before the disk spins down.

When you do that, the disk activity light goes out, but the drive keeps spinning. The game runs fine, it is just the disk drive that behaves oddly.

It only if the drive is still spinning, there is not time to type R U N and press RETURN, so it is only automatic starts like the Penultimate Cartridge file browser. I also check it does the same thing if you have the BASIC 4 ROM if you do SHIFT + RUN/STOP and have Rodman as the first program on the disk. It both cases, the game starts immediately after loading.

I decided to wire up the logic analyser and have a look what is going on. Here I have the IEC bus side of Data, Clock and ATN, and the IO chip select line for the VIA chips.

This is the end of loading Rodman.

Looks OK to me.

For comparison, this is the end of loading Bertie the Ball, which does not leave the disk spinning.

I zoomed in a little closer for the screenshot, but it is doing the same thing. The three ATN pulses at the end to ask the drive to stop talking, close the file and release the bus.

Hmm. I was thinking it might be some weird edge case if the file was a multiple of some block size or off by 1 etc.

It was only when I zoomed out that I noticed a difference.

At some point, there is some IO activity and ATN gets asserted again and the drive thinks it is going to be asked to do something so keeps the rust spinning.

This is Bertie the Ball, you can see there is some IO activity, but the IEC lines are unchanged.

I think this might be a similar issue to that of the autofire on Bandits I saw previously.

The problem was with the port A output register on the userport VIA. The pins on there control IEC ATN line output, and are inputs for datasette sense, IEC clock and data and 4 of the 5 joystick directions (don't ask).

The PA7 output drives a 7406 inverter, which will assert the signal (pull the bus low) when PA7 is logic 1.

In Bandit's case, it was writing to that port as part of the initialisation and changing the value of bit 7 to 1. This asserted the ATN signal and because of a previous change to the other signals caused the drive to react, which meant the signals read from port A were different if a drive was connected.

I asked Misfit (the creator of Rodman) if he was writing to $9111, and he was good enough to check and found no writes to that address in his code (or the alternate version at $911F).

Odd.

He sent me the code, which consisted of 7 POKEs, setting up various registers on the two VIAs, but not including $9111.

   POKE(0x911C,0xFE);

   POKE(0x911E,0x7F);

   POKE(0x912E,0x7F);

   POKE(0x9112,0x00);

   POKE(0x9113,0x00);

   POKE(0x9122,0x00);

   POKE(0x9123,0x00);

I zoomed in to the point where ATN was asserted, and I could see those 7 POKEs (it's quite neat when you can actually visualise code happening)


You will see the spacing of those is not quite even. This again is visualisation of code. Due to clever ordering the statements, the compiler is able to optimise out several of the LDA statements where the same value is written to several addresses, and has already been loaded.

Looking at the trace, you can see it is the fifth POKE which causes the problem.

   POKE(0x9113,0x00);

Ah, of course. That is writing to the data direction register for port A, and setting all the pins as inputs.

The ATN out pin, controlled by bit 7, should be an output, as it directly driving the input of a 7406 inverter.

When bit 7 is set as an input, the input of the 7406 buffer will be floating, most lightly read as a 1. In practice, this depends on make, type and age of the VIA, how strong the pullups on the input pins are, and also on the make, type and age of the 7406 if it has an input pullup or what it will consider a logic 1.

This was first spotted by a colleague using Vice with "true drive sounds" on and it continued the "disk spinning" sound after the game had started.

I was testing here with a VIC20-CR and a Mini VIC and a 1541 drive.

The internal pullup in the 6522 most likely means that the ATN signal will be asserted. If this happens before the drive has spun down, it will keep the disk spinning as it thinks it is about to receive a new command.

It probably does this on an SD2IEC as well, but you wouldn't notice as there is no sound.

I tried a few more games, and noticed the same thing on Bolder Dan.

Here there are 20 pokes this time, the 4th one seems to assert clock and / or data, and the 13th asserts ATN.

How to fix this?

Ideally you would set bits 7 as an output and write 0 to those bit 7 in $9111. Both of which should already be the case, so the easiest fix is simply to remove the fifth POKE.

I am not sure this actually needs to be fixed, since it only affects someone using a Penultimate Cartridge and a 1541 disk drive to load a game which was never released on disk, and is already in the Penultimate Cartridge. Normal loads on any device are fine as the drive normally spins down before you have had time to type in R U N and press enter. 


Adverts

You can now get a ZX80 kit for $200.

Sorry, right price, wrong advert.

You can now get a Minstrel 2 kit for $200. 1980s pricing.

Or you can get a Minstrel 3 kit for $200

Patreon

You can support me via Patreon, and get access to advance previews of development logs on new projects and behind the scenes updates. These are often in more detail than I can fit in here, and some of these posts contain bits from several Patreon posts. This also includes access to my Patreon only Discord server for even more regular updates.

Sunday, 17 November 2024

Fixing Autofire in VIC20 Bandits

Over the years of adding various games to the Penultimate cartridge, I have come across a few that behave oddly when you have a datasette or SD2IEC plugged into the VIC.

Games that automatically start or automatically fire, ones that don't register the joystick at all, and one that only moved right.

These games behave normally when there is not an SD2IEC connected, but play up when there is one.

One such game is Bandits. That seems to just autofire all the time, it does move correctly, but fire is stuck on.

For inexplicable reasons, joystick up, down, left and fire are read from port A on VIA #1. Joystick right is read from port B on VIA #2. I mean why? Who thought that was acceptable. Fire on a different port from the directions would have been more acceptable, maybe even useful, but why aren't they all just on the one port?

Port A on VIA#1 is arranged as follows:

  • VIA #1 Port A 0 -IEC Clock
  • VIA #1 Port A 1 -IEC Data
  • VIA #1 Port A 2 -Joystick Up
  • VIA #1 Port A 3 -Joystick Down
  • VIA #1 Port A 4 -Joystick Left
  • VIA #1 Port A 5 -Joystick Fire
  • VIA #1 Port A 6 -Cassette Sense
  • VIA #1 Port A 7 -IEC Attention

VIA2 Port B is keyboard column outputs

  • VIA #2 Port B 0 -Keyboard Column 0
  • VIA #2 Port B 1 -Keyboard Column 1
  • VIA #2 Port B 2 -Keyboard Column 2
  • VIA #2 Port B 3 -Keyboard Column 3 / Cassette Write
  • VIA #2 Port B 4 -Keyboard Column 4
  • VIA #2 Port B 5 -Keyboard Column 5
  • VIA #2 Port B 6 -Keyboard Column 6
  • VIA #2 Port B 7 -Keyboard Column 7 / Joystick Right

I am not sure if there were some last minute changes, but the arrangement of pins on that port, and the subset of those that appear on the userport are a little odd.

What is the point of cassette sense without the other cassette pins? What is the point of the bus side of the IEC ATN pin? Why have Up, Down, Left and Fire but not Right?

The multiple uses of VIA #1 Port A can cause problems with games, depending on how they test the port.

When you read that port, you get the joystick information you want, but also IEC and datasette signals that you don't.

It is fine if you work on the principle of testing if bit 5 is 0 to see if fire is pressed. But some games instead compare the value of the whole port against constant values. Those constant values are based on what they read back from their machines, which depends on the state of the things what they had plugged in, which does not necessarily match what you have plugged into your machine.

The standard resting state of that port on a VIC20 without a datasette or IEC device is $7E. If you press fire on a joystick, bit 5 changes, so the value now reads $5E. So you can write your game to check if the value changes to $5E, and if it does, fire must have been pressed, so you launch a rocket at a poor defenceless alien menace.

However, if the game was loaded from tape and the play button is still pressed down, the resting state would instead be $3E and when fire was pressed, would change to $1E. If you code was checking for $5E it would fail and you would be invaded by the aliens.

Bit 7
Bit 6
Bit 5
Bit 4
Bit 3
Bit 2
Bit 1
Bit 0
Value
IEC ATN
Cassette Sense
Joystick Fire
Joystick Left
Joystick Down
Joystick Up
IEC Data
IEC Clock
0
1
1
1
1
1
1
0
$7E
0
1
0
1
1
1
1
0
$5E
0
0
1
1
1
1
1
0
$3E
0
0
0
1
1
1
1
0
$1E

I have seen a few games with "please stop tape" messages, because you will get different values back from the port if the play button is still pressed down, and their code has been written assuming it was not. So rather than fixing their code, they just ask the user to press stop.

Aside: If I don't mention this, everyone will tell me about it in the comments, so yes, I am aware of the video by Robin (8 bit show and tell) about the C64 datasette controls.

And yes, I am aware of the Aquarius game from Aleksi Eeben that uses the datasette as a game controller (I was the one that arranged for TFW8b to duplicate the tapes for Aleksi).

End of aside.

With the IEC port, the power on state of the signals is usually the same, clock held low (asserted) by the VIC20, and data floating high.

That changes when you access the device, after that, the clock line is usually released by the VIC20, so both signals float high and stay high.

In that case, the resting value of $7E changes after an IEC access to $7F (or 126 changes to 127 in decimal in the BASIC demonstration below).

Again, if your code is checking for absolute values, then all your tests will fail because that one bit has changed.

Bit 7
Bit 6
Bit 5
Bit 4
Bit 3
Bit 2
Bit 1
Bit 0
Value
IEC ATN
Cassette Sense
Joystick Fire
Joystick Left
Joystick Down
Joystick Up
IEC Data
IEC Clock
0
1
1
1
1
1
1
0
$7E
0
1
0
1
1
1
1
0
$5E
0
1
1
1
1
1
1
1
$7F
0
1
0
1
1
1
1
1
$5F

An example of that is the game Multitron. It reads the port and compares against the resting state value it expects, if it does not see that, it assumes the user has pressed fire or moved the joystick and starts the game.

In the game, it tests for absolute values to see if you have moved left or pressed fire, all of which fail because bit 0 is now a 1 and on the developers machine it was a 0. Right is on a separate port, so reading that and comparing absolute values does work, so you can move right, but only right. In my testing notes, I had this down as a keyboard only game, as that is how it appeared both in the vice emulator and on a real VIC20.

They did have code in place to fix the datasette sense problem. When they read the value in, they OR it with $40. That forces bit 6 (datasette sense) to be a 1, so their comparisons become valid.

Fixing the IEC as well was just a case of changing that to OR the value read with $c3, this forces all the IEC and datasette bits high. I also had to adjust all the comparison values to expect the new resting state of $ff.

Rather unexpectedly, the game now has a title screen. This was never displayed before (or only for a blink of an eye) as the first time the game checked, it looked like fire was pressed, so started the game immediately.

It is quite a nice space invaders with a different mechanic for the invaders movements and limited firepower that takes a while to recharge, and plays much better with joystick.

I have fixed a couple of games like that, and a couple of others just needed the IEC port initialising correctly before starting the game.

Bandits, however, is proving to be more of a challenge. The test it is doing for fire is failing, so it is always firing. The other directions are fine, only fire is a problem.

The code is rather convoluted. I did go through it all in an excessively long Patreon post on the subject, but I decided to cut a lot of that out.

   lda $9111

   asl

   asl

   asl

   bcs label_b410

   and #$ef

label_b410

   asl $9120

   ror

   ora #$04

What it is doing is shifting bits around to rearrange what has been read from the port, and then doing comparisons on that.

The first two left shifts get rid of the IEC ATN and datasette sense signals, so that's good. It then reads the other port, extracts the joystick right and rolls that back into the first result, so they are all in the same byte. The OR at the end gets rid of the IEC clock signal.

For some reason, it tests the actual fire signal by shifting it out into the carry. If fire is pressed, it then sets one of the remaining bits low and uses that for future tests.

Unfortunately, that spare bit is the one used for the IEC data signal. It assumes that it will be high at the start, and sets it low if fire was pressed.

With an IEC device attached, that line ends up low, so the value ends up as a 0 whether fire is pressed or not, so the game thinks fire is pressed all the time.

The autofire can score quite well just sitting there, but ideally you would have control.

The code is quite tight, so there is no space to alter that in place to fix the issue. I did get it to work by changing the read port command to jump elsewhere, read the port, OR the data line high and return, but that adds quite a few cycles and I din't want to alter the timing in the middle of the game loop.

Checking out the signals on the scope, this is how Bandits starts up with no IEC device present. All the lines go low during reset, and then data (yellow, top) and ATN (blue, bottom) are released and float high. Clock (green, middle) stays low throughout.

However, if there is an IEC device present, the startup looks like this.

ATN goes back high, but for some reason, the data line does not. And that line reading 0 is what makes Bandits autofire.

VIC20 cartridges are started very early on in the boot process, so nothing is initialised. (frustratingly there is an entirely unnecessary jump to a routine a few bytes away and back that means the RAM which means the stack has to be working to do this, so VIC20 Dead Test cartridges need that RAM to be working before they can start)

Most cartridges just call the standard KERNAL routines to initialise the IO devices etc.

The normal start up those looks like this.

Do you see the little blip on the middle trace, the clock line? Let's zoom in.

It first releases the data line, then ATN, then releases the clock for 25uS and then goes back to the usual state of ATN and data high and clock low.

If there is an IEC device present, that will see the combination of ATN low and clock low as the start of a conversation, and will pull data low to acknowledge that it is ready to talk.

The normal VIC20 initialisation process pulses the clock line high to indicate to the IEC device that the conversation has ended, and as you can see, it releases the data line and we get back to normal.

Bandits is setting up various timers, so it makes sense to just initialise all the values in one go, just with the unfortunate effect of the IEC bus initialiation being skipped.

It initialises the port, releasing the data line but still driving the clock line low (like the normal VIC20 initialisation), and then releases ATN.

That leaves the "start of a conversation" signal in place, so the drive is duty bound to respond by pulling the data line low. And it stays low. There is no pulse on the clock line to tidy things up.

One of the odd things about Bandits, is that you can "fix" the problem by pressing the drive reset button. That snaps it out of this "waiting for a conversation" state and it releases the data line and the auto-firing stops.

There didn't seem an appropriate place I could add in the clock pulse that would clear things, so I went for the alternate option of changing the initialisation values so that it released the clock line and then releasing the ATN line. That means it should never enter the "start of a conversation" state, and so the drive should not assert the data line, and the guns will be silenced.

Hurrah.

All it took was changing two of the 65536 bits in the 8K ROM - it just took a while to work out which ones.

Can I take the rest of the week off now?


Advertisements

The patched versions of Bandits and Multitron are included in the new Penultimate +3 version which also has a built in SD2IEC drive.

More of those should be shipping this week, I will go into more detail in a full post to follow.


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.

More info can be found here:

Bluesky

For those interested in such things, I can now be found on Bluesky, and I expect to be posting more there.

Patreon

You can support me via Patreon, and get access to advance previews of posts like this and behind the scenes updates. This particular post was originally about twice as long before I cut it down, so if you want all that detail, join the Patreon. This also includes access to my Patreon only Discord server for even more regular updates.