Sunday, 24 November 2024

Five years of the Minstrel 3

Five years ago to the day, I put up a blog post announcing a new kit for sale. The Minstrel 3, a Z80 based, ZX81 compatible computer kit.

Today it is back, and for sale on Tindie again, and there is 5% off all Minstrel kits for the next month.

Shipping is only going to get slower as we move into the holiday season, so the sooner the better if you want to be sure to get your order in time.

Remember 2019? Lots of things have happened in those five years.

In that time, I have tweaked the Minsterl 3 design a bit, and added things like the Minstrel Expansion Bus header, so you can now add a microSD disk drive via the Minstrel Micro ZXpand that plugs directly into the board.

I have brought it back with two PCB options, one of which is a single board design, with the integrated keyboard.

I still have the versions without the keyboard for use with a ZX81 membrane or other keyboard, in a ZX81 case or whatever you can dream up. Still ideal as a drop in replacement for a dead ZX81.

During that time, the Z80 has also been discontinued, but fear not, I have secured a lifetime supply of Z80 chips, so these kits will be available with some of the last genuine Z80 chips ever made, date codes shortly after the last time buy deadline.

As usual, I have built one up to test the new PCB.

Whilst doing that, an idea for a change has occurred to me.

(actually, it was shortly after I had soldered in the bit that needed to be changed, but lets not mention that)

Yay, significantly past the last minute update.

Although it is something I have tried to address several times before - loading from tape on a Minstrel 3 isn't always great.

It works fine with my modified TZXduino / master tape creator, and some of the time with my Sony TCM-818, but I know people have problems with PC soundcards and phone apps and cassette recorders with lower level outputs

The problem stems from the logic chips used on the input port. On the ZX80 (and Minstrel 2), this was a 74LS365, a hex tri-state buffer.

The ZX81 ULA implemented something similar internally.

This operates in two states:

  • Reading the port
  • Not reading the port

When the port is being read, the input signals are transferred to the CPU databus. When the port is not being read the chip disconnects from the CPU databus and lets other things drive it.

This is a 74LS series chip, which means the input voltage thresholds are as follows:

  • Logic 0: 0 V to 0.8 V
  • Logic 1: 2.0 V to 5.5 V

That means the tape signal needs to reach 2V to be considered a 1.

When I designed the Minstrel 3, I moved to 74HC series logic for all the chips, these are faster, lower power, cleaner signals etc. However, one problem is the input voltage thresholds are now higher:

  • Logic 0: 0 V to 1.5 V
  • Logic 1: 3.5 V to 5.5 V

That means the tape now needs to be over 3.5V to register as a 1.

I have looked at various options over the years, different ways for boosting or biasing the signal. Transistors, op-amps, comparators etc. but none of them were quite right. Most of them helped in some situations, but not all.

For the Minstrel 4D (still available from TFW8b - https://www.tfw8b.com/product/minstrel-4d-turbo-jupiter-ace-compatible-computer-kit/), I used a microcontroller with a built in op-amp, and comparator with configurable references, so I was able to add a menu option to select a threshold voltage to suit various inputs and that worked quite well. I would like to use something like that with the Minstrel 3, and maybe one day there will be a Minstrel 3D that can use similar technology.

Testing the problem

Revisiting this problem afresh with this update, I have been trying things out.

I setup a Minstrel 2 (also back with optional integrated keyboard), and a recently refurbished Sony TCM-818 cassette recorder and tried to load some tapes.

I chose a selection of tapes ancient and modern, and tested the ones that would work on a ZX80 with the 8K ROM and most of them loaded reliably at about 3/4 volume.

I then switched to a Minstrel 3 and found that most of them didn't load.

I have been testing out a new module for the Minstrel Expansion Bus, the Minstrel Input Monitor.

(that is something else that changed, I created the Minstrel Expansion Bus and started to add a series of modules, but it seems no one was interested in that, so I haven't listed those on Tindie yet. Anyone interested? - http://blog.tynemouthsoftware.co.uk/2024/05/minstrel-expansion-bus.html)

The new module, the Minstrel Input Monitor shows when a key is pressed or data is being loaded from tape on two LEDs and also optionally via a sounder.

On the Minstrel 2, I could clearly hear the data being loaded.

On the Minstrel 3, the data just wasn't getting through as well.

I tried with the TZXduino, and that was fine, as expected.

How can I fix it?

Well, this was something that worked with 74LS input levels, but not with 74HC input levels.

So, why don't I use a chip with 74LS style inputs, either actual 74LS serise chips or 74HCT.

The 74HCT series has 74LS style inputs, and 74HC style outputs, so should work?

Well, it is not a good idea to mix logic families, a 74LS output will not generate a high enough logic 1 output to drive a 74HC input. However, a 74HC output can drive a 74LS input.

So I need a 74HCT365?

Well not quite, the design evolved a bit.

In designing the Minstrel 3, I tried to reduce the chip count where I could, and I found a way to save a chip or two.

There are only two things which drive the CPU side of the databus directly. The 74LS365 input port, and two 74LS05 chips which form the NOP generator.

This forces the CPU side of databus to all zeroes so the Z80 executes a NOP instruction whilst the rest of the circuitry on the other side of the 1K resistors is generating the display.

The 74LS05 is an open collector inverter, so if the input is high, it pulls the output low. If the input is low, it does nothing. (if you want it to read as a logic 1, you need a pull up resistor, for example, the first 74LS05 gate on the left).

I need to consider three states here

  1. The input port is being read
  2. A NOP is being generated
  3. Everything else

When the input port is being read, the 74LS365 is driving the Z80 data bus with the input port of the tape and keyboard signals.

When the NOP is being generated, the 74LS05 chips are driving the bus low to mimic a NOP command op code ($00).

When "everything else " is happening, the 74LS365 outputs are high impedence and the 74LS05 outputs are off.

I didn't want to use the 74LS05 chips in the Minstrel 3 as there was not a 74HC05 chip I could use in it's place.

The 74LS05's did actually survive until version 3.5 of the design.

Version 3.5.1 went to a 74HC244 to replace the 74LS05s.

I thought since I was using that, I may as well use the same chip for the 74LS365 input port.

The 244s didn't last long, I don't like those as the outputs and inputs are on alternating pins which makes routing more complicated.

I had already tried a pair of 74HC245 tri-state buffers in an earlier version, as the inputs are all on one side and the outputs on the other, which is much neater.

That was the very first red V3.0 prototype which was still using 74LS logic, I went back to the 05's for V3.1.

After seeing that, I remembered I didn't like the row of 20 pin chips, they didn't seem quite right.

I already had 74HC257s in the design, so I wondered if I could use a pair of those to replace the pair of 74HC245 buffers?

The 74HC257 works in three modes

  1. Inputs A are connected to the output
  2. Inputs B are connected to the output
  3. The output is disconnected, high impedence.

That fits with the requirements of the input port and the NOP generator. I just need a "NOP or Input port" signal to control the output on or off, and a second signal to select between NOP and input port.

The result is two 74HC257s replacing the 74LS365 and two 74LS05s.

I looked at two options here, the logical move would be to replace those two 74HC257s with two 74HCT257s. Those have TTL level inputs, but output the same as the 74HC series.

However, I have decided to go for two 74LS257s instead.

The output stages are different. the HC and HCT series use two FETs to drive the output strongly high or low.

Where as the LS series have a strong drive low via a transistor, but the drive high is via a pullup resistor, in this case nominally 100Ω.

The way this is used in the overall design is this feeds the Z80 directly, whilst the ROM and RAM are being used to generate the video signals. The two sides are connected via 1KΩ bus isolation resistors.

ZX81 External Keyboards?

I was hoping this might help with the issue of external keyboards (http://blog.tynemouthsoftware.co.uk/2022/05/zx81-external-keyboards-and-the-minstrel-3.html).

The problem is external devices are connected to the isolated side of the databus. The ZX81 external keyboards rely on driving the bus at the same time as ZX81 ULA, so either can indicate a key is being pressed. They do not work on a ZX80, or the Minstrel 2 or 3.

On the ZX80 and the Minstrel 2, you end up with a situation something like this, the external keyboard on the left trying to influence the Z80 on the right.

When neither keyboard is pressing a key, you end up with the datasbus seeing a logic 1 due to the 100Ω pullup on the right. The one on the left is also pulling it high, but via the 1KΩ resistor, so you effectively end up with 1100Ω in parallel with 100Ω, which means the bus is pulled up by a bit less than 100Ω. (N.B. I am assuming ideal transistors here just to make things easier).

When the 257 drives the bus low due to the internal keyboard or NOP generator, no pullups are involved. It is directly driving the databus low, so it does not really matter about the 1100Ω combined pullup on the external keyboard side.

The problem is the reverse does not work.

When the external keyboard tries to drive the bus low, the 257 is trying to drive it high, so you end up with a potential divider, 1KΩ to 0V and 100Ω to 5V. That gives a voltage on the Z80 data pin of about 4.55V, which still very much counts as a high and the external key press does not register.

In order to register as a logic 0, the bus isolation resistor would need to be dropped to something like 50Ω, which does not sound like a good idea as that would put a load of 100mA for each bit on the bus.

In order to make this design work with an external keyboard needs an open collector drive, which is probably what the ZX81 ULA does. I have tried to find a way of doing that, ideally an open collector version of the 257, but I cannot find such a thing. It would either need a couple of 74LS07's or 74HC125s or maybe diodes on each of the drive signals between the 257.

It looks like changing to a 74HCT257 or 74LS257 will help with tape input, but neither will help with external keyboard situation unfortunately.

The control signals are both generated from 74HC outputs, those can drive LS style inputs, so that is OK.

The input port signals are from the keyboard, which is a matrix of the Z80 address lines, via diodes with pullup resistors. That should be fine. The tape input, as discussed, is better feeding a 74LS input.

The 365 is a hex buffer, so only drives bits D0-D4 (the keyboard) and D7 (the tape input).

There is also one extra pin which is cheated on the ZX80. If a diode is fitted, D6 reads as a zero to indicate NTSC, without the diode, it floats up to a 1 for PAL. That can be replaced by a 0V or 5V jumper to select PAL or NTSC.

Bit 5 is not used, so that is set to high, so that it could be overloaded externally if that were ever required.

The NOP generator inputs are just tied to 0V, so that's fine.

The outputs are wired direct to the Z80 databus, and that is the same Z80 chip as I use on the Minstrel 2, so it is fine with 74LS outputs. Nothing else drives the databus directly, so there is nothing to conflict with.

Sorted.

The extent of the changes required is updating the silkscreen from "257" to "LS257".

That was easy.

Let's give it a go.

And yes, it works perfectly.

OK, it works as well as the Minstrel 2 does. As well as a ZX80 would. And probably about as well as a ZX81 would do, since the ULA inputs would have been modeled on the 74LS TTL chips they were replacing.

The only ones I couldn't get to load were things like 3D Monster Maze, where you can clearly here the tape is worn out in places and is never going to load again. (blog post to follow.....)

I will continue testing, but I think that's a given now. I will leave the other 74HC257s as is. They need to remain as they are, so I will just need to be clear where the 74LS257s go on the board. The 74HC257s are marked "257", and the two non-HC chips are marked LS257.

The next batch of PCBs will have this marked, the last of the current ones will get a white paint dot on this two chips.


Advertisements

Minstrel 2 and 3 kits are available from my Tindie store, with worldwide shipping and 5% off for the next month. Order soon to beat the usual postal slow downs in December.

Versions are available for ZX81 case:

Or standalone with keyboard:

And also Minstrel 3 with ZXpand microSD card interface:

I am in the process of moving things over there from my SellMyRetro store, so if there is anything that you want, let me know and I'll add it.

I have recently added lots of PET repair and upgrade parts, more to follow.

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. 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.