When Timex released its Technical Manual for the TS2068, we learned how to add memory to and switch between its three internal memory banks. This was wonderful stuff, and it's given us many excellent TS2068 products long after the "profane world" thought that the Timex computer was dead. Still, if we think back, we may remember that Timex originally promised 256 banks. In addition to the Home, Dock, and EXROM banks (which we we'll call the "standard banks"), there would also have been the provision for special enhancements that we'll refer to as "expanslon banks". In this series, we will look at how those extra banks would have worked. Because the subject is very complex, we can't take up too much space with descriptions of the standard banks. That information is available elsewhere, and even without including it here, I fear that the volume of our discussions will try the patience of our dear editor. We hope you'll understand our plight.
The TS2068 Technical Manual is one very good source of information on the standard banks, and it might be a good idea to browse what it has to say on the subject. Look at pages 37-39, and 115-120, in particular. [Note: page numbers quoted in this article correspond to the original Technical Manual offered by Timex (blue cover). This same information can be found in the new second edition manual published by Time Designs, only the information is generally a few pages earlier than those quoted.] Throughout the manual, it almost appears that the good folks at Timex tried to delete all references to the "lost 253" expansion banks. If so, they weren't entirely successful. Some pages containing copies of their internal documentation give us important clues. Both ROMs also contain code that was once intended to control these banks. Dissecting them gives a fairly clear picture of the full bank switching.
With a litt1e "digital detective" work, we'll see that the Timex engineers planned a LOT more for bank switching than just extra memory. We'll also be able to see some of the serious problems (and clever/bizzare solutions) that graced their workbenches. Lastly, we'll see signs that they were forced to put the TS2068 into production long before it was ready.
As a result of this, our TS2068s contain large blocks of code that absolutely have not been debugged. This may well be the reason that its ROMs (and ONLY its ROMs) are socketed. Before the additional bank switching could really have worked as intended, those ROMs would have had to be replaced. This shouldn't be overly discouraging. On page 20 of the Technical Manual, we are presented with a "surgical procedure" that would allow us to replace the ROMs with EPROMs. It was quite considerate of them to include this tidbit. Perhaps they thought someone would want to debug the dormant power lying inside.
In this series, we'll use flowcharts, tables, and descriptions, to "walk through" the extended bankswitching code. Once we stop trying to figure out what it does, and instead try to understand what it SHOULD do, it's really not too hard to follow. From this, we'll also understand how the hardware of the expansion banks and Timex's unreleased Bus Expansion Unit (BEU) would have worked.
Please understand that this is a report on my own study and analysis of the Timex ROM code, and forms a self-consistent explanation of how the bank switching hardware and that code would have worked together. It is NOT a construction project. Still, it should be possible to design an expansion bank system with the information we'll be studying here. If some enterprising readers wish to correct the ROM bugs and build the necessary circuitry, I'll assist in any way I can. I give no guarantee that I've found all the bugs, but by the end, you should also have a good enough understanding of the subject to find further bugs on your own. All in all, this could be an interesting "team project".
As you may have guessed, it will be absolutely essential that you have a TS2068 Technical Manual handy as we go through this series. Coincidentally, this very magazine can sell you a copy for just twenty-five bucks. If you've read this far, you're probably the type who'd find it useful anyway, so send 'em the money. You won't just be helping them, you'll be helping yourself. [Editor's Note: Thanks for the great plug Wes... but please tell the good folks that I didn't put you up to this!]
You'll also need some sort of disassembler. We just can't provide complete listings of the ROM code here, but we'll give TONS of memory addresses, so you can look for yourself. Ray Kingsley's excellent HOT-Z-AROS will let you look directly into the EXROM memory, which would be he1pful. But if you have another version of HOT-Z, or another disassembler altogether, never fear. We need only copy the EXROM code onto a cassette, LOAO it back into some convenient RAM location, and then disassemble it. If we are clever about where we LOAD it, the difference in memory addresses wi11 be no prob1em at all.
Most schemes for putting the EXROM on cassette involve a
lot of convoluted bank switching and code moving in machine
code. But we're going to be a little lazy, and do it the easy
way. Perhaps the best kept EXROM secret is that you can do the
entire job in a sing1e line of BASIC! Just type:
To disassemble this, you'll want to LOAO it back into RAM. If the memory addresses will be displayed in hexadecimal, then first CLEAR 32767 and then LOAD "EXROM" CODE 32768. This is location 8000 hex, so you need only subtract 8 from the most significant digit to get the true EXROM address. If you plan to disassemble in decimal, then LOAD "EXROM" CODE 4000 and just drop the 4 from the most significant digit, Note (for this second case) that if your disassembler is located below location 48192 in memory, it will overlap the code. You may want to work out some similar tricks of your own to please your particular software.
Flowchart #1 is the top level intialization routine in the EXROM. This part of the intialization was to have done all the "set up work" to find, sort out, and initialize any extra banks (RAM or ROM) that may have been added. We'll be discussing this flowchart in detail next time, but it's included here for three reasons. First, it will let the truly enthusiastic do a little extra work on their own. Second, it will help prevent later installments from getting too bogged down in flowcharts. Third and most important. it will give everyone a bit of time to practice on and get used to the notation we'll be using.
Note that each flowchart box contains the memory address of the code it represents. But the very idea of bank switching means that more than one bank of memory will be sharing the same addresses, which just begs to cause confusion. In this series, all addresses will be given in hexadecimal, but EXROM addresses will be proceeded by the letter X. As such, we can say that the NEW routine, which starts to intitialize the system variables is located at 0D1D (or 0D1D in the Home ROM) but that the routine that finishes intializing the system variables is at X096C (or 096C in the EXROM). This will save a lot of verbage, and is handy, once you get used to it.
In addition to this memory address notation. we'll also examine special shorthand ways to talk about things we'll call Bank Switching Registers and SYSCON Table Entries (we'll get around to defining these eventually). These notations have been carefully selected so as to be completely un-ambiguous, but they may require some getting used to. Also, although some users have a strong dislike for hexadecimal numbers, we hope you'll understand that they're needed here. We use numbers in a computer both for quantities and to denote various binary bit patterns. Decimal is fine for showing a quantity. But it's pretty darn hard, for example, to tell if bit 4 is set or reset in decimal 239. If we see it as hexadecimal EF, however, the experienced user can immediately see that bit 4 is zero. Since the bank switching makes considerable use of bit patterns to control different hunks of hardware, hexadecimal is the only way to go.
The Z-8O Microprocessor, around which our TS2068s are based, can only address 65536 (Horrors! A DECIMAL number!) bytes of memory. This is fixed in its hardware, and it's simply not negottable. If we want it to control more memory than this, then some of the memory will have to share that "address space" in a game that's a bit like a telephone party line system. While the proposed 256 memory banks would theoretically allow control of some 16 MILLION bytes, only 65536 of them could ever be immediately available. The rest would be disconnected in a way, and waiting patiently for the Z-8O to "call them up", switch them in, and talk to them.
The TS2068 memory is broken up into 8 "chunks" of 8K apiece. They are laid out as follows:
Address Chunk # 0000-lFFF 0 2000-3FFF 1 4000-5FFF 2 6000-7FFF 3 B000-9FFF 4 A000-BFFF 5 C000-DFFF 6 E000-FFFF 7
These 8 chunks might be analogous to 8 "party line" inside the 2068, each with up to 256 subscribers. Anyone of the banks (subscribers) could be using a particular line, but only 8 lines are available at a given time. Each memory bank has to have its own identifier (phone number) and it also has to have a way to know which, if any, of its chunks are presently able to be addressed by the Z-80. This is done in what's called a Horizontal Select Register. This register contains 8 bits; one for each chunk in its memory bank. The contents of each bit tells wether its corresponding chunk is enabled (available to the Z-80). Bit 0 tells about chunk 0, bit 1 about chunk 1, and so on. If you've made sense of this, you may wonder what would happen if two banks both have the same chunk enabled at the same time. The result would be conflict, and you'd have trouble. But if you go about it properly, you can see to it that this never happens.
Now, in order to keep the TS2068's cost in line with its competition the Timex engineers put a lot of its circuitry inside a semicustom integrated circuit called the SCLD. This is a "gate array" type circuit, distantly related to the programmable arrays available to home experimenters. This type of array is programmed at the factory, however, and tends to be far more versatile and contains far more gates than the kind we might be more familiar with. The gates may be used simply as gates, or arranged into randomly ordered flip flops, allowing reasonably sophisticated functions. A disadvantage is that there is still only a fixed number of gates and signal pins to work with, and the designers may have to make some weird compromises in order to get all the functions they want out of the chip.
This fact comes back again and again to haunt the bank switching scheme.
0ne such example may be found in the single Horizontal Select Register used to control the three standard banks. This register is accessed through I/O port F4. Although it would have seemed more reasonable to give each standard bank its own register, as is done with the expansion banks, this would have eaten up too many gates. Thus, through a "wild and whacky wisp of whimsey", the Timex engineers found a way to make do with only one; it works like this.
If a particular bit of the register is zero, then the corresponding chunk of the Home R0M is enabled. If the bit contains a one, then the corresponding chunk of either the Dock or the EXR0M bank is enabled. Which one it is depends on bit 7 of I/O port FF, which, I suspect, just happened to be left over, with nothing important to do. If this bit contains a one, then it's the EXROM bank; otherwise the Dock bank applies. As we said, we can't really take too much space to discuss this. The capsule description given here is just included for completeness. A more complete description for switching the three standard banks may be found in the TS2068 Tech Manual.
Leaving the "nuts and bolts" of the standard banks behind, we should still examine some of it's consequences. The one of most immediate importance is that this scheme prevents your having chunks from both the Dock and EXR0M banks enabled at the same time. 0rdinarily, this wouldn't have been important. The EXR0M bank was only intented to "catch the overflow" of the code that couldn't have fit in the Home R0M. The Dock bank was intended only for cartridge based software. If any other banks were needed, well, there was space for 253 more, right? Unfortunately, those banks never became available, and ingenious TS2068 users have had to use these three as best as possible.
But this minor perversion has its problems; it's important that you exercise care in trying to access the EXR0M while running in the Dock bank. But there are even more subtle ways that this little foible can trip you up. In articles I've written on running RAM in the Dock bank, I've always cautioned the readers not to try to L0AD anything directly into the Dock bank. The proper procedure is to L0A0 the code into the Home Bank and then transfer it yourself. I've always shyed away from explaining exactly why this is so, but having gone through this long explanation, it now can be told! The fact is, the L0AD routine is in the EXR0M, and so while your're LOADing, none of the Dock chunks can be enabled. (Remember, you can't have EXR0M and DocK chunKs enabled at the same time.)
As such, any attempt to LOAD data into the Dock bank will instead cause the TS2068 to try to L0AD the data into the EXROM bank, where there's no RAM to be had. Furthermore, you can't put RAM into the EXROM bank without messing with your TS2068's innards. The 8K ROM in that bank is mapped into all 8 chunks of that bank, due to incomplete address decoding. You just can't win.
The odd use of one Horizontal Select Register to control three banks has another consequence. The Home bank always "assumes" it's enabled, unless told that the Dock or EXROM have a particular chunk. This ordinarily leaves no way for the other 253 banks to be enabled without conflicting with the Home Bank. This is dealt with in a "cheap and dirty" manner with a signal on the TS2068 rear connector, called /BR. When this signal is low however, all internal memory is disabled, no matter what the Horizontal Select Register for the standard banks says. This would allow the additional expansion banks to "muscle their way in" when it's their turn to "talk".
The TS2068 appears designed to contain almost no circuitry that would support the expansion bank switching. That would be contained almost entirely in the never-released (and possibly never built) BEU, and the expansion banks themselves. An early map of TS2068 I/O port assignments shows ports FC and FD reserved for bank switching. For good reasons, to be discussed later, this is not the way it's turned out. These ports are never used in either ROMs, and communication with the bank switching circuitry is instead done through a memory mapped register scheme.
Four Bank Switching Registers are used. We will call them registers CO, AO, 80, and 40. These are the ways that the bank switching software refers to them, so it will make it easier to follow. Also, it's useful to retain the second digit even though it's always zero. This will prevent registers AO and CO from being confused with the A and C hardware registers inside the Z-8O itself. When we write a value to these registers, we are sending bank switching information to the (presently nonexistent) BEU and expansion banks. However, when we read the registers we get back different information relating to the status of various banks. WE DO NOT GET 8ACK THE SAME INFORMATION WE SENT. Furthermore, although we send out 8-bit groups of information, we read back only 4-bit groups. That is, only the low nybble of the byte contains useful information. A summary of the Bank Switching Registers follows, and we'll explain them in detail next time:
|40||Horizontal Select. Receives the horizontal select byte (hi-active) for the "presently accessed bank".|
|80||Bank Number Access. Sets the "presently accessed bank".|
|A0||In Setup Mode: Receives the assigned bank
number for the bank presently selected by the daisy chain.|
In Normal Mode: Receives the universal deselect byte. Chunks are hi-active.
|C0||Command Register. Four commands have been found:|
00-Reset daisy chain & enter the setup mode
01-Step the daisy chain to the next bank
02-Reset the nybble steering logic
04-End the setup mode & enter the normal mode
-Note that no more than one bit is ever set simultaneously
-The hardware of this "register" must be able to accept the 02 command, wether it's sent as one or two nybbles, and it must be able to properly interperet the command, even if the nybble synchronization is faulty.
|40||Least significant nybble -- horizontal select for "presently accessed bank".|
|8O||Most significant nybble -- for register 40.|
|AO||Least significant nybble -- bank status for "presently accessed bank".|
|CO||Most significant nybbIe -- for register AO.|
These Bank Switching Registers are intended to control all banks EXCEPT the three "standard" banks. Each bank has a number to identify it. For the expansion banks, these are defined through the intialization software. If seven expansion banks wre present, for example, the banks would be numbered 01 through 07. Additional numbers are allocated as needed. The three standard banks, on the other hand, have fixed numbers:
Numbers for standard Banks FE - EXROM Bank FF - Home Bank 00 - Dock Bank
Now suppose we wanted to read from or write to one of the Bank Switching Registers. The software for it is already in place when you turn on your computer. Appendix A of the TS2068 Technical Manual has the assembly code listings for the RAM resident code, which includes the routines WRITE_BS_REG (write to Bank Switching Register). After a short description, we'll look them over, and see how they work.
The WRITE_BS_REG routine at location 635C will write the value in the E register to the Bank Switching Register whose number is in the D register. To do this we first make a memory address out of the value of the Bank Switching Register. The register value becomes the two most significant hex digits, and the other two digits are zeros. For example, register A0 becomes memory address A000.
Eventually, we'll be writing our data to this memory address, and the BEU or a bank will pick it up and put it in the proper register. But how will the bank "know" that we're talking to it, and not just trying to use that memory location for some more mundane purpose? Another signal has to be sent out, to indicate wether the memory write operation is intended for memory or for the bank switching. This normally unused signal is on the rear connector, and is called IOA5. This signal comes from the sound chip, of all places, and is one bit of an I/O port it contains.
With IOA5 low, the data written to certain memory locations (A000, in this example) will also get written to a bank switching register (the A0 register, in this case). For reasons to be explained later, we only write four bits at a time. That is, only the four least significant bits are accepted by the register. The first memory-write sends the low nybble, and the second write sends the high nybble. Since it's possible for a glitch to cause the hardware to "lose sync" and try to accept the high order nybble first, a "reset" to steer the nybbles properly must also be sent out. After this occurs, the hardware is set to accept the low order nybble next.
Some readers may be amazed that a mere 81 bytes can make such a complicated subroutine! This does sometimes happen when a function is divided partway between hardware and software, and here's a prime example. The fact that sanity was sacrificed for a low cost design doesn't help, either. For now, it would be helpful to review the section in the TS2068 Technical Manual on the registers in the sound chip (pages 21 and 22). Then we'll go on and look at the actual subroutine. Ready? OK, here we go! Turn to Appendix A of the Tech Manual, and look at location 635C. Here's a blow-by-blow description of what's happening:
|635C-635E||Saves the registers (so far so good).|
|6360-6367||Saves the contents of the memory locatIons we're going to wipe out in a moment. Location C000 always takes a hit. Also wiped is the memory location that corresponds to the register we're going to write to. (For register AO, this is location A000.)|
|6368-6375||Saves the contents of the sound chip registers we're about to wipe out.|
|6376-637D||Sets the sound chip I/O port to OUTPUT mode.|
|637E-6384||Sends 00 to the sound chip output port. This will clear IOA5, on the rear edge connector.|
|6385-6389||Now that 10A5 Is low, this causes the low nybble of 02 to be sent to Bank Switching Register CO. This resets the nybble steering logic, so that the next nybble written out will be accepted as the low order nybble. Note that the C0 register is only receiving a single nybble, in this case.|
|638A-638B||Finally! We're sending the low order nybble to the Bank Switching Register we want to talk to.|
|638C-6393||Shifts the high order nybble into the four least significant bits, so It can be sent out.|
|6394||Sends out the second nybble.|
|6395-63A2||Puts the sound chip registers back the way they were. As such, IOA5 goes high again.|
|63A3-63A8||Restores the memory locations we wrote over. Since IOA5 is now high, this does NOT write new values to the BEU.|
|63A9-63AB||Restore the registers we changed.|
|63AC||...and RETurn to the CALLing routine with everything exactly as It was, except that a Bank Switching Register has changed!|
If you've gotten this far, congratulations. But you may want to get yourself a cup of tea, coffee, or whatever more potent nerve settling beverage you'd like. We're about to do the same thing with the READ_BS_REG routine!
While we write to the Bank Switching Registers one nybble at a time, there still 8 bits wide. When we read them, however, they're only four bits wide. (As we said before, we don't read back the same information we've written.) Because of this, we have to read two registers to get enough information to fill a single byte.
The READ_BS_REG routine at location 63AD reads a nybble from the Bank Switching Register whose number is in D, and another from the register whose number is in E. It then packs them both into the E register. Here's how:
|63BO-63B2||The programmer was probably copying code directly from the WRITE_BS_REG routine. This portion is useless here.|
|63B3-63B6||Save the contents of C000, before we use them.|
|63B7-63B8||More useless code.|
|63B9-63C7||Save contents of two sound chip registers about to be wiped.|
|63C8-63CF||Set sound chip I/O port to OUTPUT mode.|
|63D0-63D6||Send 00 to I/O port so IOA5 goes low.|
|63D7-63DB||Reset nybble steering logic.|
|63DC-63DF||Register (D) is read, and the useful information from it is put into the least significant nybble of C.|
|63E0-63E9||Register (E) is read, and the useful information from it is put into the most significant nybble of A.|
|63EA-63EB||Both nybbles are packed into E.|
|63EC-63FA||Restore originaj sound chip registers.|
|63FB-63FC||More useless code.|
|63FD-6400||Replace the contents of location C000.|
|6404||RETurn (at last!!!).|
Note that the code we refer to as "useless" is not at all benign. The three parts hold each other in check, counteracting each other, and making it appear that all three parts don't exist. But if we remove some but not all of them, the remaining part(s) will cause all sorts of mischief. So if you wish to modify this routine, beware!
These two routines form the lowest level interface between the rest of the bank switching software and the actual hardware. From here on, we'll just set the Z-80 registers up to read or write to a particular Bank Switching Register, and CALL the appropriate routine. We needn't worry about how it's done. That is, unless it's desired to experiment with bank switching hardware; then the knowledge is absolutely fundamental.
Some readers may look at these two incredibly convoluted subroutines, look back at the earlier statement that the bank switching software isn't too hard to follow, and then wonder wether my brain hasn't dropped a bit or two, somewhere. Please be assured that the rest of the bank switching code is much more civilized, however comical it may become. If you've come this far, I beg you to read on.
This discussion will generate a lot of questions. Probably the first and foremost arises from the very idea of reading and writing nybbles to memory mapped I/O, and that question is simply, "Why?" Once again, the use of SCLD gate arrays for a cheap design comes in and messes up the bank switching scheme.
Our good friends at Timex could have made things much simpler for us. They could have used I/O ports FC and FD to control the Bank Switching Registers in a manner similar to the two-port scheme used on the sound chip. This would have reduced the two subroutines we've discussed to a few simple instructions, and we could have sent 8 bit information back and forth, as well. The circuitry would be simpler, and easier to follow. All we'd have to do was run a few more signals to it.
That last sentence is the killer that sends chills through the hearts of every chip designer. Though we rarely think of it, each chip has only a limited number of pins. The more complex the chip is, the harder it is to get all the signals you need in and out of the package!
Each expansion bank would likely have contained its own SCLD, to hold the registers for that bank, and do its bank switching chores. It's limited pinout is the probable cause of the problem. By writing one nybble at a time, only four of the 8 Data lines (D0-D3) would have to be run into the chip. By using memory mapped I/0, the signal /IORQ would not be needed by the SCLD.
We've already eliminated 5 pins, and that makes any chip designer smile. A possible 6th pin would also have been saved if the designers intended to make the maskable interrupt (a subject we won't cover here) available for general use. In some cases, the /M1 line would then be needed to distinguish the difference between an interrupt service and a true I/O request. [If the I/O port is activated by /IORQ only, without /RD and /WR lines, decoding /M1 line prevents the port to be activated while interrupt (and possibly also provide IM2 vector). JA]
Now, 5 or 6 pins is a lot, even if we've got 40 to work with. Actually, a preliminary circuit design suggests that a RAM bank SCLD would need only 28 pins, and a ROM bank only 20 pins. These are all standard pin groupings, and the lower the number you can get away with, the cheaper your design. And in the cutthroat atmosphere of the computer business, EYERY penny counts.
Sadly, since we can't put lots of functions on a single chip, this offbeat switching scheme simply gets in our way. Note that the READ_BS_REG and WRITE_BS_REG routines do essentially all communication with the Bank Switching Registers. (One renegade routine tries -- and misses -- communicating with the registers; this can be ignored.) As such, it might be worthwhile to consider rewriting those two routines to use I/O ports FC and FD, instead. Perhaps we could write the register number to port FC, and read or write our data from port FD. This would do a lot to simplify the Bank Switching hardware.
Should anyone want to experiment a bit with building Bank Switching Registers as Timex envisioned them, note that only address lines A13-A15 need to be tested to see if a register is being accessed. This will simplify your circuitry. Note that only some of the Bank Switching Registers are really true registers. Others will serve to reset only selected bits of a different register, and others switch hardware modes without being "stored" in any register at all. (Register C0, bit 1 simply clocks a shift register, for example.) We'll explain it all next time, but this is mentioned so that no one gets too serious about designing a bank switching system until we go over a few more things.
Unfortunately, this article's volume has already expanded beyond all pretentions of sanity, and we have not even covered all of the basics yet. I must apologize for the somewhat sketchy treatment of some topics. I've been hounded for some 18 months to get this information into print, and I've tried to include as much sheer information as I could, to appease some of those who are already familiar with the code in the EXROM.
For the rest of you, I'll be filling in the blanks next time, particularly on the Bank Switching Registers. We'll also look at the RAM resident code, the SYSCON table, and the daisy-chaining of the expansion banks. Doing all this, we'll finally start looking at how the TS2068 handles it all with a guided tour of Flowchart #1, which is included here.
For those readers who don't want to wait two months to learn more, I've left lots for you to do on your own. Read the short explanation on the System Configuration (SYSCON) Table on page 81 of the Technical Manual. Put the EXROM on cassette, and compare the disassembly to Flowchart #1. Use this to begin your own annotated disassembly of the bank switching code. My SYSCON notation (to be explained next time) needs a quickie explanation to do this. The phrase "SYSCON 00" refers to the first entry of a 24-byte block associated with an EXPANSION bank. We'll pretty much ignore the AROS and LROS parts. Try to wade through the listings of the RAM resident code in Appendix A of the Technical Manual. Read the comments, and try to understand what the various routines do. In short, there's plenty to keep you busy for two months.
I'd like this series to be an interactive one. If you're particularly interested in certain things, or need more detail, let me know. Future columns could very easily cover them. If you disagree with anything I've said, or think I've missed something important, PLEASE let me know. Also, feel free to write or call with questions. I'm Wes Brzozowski, 337 Janice St. Endicott, New York 13760. If you want a reply, please enclose a stamped, self-addressed envelope. If you're in a hurry, don't be afraid to call at (607) 785-7007. I'm very friendly, provided you don't call collect and call BEFORE 9:30 PM, EASTERN time. Hope to hear from you!
Editor's note: Wes Brzozowski is an electrical engineer by profession, and is employed by an international computer giant. Wes is a member of the SINCUS T/S Group in New York, and a regular columnist for the group's newsletter.