Sunday, 29 January 2023

Odd keyboard issues, or "My Spectrum Won't Stop"

There is an odd fault you occasionally see on the ZX Spectrum where all of the keys work, but some key combinations do not. The usual one to test is SHIFT + A, which should generate the STOP keyword, but that does not register. A on it's own works. SHIFT + most of things work, just not SHIFT + A and a few others.

In some case, it can be down to the Z80, but it is often the ULA. Changing either can fix the issue, but that's not ideal. There is a fix, which usually works, which involved changing the pullup resistor on the column from 10K down to 6K8 or 4K7.

What is going on here?

This is one of those cases where the digital electronics are affected by their analogue properties. The basic circuit for the keyboard can be seen by looking at a single key:

When the key switch is not pressed, the column input is pulled high via the 10K resistor and reads as a 1. During scanning, the address line is used to select the row. When scanning the other rows, A10 will be high, so if the switch is pressed, the diode will prevent any current flowing and the column will read a 1 again. If this switch is pressed and this row is being scanning, A10 will be low, the diode will conduct and pull the column input low and a 0 will be read.

See a previous post on the whole range of 5x8 keyboards used by Sinclair and others in the 1980s

http://blog.tynemouthsoftware.co.uk/2022/05/sinclair-5x8-keyboards.html

It is difficult to visualise what is going on inside a ULA, so to better demonstrate this, I am going to look at the ZX80 / Jupiter Ace implementation. These use discrete logic chip, the input being read by a 365/367 input buffer. With that, I can show both the input value being read and the logic output that is generated.

On these traces, blue is the trigger, the pulse which causes the keyboard input to be read. Green is the keyboard input, and yellow is the logic output. You can ignore the yellow trace either side of the trigger as that is ROM read cycles.

And that works fine most of the time. You can see the switch is pressed and when sampled, the output is interpreted as a logic 0.

When there are no key pressed, it is likewise clear that it is a logic 1. (again ignore the yellow trace either side, I just picked one where the ROM reads were low to show the change)

It is less clear cut when the key is not pressed, but another key in the same row is pressed. Rather than being pulled straight back to 5V, the input takes a while to recover. The input capacitance of the gate comes into play. It is usually only 5-10pF, but with a 10K resistor, that is a time constant in the 10s to 100s of nS, which can be important when the sample pulse is less than 800nS.

Even more so when two other keys in the same column are pressed. The trace only just gets past half way back to 5V before the scan is complete.

The above traces are using 74LS logic, where the logic threshold for a 1 is over 2.0V.

With 74HC logic, this is raised to 3.3V, and you can see the interpretation is very different.

The input signal only passes the logic threshold to be read as a logic 1 right at the end of the read cycle.

And with two switches on the same column pressed, it doesn't even register at all.

I think what is happening here is not that it is failing to read the SHIFT + A keys as being pressed, but that it reading all the keys in the row as being pressed. When they all appear to be pressed, it aborts the read.

During the development of the Minstrel 4D, the first prototype suffered from this problem. Here it was SHIFT + 1 that was not working. This was a little odd, since the schematic was the same as the Ace and the Minstrel 4th. Neither of which suffered from this issue. I guess it's more of the analogue influence, and the extra length of the traces in the keyboard matrix are enough to cross the line.

Solutions

Reducing the pullup resistor value

There are a few ways to deal with this. In the Spectrum world, the solution seems to be putting a 10K resistor in parallel to drop the resistance to about 5K, or a 22K in parallel to bring it or about 6K8. Or just swapping out the resistor for a 4K7 or 6K8 resistor. Either way, reduce the value of the pullup resistor to reduce the RC time constant and speed up the recovery.

I hit this issue in the design of the Minstrel 3. The ZX80 design actually used 47K pullup resistors and 74LS gates, and that had worked fine on the Minstrel 2. For the Minstrel 3, I had moved to 74HC logic and found that some SHIFT + key combinations did not work. Reducing the values to 10K (as used on the Spectrum) resolved the problem in that case. (the first run of Minstrel 3 PCBs have the pullups marked 47K, and are probably worth at least £1M each now)

Use 74LS logic

This is a simple option, switch the gates doing the reading over to 74LS logic with their lower logic threshold. 74HCT logic may also be an option, but 74LS has the extra benefit that it does reduce bus contention when used with an external keyboard - see the previous post on issues with those:

http://blog.tynemouthsoftware.co.uk/2022/05/zx81-external-keyboards-and-the-minstrel-3.html

Drivers

An alternative is to drive the row lines via a buffer, rather than using the address lines directly. This is used on the external keyboards I reverse engineered in the above post. I imagine the effect is multiplied due to the longer cables.


This also adds the option is disabling the drive to the rows when they are not being scanned. That would probably help EMC (if that is important to you).

It would be neat if the buffer and 8 diodes could be replaced with a single octal open drain buffer. I think the 74x641 will do the job. This is an open drain bus transceiver, it is only needed to work on one direction, but should in theory do the job.

The high numbered 74 series is usually where all the esoteric stuff lives, so it's not a great part to use when supply issues are commonplace, but I will see if I can get some to give it a go.(I couldn't. I didn't.)

I did consider making a suitable buffer using a 16V8 GAL, but adding programmed parts to a kit is never a popular move. Two 74LS07 chips would be another alternative.

The Harlequin ZX Spectrum clone went for a row of PNP transistors. That does the job quite neatly if you can find the 'E line' packaged versions that fit in line.

They line up quite neatly in that package.

Minstrel 4D

For the Minstrel 4D, the best option was to change the buffer IC from a 74H3265 to a 74LS365. That solved the SHIFT+1 combination not responding, and also gave additional protection against RC2014 bus cards that write to the same address.


Advertisements

Minstrel 4D

The Minstrel 4D kits are shipping now, you can order one from The Future Was 8 bit

https://www.thefuturewas8bit.com/minstrel4d.html

More info in a previous post:

http://blog.tynemouthsoftware.co.uk/2022/08/minstrel-4d-overview.html


Sell My Retro

PCBs for the Minstrel 2, 3 and 4th, as well as Mini PET and other projects, and keyboards for all the above, can be found at my Sell My Retro store:

https://www.sellmyretro.com/store/tynemouth-software

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. This also includes access to my Patreon only Discord server for even more regular updates.

https://www.patreon.com/tynemouthsoftware

Saturday, 21 January 2023

Driving the AY-3-8910 / YM2149 from an 8 bit micro

In several previous posts, I have been building and using the RC2014 YM2149 sound card from Z80kits.com.

In the last post, I glossed over a strange anomaly in the way these devices are driven from the Z80 bus. It all goes back to the original use of the General Instruments AY-3-8910 chip, which was with the early General Instruments PIC microcontrollers like the PIC1650, or the General Instruments CP1610 microprocessor (as used in the Intellivision console).

Most other chips used on 8 bit microcprocessor systems in the day were designed for things like the 65xx/68xx series or the 8080/Z80 series, and as such use a separate data bus and address bus. Interfacing to these is fairly straightforward.

65xx/68xx series

These microcprocessors have an 8 bit data bus and usually a 16 bit address bus. IO is memory mapped, and everything is controlled from a single R/W pin, high for reads, low for writes.

There are two states for accessing external devices:

R /W
State
0
Write to external device
1
Read from external device

Z80/8080 series

These microcprocessors also have an 8 bit data bus and usually a 16 bit address bus. IO and memory are accessed separately with either a /IO_Request or /Memory_Request signal, and there are separate /Red and /Write signals.

As well as various other modes for interrupts etc., there are four states for accessing external devices:

/Read
/Write
/IO Request
/Memory Request
State
0
1
0
1
Read from external IO device
0
1
1
0
Read from external memory device
1
0
0
1
Write to external IO device
1
0
1
0
Write to external memory device

/WR and /RD both low is invalid, both high is inactive. /IORQ and /MEMRQ low is invalid, both high is inactive.

CP1610

These microprocessors have a combined 16 bit address and data bus, and these are controlled by a BusDirection signal (BDIR) and two BusControl lines (BC1, BC2).

There are eight states used on those systems, but for simplicity, I will only look at the four used by the AY-3-8910.
BDIR
BC2
BC1
State
0
1
0
Inactive
0
1
1
Read from external device
1
1
0
Write to external device
1
1
1
Latch Address

BC2 is always high in these situations, so can be pulled high or wired direct to 5V as it is in all of the circuits below. There are also two additional address lines, A8 and /A9 (only on the 40 pin chip), these are not used in these applications, and just pulled high and low respectively.

As a side note, look at that pinout. See if you can spot D0 and D1. Why couldn't they just put them in order. It's like the databus on a Z80, that is just annoying.

How do other computers do it?

These signals don't easily map to those used on the Z80 or the 6502, and looking at machines from the 1980s, various methods were employed to get around this.

Amstrad CPC464

To get around the complications of driving these pins directly, the Amstrad CPC464 uses an 8 bit IO port on an 8255 PIO chip to drive the data bus, and two lines from a second port to drive BDIR and BC1.

This also avoids any issues of the speed of the bus interface (which seems to vary from datasheet to datasheet), since the data can be latched on the IO chips output port for as long as necessary and the BC1 and BDIR singals toggled before and after setting it.

This may seem wasteful of an IO port, but the AY-3-8192 chip used has it's own 8 port which is used to drive the keyboard and joystick. Slightly convoluted, but it works.

Oric 1 / Atmos

The Oric solution is very similar. A port on the 6522 VIA is used to drive the databus, which is shared with the printer port. Two of the handshaking pins on the 6522 drive BC1 and BDIR.

The Oric 1 PCB has a direct connection from CB2 to BDIR, but some Atmos boards had the track cut and a 1nF capacitor installed, which would delay the BDIR pulse slightly. As seen on a previous repair blog:

http://blog.tynemouthsoftware.co.uk/2022/11/oric-atmos-repair.html

The IO port on the AY-3-8192 is used to drive the keyboard.

The Oric is the only one to directly tie the three outputs together, the rest use a resistor mixer to combine the three output channels.

ZX Spectrum + / +2

The ZX Spectrum + and +2 drive the data bus direct, but the BC1 and BDIR are driven by a combination of diodes and resistors, redrawn below. The IO port is used to drive the 1488/1489 buffers that form the keypad and serial ports.

If the PSG (Programmable Sound Generator) line is low, BC1 and BDIR will be pulled low via the diodes, leaving the chip in the inactive state. If that is high, the diodes do not conduct, and the two pins are driven via resistors. BDIR is driven from /RD, and BC1 is driven from A15.

Connecting the AY-3-8910 to the RC2014 bus

The RC2014 YM2194 / AY3-8192 sound card also uses a direct bus connection, but the generation of the control signals is more complicated. I've redrawn that to work it out, with the jumpers set in their default positions. The board can be jumpered for various other combinations (including ZX Spectrum mode), but I have drawn it in the "default" mode. I have shown the 28 pin AY-3-8192 as I had that drawn already. The board actually uses the 40 pin AY-3-8190 which just has an extra 8 bit IO port, but as seen in a previous post, you can use an AY-3-8192 in an adapter socket.

The first 74HCT138 is used to decode addresses in the range D0-DF, shown as /Dx, inverted to give Dx.

The second 138 is enabled when the current address is in the Dx range, A1 is low (so D0, D1, D4, D5, D8, D9, DC, DD) and an IO Request is in operation. This is further filtered by the three address inputs. Three outputs are generated, /D8_Read, /D0_Write and /D8_Write. These are combined by the two 74HCT00 NAND gates to give one signal which is high when there is a D8_Read or a D8_Write; this drives BC1. The second signal is high when there is a D8_Write or a D0_Write; this drives BDIR.

That gives the following results:

Signal
BDIR
BC2
BC1
State
-
0
1
0
Inactive
D8 read
0
1
1
Read from external device
D0 write
1
1
0
Write to external device
D8 write
1
1
1
Latch Address

As you can see, that's not quite what you would expect. D0 (also D1, D4 and D5) is the data register. Writing there is a write to the sound chip, that's OK.

D8 is the address latch (also D9, DC and DD). Writing there latches the address.

However, the read operation is set for D8, so to read from the chip, you read from the register port rather than the data port. I am not sure if there is a specific reason for this, maybe compatibility with some existing systems? (and no, you can't change that with the jumpers)

The D0 and D8 addresses in hex are the 208 and 216 addresses in decimal in the previous posts.

The full schematic for this V5 version of the board can be found on the designers github:

https://github.com/electrified/rc2014-ym2149/tree/4b8af5396633bc87178b81087cec0f71b8307908

Note there is also a V6 version of the board which removes the D0/D8 access and uses addressing compatible with the Spectrum and MSX:

https://github.com/electrified/rc2014-ym2149

Could it be fixed?

Swapping the output of the second 138 from pin 12 to pin 13 should make it respond to reads on the data port instead.

I guess the existing board has been around long enough now that reading the register port is the accepted way of doing it, and to change it would break existing code.


Advertisements

Minstrel 4D

The Minstrel 4D kits are shipping now, you can order one from The Future Was 8 bit

https://www.thefuturewas8bit.com/minstrel4d.html

More info in a previous post:

http://blog.tynemouthsoftware.co.uk/2022/08/minstrel-4d-overview.html


Z80 Kits

The YM2149 Sound Card is available from Z80Kits.com

https://z80kits.com/shop/ym2149-sound-card/


Patreon

You can support me via Patreon, and get access to advance previews and behind the scenes updates. These are often in more detail than I can fit in here, this post contains bits from three Patreon posts, and there is a forth and fifth to follow. This also includes access to my Patreon only Discord server for even more regular updates.

https://www.patreon.com/tynemouthsoftware

Sunday, 15 January 2023

Some simple Forth programs for the Minstrel 4D

In the previous post, I looked at Testing the AY-3-8910 / YM2149 Sound Card for RC2014.

As part of that, I included several simple test programs written in Forth, the native language of the Minstrel 4D I was using (and also the Minstrel 4th, and of course the Jupiter Ace).

I asked in the post if there would be any interest in a blog post going through these programs, and there was, so here it is.

Immediate mode

The first test was entered in immediate mode, i.e. a series of commands typed in and executed immediately, rather than a program of any sort.

I will probably be doing a lot of comparisons with BASIC, since many of the readers of this blog will be more familiar with that.

In the case of BASIC, this is like doing PRINT 2+2, rather than doing 10 PRINT 2+2

The program started with two CONSTANT definitions. These are simply there to make the rest of the program easier to follow. There are in the form of value CONSTANT name, in BASIC terms, LET value = name., or in C #DEFINE name value

216 CONSTANT REGPORT
208 CONSTANT DATPORT

These are the addresses that are used to latch and address or data into the sound chip.

Once those have been set, they can be used in place of a number

0 REGPORT OUT
0 DATPORT OUT

So these would be the equivalent of

0 216 OUT
0 208 OUT

The OUT statement here is used to write to an IO port. value port OUT. (in Spectrum BASIC, OUT port, value). So this writes 0 to the register port, and the next line writes 0 to the data port.

The constants just makes it easier to follow, and also saves you having to look up which one is which, particularly when sending similar values. For example:

CTRL_REG REGPORT OUT
MODE1 DATPORT OUT

Instead of

200 216 OUT
108 208 OUT

The next line is is actually two commands, and is the first inkling of the power of Forth.

REGPORT IN .

The first part reads a value from the data port and puts it on the stack. In Spectrum BASIC LET X = IN port .

This could have been 208 IN .  but the constant hopefully makes it easier to follow.

The second port is a full stop, surrounded by space. Space is important in Forth, it can be one or more spaces and or newlines. Everything else is either a word (i.e. a command or construct) or a numeric literal. Everything including . and ; and : etc. are words, so all must be surrounded be space when you are typing them in.

When doing things like this, it's quite handy to cut and paste text over the serial link to the Minstrel 4D, so you can type in the programs in this blog.

When a program is typed in, all the whitespace is stripped off and a single space is stored. Stored programs are listed with newlines inserted when appropriate and spaces used to indent as required. Not always where you would like then (i.e. I would prefer 2000 0 to be on a separate line), but good enough most of the time.

Back to the full stop. This is the print command. It writes out to the screen the thing on the top of the stack. So to go back to Spectrum BASIC again, this is LET X = IN port : PRINT X but you don't need to worry about creating the X variable, it is all passed along the chain. I don't want to go into too much detail about this here, the stack in Forth is very powerful, see Minstrel 4D Quick Start Guide for more information.

http://blog.tynemouthsoftware.co.uk/2022/11/minstrel-4d-quick-start-guide.html

So REGPORT IN . reads a value from the register port (I have another post to follow about why that is the register port and not the data port) and prints it to the screen. You can do that in BASIC, PRINT IN port, but only with simple cases like that.

The rest of the programs continues in a similar way, writing to ports and reading back, so I won't go into that any further.

Strings

A full stop . is used to print the number on the top of the stack, If you want to use strings, you use full stop quotes and then quotes: ." and " e.g.

." Hello, World"

Spacing is important here, it should be: space ." space string" space. There is no space between . and " at the start, and no space between the end of the string and the closing "

What's the word?

It is time to start writing a program. It may be easier to think of functions in C or Python etc. BBC BASICs DEF PROC is probably closer I suppose, but maybe less familiar.

A simple example would be

: INPRINT
  REGPORT IN .
;

This defines a word called INPRINT, which does the read from a port and print to the screen, so you can just type INPRINT and it will execute IN REGPORT . The names are not case sensitive, so you can also type inprint if you prefer, but I will stick with uppercase here for clarity.

The : and ; surround the definition. So you have : name code ; The spaces around : and ; are important. They are words themselves, and all words need to be separated by white space.

So, in C, you would have something like

void inprint()
{
    byte x = in(REGPORT);
    print(x);
}

That of course is a bit limited as the port address is hard coded, so you would probably want the equivalent of

void inprint(byte port)
{
    byte x = in(port);
    print(x);
}

In Forth, you don't have a parameter list as such, you again use the stack, so the more generic version is just

: INPRINT
  IN .
;

When this is run, there is no value before IN for the port number, so how do you put one there? Easy, you just put it before the word, so

REGPORT INPRINT

In function terms, you could think of that as passing a parameter to a function, but in Forth terms, it is just using the stack as before.

DO..LOOP

Next, I will go to a different blog post, one about the Digital I/O Card for RC2014 (and Minstrel 4D).

Here, a simpler version of the program I included, one that writes very fast to the IO port, counting up in binary on the LEDs, too fast to see.

0 CONSTANT LEDPORT
: FASTLEDONCE
    256 0
    DO
      I LEDPORT OUT
    LOOP
;

This is a "do loop".

number_of_steps starting_value DO stuff LOOP

Sort of FOR I = 0 TO 255 : stuff : NEXT I but it is often easier to think in terms of the number of steps, rather than thinking in terms of N-1, like the stop at 255.

Within the loop, I can be used to get the current count, so here the value of I is being written to the LED port (set using a constant as previously discussed). So this writes 0, 1, 2, 3 etc. up to 255 to the LED port. But that only does it once. You could do another DO .. LOOP to make it do that 10 times, but I will use another construct instead.

BEGIN...UNTIL

This is sort of the while loop in C terms, and is BEGIN stuff condition UNTIL. This does stuff until condition is true (i.e. not 0).

: FASTLEDLOOP
  BEGIN
    FASTLEDONCE
    0
  UNTIL 
;

This is a simple example, the condition will always be 0, so it will keep looping until you press BREAK (shift + space).

You could set a condition, say you have a switch wired to the Digital IO card that returns 0 unless a switch is pressed, so test that each loop after the LEDs have been flashed.

0 CONSTANT SWITCHPORT
: FASTLEDLOOPSTOP
  BEGIN
    FASTLEDONCE
    SWITCHPORT IN
  UNTIL
;

Note there is no . after the IN, this is not printing the result, it is reading it, and then leaving it on the top of the stack. When UNTIL comes around, it will check the top of the stack for the condition to test and loop back to BEGIN, UNTIL it is true.

Previously this was the 0 that was hard coded, now it is the result of the IO read. This again is passing things around using the stack, so no need for extra variables, just make sure all the bits are in the right order.

Combined all in one, that would be

0 CONSTANT LEDPORT
0 CONSTANT SWITCHPORT

: FASTLED
  BEGIN
    256 0
    DO
      I 0 OUT
    LOOP
    SWITCHPORT IN
  UNTIL
;

The equivalent in C, would be something like

do
{
  for(int i=0; i<256; i++)
  {
    out(i);
  }
}
while(in(0) == 0);

Or in BASIC, something like

10 FOR I = 0 TO 255
20 OUT 0, I
30 NEXT I
40 IF IN(0) = 0 THEN GOTO 10

IF..ELSE..THEN

OK, if you aren't already confused, then let me throw this at you. Forget what you know about BASICs IF THEN, and C's if { } else {} and consider this alone and it makes perfect sense, honest.

condition IF stuff THEN rest of program

So you have a condition first, that comes off the stack. That is evaluated, and IF condition is true, stuff is executed. THEN it gets on with the rest of the program.

16 0
DO
  I 10 < IF
    SPACE
  THEN
  I .
  CR
LOOP

In this extract, there is a loop, 16 steps from 0 to 15, and the value I is printed to the screen. There are various very powerful ways to format strings in Forth, but I've not fully worked those out yet, so this is a simple version to pad out single digit numbers

Here the condition is I 10 <  This is a less that test, in the form of a b < or "is a less than b". This is where it starts to look like reverse Polish notation for anyone familiar with early digital calculators.

So the condition is true when I is less than 10. IF this is the case, it prints an extra space using the SPACE word, THEN it goes on to the rest of the code.

(This could have been IF ."  " THEN, but SPACE is easier as you have to remember there is one space after ." for separation, then the actual space to be printed, then the closing quote).

When I put the preview version of this post out to my Patreon supporters, it was suggested that would make a nice word that could be reused.

: .2DIGITS
  DUP 10 < IF
    SPACE
  THEN
  .
;

So the test function would become

: TEST
  16 0
  DO
    I .2DIGITS
    CR
  LOOP
;

That also introduces the concept of keeping the stack in balance. It is important to keep track of when you are putting things on the stack, and when you are taking them back off again. The < test would take the last value off the stack to test it, and then it would be thrown away. When it came to the . to print it, that would not be there and it would go to the previous value from the stack, which is not what you wanted. DUP takes one thing from the stack, then duplicates it and puts both copies back. That way, you can use one for the test, and one for the print. (There is also ?DUP that only does the duplication if the value is non-zero, which is quite handy, but lets not get too complicated just yet). Thanks to George Beckett for that suggestion.

Taking another extract from one of the sound card test programs, the test condition, in this case, it was the result of reading the register port.

  REGPORT IN 31 <
  IF
    ." AY-3-8910/2"
  ELSE
    ." YM2149"
  THEN
  ."  Detected"

Here, REGPORT IN reads a value and leaves it on the stack, then we get our test. In this case, the first value comes off the stack, just after it is placed there by the IN command. The second value is the literal value 31. The result is put on the stack, and used by the following IF command.

condition IF stuff ELSE otherstuff THEN rest of program

IF the condition is true, it will print AY-3-8910/2, ELSE, it will print YM2149, THEN it will go on with the rest of the program and print Detected.

You see, that does make sense. Just not IF you have been writing IF THEN statements for 40 years. THEN it takes a bit of getting used to

Full example

Here is a full program from the sound card blog post

216 CONSTANT REGPORT
208 CONSTANT DATPORT

: DETECT
  CLS
  1 REGPORT OUT
  31 DATPORT OUT
  
  REGPORT IN 31 <
  IF
    ." AY-3-8910/2"
  ELSE
    ." YM2149"
  THEN
  ."  Detected"
  CR
  CR
;

The full program adds two new words, CLS which is just like the BASIC CLS, and clears the screen. (PRINT "♥️" for Commodore people who seem to think that is in any way acceptable).

The second is CR. This just prints a carriage return, because the standard stuff . command is actually equivalent to PRINT "stuff"; (with a semicolon, which does not move to the next line) rather than PRINT "stuff" (without a semicolon, which does).

Homework

I think that is enough for now. Just a few simple constructs that are enough to write little test programs I used in the last few posts. Hopefully that will give you a taster of how powerful Forth can be. And I haven't even mentioned how much faster these examples would run compared to BASIC equivalents as so many of these map down fairly directly into machine code.

I will leave you with another of the programs from the sound card post. see if you can now understand what it is doing.

216 CONSTANT REGPORT
208 CONSTANT DATPORT

: REGS
  CLS
  ." YM2149" CR
  ." Real" CR
  CR
  
  16 0
  DO
    I .
    I 10 < IF ."  " THEN
    I REGPORT OUT
    255 DATPORT OUT
    I REGPORT OUT
    REGPORT IN .
    CR
  LOOP
  CR
;


Further Reading

Here are some links that contain further information to help you go Forth in your programming ability.

I would also highly recommend the Jupiter Ace Forth Programming manual by Steven Vickers. This is the manual that originally came with the Jupiter Ace. It was reprinted for the 35th Anniversary, so should be available from all good bookshops. And Amazon.



Forth programmers: how did I do, did I get anything wrong?

Non-Forth programmers: did any of that make sense?

Non programmers: don't worry, there will be more pictures of bits of computers next week.


Advertisements

Minstrel 4D

The Minstrel 4D kits are shipping now, you can order one from The Future Was 8 bit

https://www.thefuturewas8bit.com/minstrel4d.html

More info in a previous post:

http://blog.tynemouthsoftware.co.uk/2022/08/minstrel-4d-overview.html


Z80 Kits

The YM2149 Sound Card and RC2014 Digital IO Module are available from Z80Kits.com

https://z80kits.com/shop/ym2149-sound-card/

https://z80kits.com/shop/digital-i-o-module/


Patreon

You can support me via Patreon, and get access to advance previews and behind the scenes updates. These are often in more detail than I can fit in here, this post contains bits from three Patreon posts, and there is a forth and fifth to follow. This also includes access to my Patreon only Discord server for even more regular updates.

https://www.patreon.com/tynemouthsoftware

Sunday, 8 January 2023

Testing the AY-3-8910 / YM2149 Sound Card for RC2014 (and Minstrel 4D)

In a previous post, I built an AY-3-8910 / YM2149 Sound Card for RC2014 (and Minstrel 4D)

In that post, I tested the card using the game Valkyr, which has built in support for a similar device, and has been patched to use this card.

Today, I want to try a few more things, and also look at some of the other supported chips and some of the potential problems associated with sourcing them.

Play

George Beckett has a repository of Forth programs which support this card (including the aforementioned patched version of Valkyr). https://github.com/markgbeckett/jupiter_ace

One of those is "play", which can be used to play a series of notes, given by their letters (c, d, e, etc.) with # and & for sharps and flats.

A special version has been provided for the Minstrel 4D. Unlike the original, this has Forth and assmembly in a single dictionary ("playd"). This can be loaded from the SD card menu on the Minstrel 4D. Note here the menu scans the file for a dictionary and identifies it is called "play", even though the file is called playd.TAP.

Select option 2 to load it (we don't want to run "play" directly, so we don't use the first option).

I am still pleased with the loading lines on the Minstrel 4D. For those not in the know, the Jupiter Ace didn't do that, you just has to sit and wait and hope the program loaded.

Once that is loaded, you can use it to create words that will play tunes (see the notes on George's page for more info).

Typing over serial is really useful for this sort of thing, you can just select the text from github and paste it into a terminal window and have it typed into the 4D automatically. You can then save if to SD if you wish to enjoy Hall of the Mountain King at a later date. Or you can add it to your own program and have it playing over the title screen (see notes about relocatable code if you do).

Audio Output

These chips are three channel, mixed down to two for a stereo output. The YM2149 seems to suit the mixer on the board better, compare the tune from the start of Valkyr.

On the AY-3-8192, there is more bleed through.

You can also see that during the gameplay. There look to be three types of sounds, one on each channel, you can see that more clearly on the Yamaha chips.

The laser firing noises are on channel 3, which is mixed equally to left and right, and different ship and explosion noises are on channel 1 and 2 (left and right).

On the AY-3 chip it seems a bit more jumbled up.

Either way, it enhances the game play.

Sourcing chips

This card supports a variety of different, but largely compatible chips. Unfortunately, none of those are still in production, so you have to rely on the likes of ebay, and as I am sure you are aware, all may not be as it seems.

Here I initially assembled four chips to test. These are as follows:

  • GI AY-3-8910A, datecode 8547
  • Yamaha YM2149F, datecode 8903
  • Microchip AY-3-8910, datecode 0827
  • Microchip AY-3-8910A, datecode 1030

I have had the GI and Yamaha chips for a long time, and I have no reason to think that they are not genuine chips.

The "Microchip" ones, less so, these are from ebay. I have not been able to find a date when they stopped making these, but I suspect they are both dated a bit late. I'm not entirely sure if Microchip only produced their improved AY-3-8930 / AY8930, or if they did actually produce Microchip branded AY-3-8910 and AY-3-8910A chips.

Just based on the packaging, with the two release marks, the dot for pin 1 and no notch, I think the Microchip AY-3-8910 dated 0827 is in fact a Yamaha YM2149F. It has gone through an unfortunate process where it has been sanded down, painted black and laser etched to look like a Microchip device.

This is confirmed by the first test, which is the output level. The original Yamaha device outputs sit at about 2V at rest, and the 0827 Microchip matches that.

The other Microchip chip looks and acts like the AY3-8910, with a notch for pin 1, and there are two release marks and a dot for pin 1 which are still just visible, the result of the chip being sanded down.

Comparing it to the original GI chip and a contemporary Microchip PIC, it is clear the font and logo are wrong, but the release holes don't quite match either of the other chips. It does however have a low output, around the 0.2V expected from the GI chips. So it looks like the 1030 Microchip is actually an AY-3-8910A, just remarked with a later date.


Register test

There is another way to tell. Some of the control registers are not fully used. Register 0 and 1 together provide a 12 bit tone period. Register 0 uses all 8 bit, but register 1 uses only 4 bits. In theory, the GI chips will always return 0 for the unused bits, but the Yamaha chips will store and return all the bits written, even though they are not being used.

In this test program, I am selecting a register, writing 0 and reading back and displaying the value, then writing 255 and reading and displaying that.

First off, select register 0, write 0, read 0. Good start.

Write 255, read 255, as expected.

OK, so that is working.

Now it is time for register 1 (which is only 4 bits).

Select register 1, write 0 and read 0, so far so good.

Write 255 and read 15.

Ah, so that would suggest this is an AY chip, as it has read 0 for the upper 4 bits, and the value written for the lower bits.

I did have a long section here about how the unusual interface to these sound chips works, but this post is already way too long and I am barely half way through, so I will move that to a separate post -

http://blog.tynemouthsoftware.co.uk/2023/01/driving-ay-3-8910-ym2149-from-8-bit.html

Briefly then, most of the other chips used in the 8 bit era were designed for the 65 or 68 series chips or the 8080/Z80, but these were designed to be used with the more obscure GI CP1600 and early GI  PIC microcontrollers (before the Microchip takeover of GI, and before the Atmel takeover of Microchip).

These had a shared address and data bus, hence the odd way you have to select the register then write the data. On the CPC464 and Oric, the chip is connected to the outputs of an IO chip, rather than direct to the databus, to be able to drive this. On the Spectrum +2, it is driven from a latch forming a simple output port. On this RC2014 card, it is being driven direct, so there are a few oddities, like reading data back via the register port.

OK, time to test the other three chips. I expect to get 255 for both registers.

Well, that is what I expected, unfortunately, I also got 15 for both the (theoretically) Yamaha chips.

Not sure what is going on there.

At this point, I was doing a lot of powering on and off (soft power on is great, isn't it?), and swapping chips, so I had fitted a ZIF socket to the board to make that easier.

I was making changes to the code in a text editor and pasting the text over serial to the Minstrel 4D. When doing that a lot, it is useful to disable the menu at power on, so it boots straight into Forth, ready to type in the code. Go to settings, and press A to disable it.

You can still get to the menu by pressing the Menu button, and then go to settings and turn it back on by pressing A again when you're finished.

I reached out to Ed Brindley, who designed the RC2014 card, and he also expected to see different results from the Yamaha chips. However, he also ran some tests and seem to confirm the results I was getting, 15 way being read back, from all the chips.

He was testing all registers, so I wrote some more forth to test all 16 registers. ("YM2149" and "Real" are printed at the start to make it easier to know which results are which, I changed that to match the chip being tested).

216 CONSTANT REGPORT
208 CONSTANT DATPORT

: REGS
  CLS
  ." YM2149" CR
  ." Real" CR
  CR

  16 0
  DO
    I .
    I 10 < IF ."  " THEN
    I REGPORT OUT
    255 DATPORT OUT
    I REGPORT OUT
    REGPORT IN .
    CR
  LOOP
  CR
;       

(is there any interest in going into a bit more detail of the Forth used here, line by line, for those new to the language?)

All these test programs on here are available on my github - https://github.com/tynemouthsoftware/Forth.

I ran this with a real YM2149 and the results were as before, only the allocated bits were returned, the rest were 0.

I repeated that on each of the types of chips I had, and the results were all exactly the same on every chip.

I also added an AY-3-8192 to that test, using the adapter board that came with the kit from Z80kits.com.

Now it is of course possible that all my chips are actually AY-3-8910, some of which have been remarked as YM2149, but that seems unlikely.

More chips to test

To be sure, I dug around in my spare boards to find some more chips.

First up, another AY-3-8912A, this one from a dead ZX Spectrum +2.

I found a couple of scrap boards with YM2149 chips on, one from an Atari ST.

And one from an MSX.

Do you spot anything about the MSX board?

Yes, the position is marked for an 8910, but the Yamaha YM2149 is fitted instead. It also has the full part number on the back.

The MSX board had already been raided for parts, so I desoldered the YM2149 from there.

Both of these chips were factory soldered to boards in the 1980s, so I have no reason to think that they are not real chips.

OK, as expected, the AY-3-8192 returns 15 for register 1, only the used bits.

Oh, so does the YM2149.

OK, so that seems to confirm that this "reading back extra bits" thing doesn't work.

I fed this back to Ed, who did a bit more digging, and it turns out that the behaviour is different if you write 31 rather than 255 to the port.

So I tried writing 31 instead, and got 31 back.

I didn't take a screenshot, I just updated my test program and ran that and it worked!

I ran the same test on an AY-3- 8910 and that was also correctly detected.

So the information about the extra bits was correct, just with a caveat. If you write 31 to register 1, you will get 31 back on a Yamaha YM2149, and 15 back on an AY-3-8910/2. All other bits and values work the same on both devices. Excellent, we have a test program.

216 CONSTANT REGPORT
208 CONSTANT DATPORT

: DETECT
  CLS
  1 REGPORT OUT
  31 DATPORT OUT
  REGPORT IN 31 < 
  IF ." AY-3-8910/2"
  ELSE ." YM2149" 
  THEN
  ."  Detected"
  CR
  CR
;       

Conclusion

After all that, and testing many different chips, I can confirm that at least with these particular chips, all the chips with the notch on the end were AY-3-8910, and the ones without the notch were all YM2149, despite what the marking say.

Yamaha YM2149 chips

I believe all three of these are Yamaha YM2149 chips.

  • The package has a round depression for pin 1, and no notch on the end
  • The audio outputs have around 2V offset voltage
  • When you write 31 to register 1, you read 31 back

It seems both of these chips started out life looking pretty similar. You can sort of see why they would do the remarking. The old, scratched, chip with the less popular part number, and older date code is far less likely to sell than the one that has been sanded, painted, and laser etched with a largely compatible, more popular part number, and a more recent date code.

Whoever is doing this, please stop it.

AY-3-8910/2 chips

I believe all of these are GI AY-3-8910 and 8912 chips.

  • The package has a round depression for pin 1, and a notch on the end
  • The audio outputs have around 0V offset voltage
  • When you write 31 to register 1, you read 15 back

It is not clear if the two chips remarked as Microchip were originally Microchip, or were old GI chips like the rest.

You can just see the remains of the pin 1 dot in the right light. Not much left after the original top later has been sanded down.

If you don't believe me about the sanding, see for example this AT28C64B chip where not all of the original top was sanded off before it was remarked.

See this video for much more information about the remarking of chips: https://www.youtube.com/watch?v=k72SFBOZ_lw


Conclusion to the conclusion

So, after all of that, which chip am I leaving in the board?

Well, the Yamaha YM2149 seems to sound better to me. I am using the old scratch one, I just gave it a bit of a clean up with some Meguiar's Plast-RX, which is good at getting out light surface scratches, and it turned out reasonably presentable.

I think that is all I have to say on this for the moment, that turned out to be a lot more than expected, I hope you found it interesting.

Thanks to George Beckett and Ed Brindley for their help with this post


Advertisements

Minstrel 4D

The Minstrel 4D kits are shipping now, you can order one from The Future Was 8 bit

https://www.thefuturewas8bit.com/minstrel4d.html

More info in a previous post:

http://blog.tynemouthsoftware.co.uk/2022/08/minstrel-4d-overview.html


Z80 Kits

The YM2149 Sound Card is available from Z80Kits.com

https://z80kits.com/shop/ym2149-sound-card/


Patreon

You can support me via Patreon, and get access to advance previews and behind the scenes updates. These are often in more detail than I can fit in here, this post contains bits from three Patreon posts, and there is a forth and fifth to follow. This also includes access to my Patreon only Discord server for even more regular updates.

https://www.patreon.com/tynemouthsoftware