I’ve talked about the aftermarket expansion modules I reverse engineered for the Sorcerer, but what about something first party? I was going to wait because I haven’t been able to get it to work completely, but that may be the fault of the sorcerer I have, not the reproduction. Today I bring you a replica PCB of the ROM PAC.
Now hold on, some of you have realized something silly about these, but we’ll get there I promise. These are the ROM expansion cartridges for the Exidy sorcerer. By 1981 they released six first party ones from what I could find.
- EPROM PAC (DP 2001)
- for making your own PACs, this was essentially a ‘blank’ one with a label so you could write what you put on it (board was set for 2716s, not the PROMs in the others)
- Standard Basic (DP 2002)
- I can find a couple ROMs of these out there, I think at least one is a homebrew fix, but there may have been two OEM releases
- Wordprocessor (DP 2004)
- This is referred to as an early version of Spellbinder, how apt
- Development PAC (DP 2003)
- Z80 development tools for assembly programming
- Auto Program Load Pac (DP 2005)
- Automatically loads and initializes CP/M operating system on Sorcerer power up or reset. Used for Display/ disk or Floppy disk (DP 6300 or DP 6400).
- Terminal Pac (DP 2006)
- Transforms the Sorcerer keyboard computer into a glass teletype unit. This turnkey communications software powers-on with a menu of selections including parity, stop bits, 300/1200 baud and full or half duplex
Those are just the first party ones that I could find, there’s also the:
- Software Source Disk Pac
- automatically boots your disk when you press RESET or switch on your Sorcerer
- IOPac
- adds ROM, RAM, and parallel memory mapped IO
- The Ultimate Pac
- a battery backed RAM cartridge with selectable write line to make it act like ROM (or partially like ROM)
- RAM PAC
- just a regular PAC fitted and jumpered for the 6116 SRAM chips and with the write line hooked up to an unused pin, this should be able to get you 56K of contiguous memory which is good for CP/M.
I have a sorcerer 1 and an old revision BASIC pac, so let’s take a look.
This is the inside of my (non-functional) basic cartridge. It is now time to talk about that shell. You can see inside and it’s very obvious that the shell is made from an 8-track cartridge. They even snapped off and melted some of the internal plastic to make the PCB fit. Another detail is that the cart is inserted into the machine component side down. This is important if you want to try things out before reassembling. Now why was my cart not working? I had a bad ROM chip. I removed and tested all the ROMs in the cart against the ROM images floating around on the internet and I found that I had a bad EXSB1-2. To determine this I had to ‘build’ an adapter to dump the ROMs I had.
I plugged it into a standard chip tester, but into a socket so I could lean one leg out and have it not be in the chip tester. I then clamped a 24 pin chip clip on top and wired that pin to ground like the pac pcb did for reading. This is because the mask ROMs in the cart are almost but not exactly a 2716 pinout, the difference is pin 21 on the AM9218 is a programmable chip select and Exidy programmed it to require it to be grounded. On the 2716 that pin is Vpp and should have 5v on it while being read.
My fix there is to pull that pin out of the socket and solder it directly to 5v on pin 24. The board, as you can see, has jumpers you can cut in order to set the pac for different types of ROMs. I’ll obviously keep that in my replica, but what do all those jumpers do? This gets complicated. Let’s look at the schematic one section at a time.
The sorcerer is unique in that it combines address line A15 with the read line meaning that it is high only if:
- there is a memory request
- the request is a read
- it is not a memory refresh cycle
- a15 is high (it is in the top 32k of memory)
One of these interferes with the features of some of the pacs described above, but we’ll get to that later. How does this get implemented in the cartridge?
lots of cuttable jumpers. We have 7 jumper blocks and I’ll go through them out of order and one at a time.
Jumper block 7 determines which lines must be high to enable the ‘LS138 decoder. You have a choice of any combination of: A15, A14, and A12. A15 is already required to be high, but for the other two you can pick. You can even pick to have only A15 being high as needed. The configurations are:
- ROMRD15 and ROMRD15
- enabled for all regions $8000 to $FFFF
- ROMRD15 and A14
- enabled only for the upper $C000 to $FFFF
- A14 and A12
- enabled for two non-contiguous regions, $D000 to $DFFF and $F000 to $FFFF but only the first one can be read from the cart slot
- ROMRD15 and A12
- enabled for four non-contiguous regions, $9000 to $9FFF, $B000 to $BFFF, $D000 to $DFFF and $F000 to $FFFF but this would put the next decoding section into nonsensical areas so I don’t believe it was ever intended to be used
Jumper block 6 determines how the regions enabled by block 7 are split up. You have available A10, A11, A12, A13, and A14 to pick how finely to divide up the larger region. This all gets very complicated very fast. The ‘LS138 uses 3 of those address lines, in whatever order you want, to decode to 8 evenly spaced regions of memory. Of those regions of memory two are fixed to two specific ROM locations (U3 and U4) and two can be remapped from banks 0 and 1 to banks 4 and 5. Through these combinations you can choose how broken up the memory map gets and which ROM each region enables. The overarching truths are these:
- If you use 1K ROMs, you need to use A10 somewhere so it splits into 1K chunks and the different ROMs end up contiguous
- If you use 2K ROMs you need to use A11 somewhere but not A10 so it splits into 2K chunks
- If you use 4K ROMs you need to use A12 somewhere but not A11 or A10, same reason but 4K
Beyond that where you place the address lines determine what order the memory map is decoded, so to which pins the chip enable signals are sent, As mentioned above only 6 of the 8 signals on the ‘138 go anywhere we can use and of those two are mutually exclusive with the others and the remaining two are fixed. To decide where we actually want these ROMs mapped we need to look at the overall memory map of the sorcerer
This is from the sorcerer 2 technical manual, as a note the sorcerer 1 could be equipped with as little as 8K of internal RAM and that would obviously be from $0000 to $2000 as you could extrapolate from this picture. From this picture you can also see three different sizes of ROM PAC: 4K, 8K, and 16K. So for our PAC we will want to decode ROMs to fill those sections and only those sections to be used as different sizes of PAC.
- for a 4K PAC we want to fill from $D000 to $DFFF only, and that can be done in three ways
- 4 1K ROMs at $D000, $D400, $D800, and $DC00
- 2 2K ROMS at $D000, and $D800
- 1 4K ROM at $D000
- for an 8K PAC we want to fill from $C000 to $DFFF
- we only have 4 spots for ROMs so this cannot be done with 1K ROMs without doing some DIY stuff that’s beyond the scope of figuring out how to strap these jumpers
- 4 2K ROMs at $C000, $C800, $D000, and $D800
- 2 4K ROMs at $C000, and $D000
- 1 8K ROM is not possible with simple jumpers because the only 24 pin ROM I know that is 8K is the 2364 and that would need A12 on pin 21 and A12 is not brought out to these jumper blocks
- for a 16K PAC we need to fill all the way from $A000 to $DFFF
- The only way we have to do that with this board is with 4 4K ROMs at $A000, $B000, $C000, and $D000 for the reasons mentioned above
You can make other configurations that are non-contiguous and that use different sections for different things (like the IOPac above) but for this board, I think these are the intended configurations. But why is it so important to only map the ROMs we intend to use?
Well, here we have some AND gates that are acting like inverted logic OR gates. That means if either input is low, then the output is low. This generates a signal called ROMPRE or ‘ROM Present’. What this does is it tells the sorcerer that there is a ROM there and to turn off accesses to anything else that may be in that section of memory. This sort of works like the PHANTOM line in the s100 world which lets two things exist at the same location, but you decide which one to access and then disable the enable signal to the other. In a normal sorcerer memory map there is nothing in the same section of memory as a 4K or 8K PAC, but the 16K PAC overlaps the top RAM if you have a 48K system. If you have something that expands the sorcerer on the rear 50 pin connector like the S100 chassis then anything could be in that section of memory and it needs to be disabled when the 8K or even the 4K PAC is installed.
I mentioned it breifly above, but two of those decoding signals are not cut-and-jumperable which means they will always disable other chunks of memory when they are activated. This means certain jumper configurations of the PAC can screw with other areas of memory that we may not be intending to take over. To decode all the many possibilities Frank helped write me a python script to generate mappings for different configurations. It has the following things to try:
- All combinations of block 7
- All combinations of block 6
- whether to set block 5 to the a, b, or neither set of jumpers
The output of that script helped make this mappings file that details which memory blocks decode to which ROMs
This helped determine the configurations for the PAC that are valid. Obviously with some tinkering you can make it do different things, you could even add chips to make some areas RAM and some areas ROM. We should talk about that, but first I can show you how I made my boards a lot more user friendly.
You can see that I labeled each pad and also indicate which pads were originally jumpered for the PROMs in the standard PACs with silkscreen (not in the copper layer though, I didn’t want you to have to cut anything, just solder).
And on the back, oh boy. It’s free real estate as they say. I have what I consider the master configurations of all the variants you could want for memory mappings and ROM sizes. I also indicated the pinouts of the most common ROM and RAM chips so you know how to set the individual jumpers for pins 18-21 on each chip. I keep saying RAM though, and above we saw a schematic that showed the ROMRD15 signal has the memory request and the read signal both mixed into it so it will not go high during a write. Well, that was true for the sorcerer 1…
The sorcerer 2 leaves out that signal. This means that if there is a write to a memory address in the PAC region, the chip enable signal is also set. That write signal has to reach the cartridge though, so how does that happen?
The sorcerer 2 took an unused pin and added write to it. Why didn’t they hook it up all the time? Well, look at my OEM cartridge and see how that one pin is massive? That’s ground, and it connects both pins 28 and 30. Look at the same spot on my cart and you will see I put a cuttable jumper there (actually it’s not connected in the copper). I did that so that when my cart is inserted into a sorcerer that has pin 28 hooked to the write pin it doesn’t try to ground that signal. The OEM cart didn’t do that and people were instructed to take their original carts apart and carve a groove in the copper there to separate the pins so it doesn’t cause issues. I’m not one for hacking up original carts so I made a general purpose solution to protect your RAM modded sorcerers from rampaging original ’70s carts.
The sorcerer extension cart. Or, the ROM PAC to ribbon adapter. Or just the Cart condom. This does three things, it extends the cart slot outside of the sorcerer case, it provides you a place to stick a ribbon cable to a breadboard and develop your own carts for the sorcerer (or just read some bus signals), and it disconnects the write pin so you can plug in old carts without any concern for damaging anything. New sorcerer owners may not even know that they have one that has this potential issue and with this you just have an abundance of caution. If it wasn’t obvious to make this work you solder a cart slot on the left side of the board, but edge mounted. Another way you could solve this is to put a schottky diode on the write line so that the write line can only pull down, but if it’s not writing and tries to assert that pin as high then it’s blocked by the diode and a pull up resistor is on the other side. In that case if you plug in a cart that grounds that pin you are simply wiring that pull up across 5v to ground the entire time the cart is inserted. It’s not ideal, but it may work. The problem is at around 3Mhz is the diode fast enough and will it give a crisp enough edge to work reliably. I dunno, so following all period mods I just made the add-on board.
So how are you supposed to jumper the PAC for RAM if the write line isn’t at any of those pads for configuring each chip? well ,this is how.
the write pins, as indicated on the silkscreen on the back of the board for 6116s are connected to the write line on the cart connector. I’ve actually run into a conundrum here. The sorcerer 2 enables the ROM PACs whenever a memory request is being made to or from the top 32K of memory. If there is a ROM PAC installed, it disables any other hardware that might respond to that request in the regions it is present. However, what happens if you have ROM there and not RAM and the sorcerer tries to write to that area of memory?
Here I have the truth table for a 6116. Based on my jumpers outlined in the silkscreen /OE is always low so we can ignore line 3. When the chip is not selected, it disconnects itself from the bus, that’s the top line. When the chip is selected and the write line is asserted, the chip is in write mode and takes in the bits on the bus. When the chip is selected and not in write mode it is outputting bits onto the bus. That’s fine for a chip that is aware of the write signal, but what about a 2716 that is not aware that it’s a write that’s trying to happen?
Based on the jumpers indicated for the 2716 we have 3 available modes, and that’s really 2 because read and verify are the same thing for us. So we are either outputting data on the bus, or we are disconnected from the bus. A regular ROM PAC in a factory sorcerer 2 or a sorcerer 1 modded for write capability seems to have bus contention if you try to write to a section of memory and a ROM PAC is installed. But contention with what?
Here is the CPU side of the data bus during that contention event. The data bus is buffered through an 8304 tri state bus transceiver. I think of this as a late ’70s 74LS245, as you notice they are using ‘241s everywhere on this schematic, but no ‘244s or ‘245s. That chip is hooked up so the lower current capable side is out towards the bus which seems backwards, but maybe that’s to prevent the harm it could cause if there’s some serious contention. What sort of specs does it have?
We have in the datasheet that it will sag the voltage to as low as 2.7v on the output if it tries to output a logical 1 and has to drive as little as 3mA, but the converse shows that it can sink 16mA of current and it’s only a max of 0.5v off of ground. I read this as the chip is good at pulling down, but sucks at pulling up. That’s pretty standard in the TTL days, so when there is bus contention the chip outputting a logical “0” will inevitably win and the other chip won’t be too bothered by getting pulled down. It’s kinda like everything is open collector with a weak pull up, but all that is inside the chip. Now we know what will happen if there is contention, do we know it will happen? I know the sorcerer’s monitor ROMs can detect how much RAM you have installed at boot, so it must be checking that somehow.
Here’s the ram check code. In summary, it starts at 0 (you’ll have to trust me that “RAM” was initialized to zero up above) reads what’s there in to A and B, inverts A, writes A back to the same memory location it came from, checks if it is the inverted or the original version, then puts it back from B (how it was originally). Once it has done that it checks to see if the inverted write took, and if it did it knows RAM is there and increments to the next location and tries again. If it hits a point where it writes something and when it reads it back that’s not the same then it knows it tried to write to ROM and it stops, backs off two locations and says that is the location of the top of RAM. In this case a factory sorcerer 2 with 48K of memory and an 8K ROM PAC (like standard basic) inserted will have two instances of bus contention every power up. I didn’t think this would be how it works, but I guess it is. Now, how can we fix it?
I don’t think we do. I mean, you could modify your cartridges that are ROM only to have a separate gate in there to detect a write and disable any ROM chips. That would mean modifying every cart that might be plugged in to your sorcerer though.
I know I could 3d print cases for these, but with innumerable 8-tracks being broken, or super common commercial releases not worth saving I decided to do some recycling and get a bunch from estate sales to make into shells. I happened to find the exact mold the sorcerer carts are meant to fit in.
If you ever want perfect replacements for sorcerer PAC shells, get yourself some AudioMagnetics Tracs 90 carts and it’ll be the right mold. Just clip or melt out the right sections and you’re good to go. You can find my ROM PAC board design up on github here, and my protection/experimenter PCB is also up in the same repo. I hope to get around to documenting more of the standard recommended mods to a sorcerer 1 soon because that will give me a single place I can refer to remember what I did. This is a fun machine where in order to make it work as intended you need to cut and jumper a bunch of stuff.