Welcome back to another episode, as we try to uoravel a few more clues about the Extended Bank Switching for the Timex Sinclair 2068. This time, we'll be getting heavily involved in how the bank switching hardware would have worked, making this installment the most complicated of the series. But this article will cover a lot of subjects, and if one item seems hazy, just skip it and move on to the next. With some rereading, things WILL get clearer, so don't get discouraged. And don't forget that the order that's easiest for YOU to learn these things, may be different from that of others. Keep rereading, and learn in your own way.
Since this kind of information hasn't been published elsewhere, I've had to invent my own notation for a lot of things. These were covered in Part 1, but if you've missed it, you can still get the back issue -- July/August 1986 for $3.00 from TIME DESIGNS MAGAZINE.
This paragraph is for those who may have written or called me with information/advice/questions. If it appears that I'm ignoring you in this column, I must beg you to remain patient. Most of this second installment will have been written before Part 1 has even been put into print (publication delays, you know). As such, there's a good chance you'll have "missed" being mentioned in this installment. But rest assured that I do appreciate your interest, and WILL get to you in Part 3.
Some of you who've been looking up my page references for the TS2068 Technical Manual have probably been a bit befuddled. If you bought your manual from Time" everything will be fine. However, the new version from TDM has the pages re-numbered a bit, and the page numbers I gave last time won't quite match up. I wasn't aware of this when I wrote Part 1, and will give the section numbers instead, from now on. I hope no one was inconvemenced by this. In order to accomodate everyone, let's define yet another notation. From here on, Techmcal Manual references will be abbreviated. The expression "TM3.3.2" would then refer to section 3.3.2 of the TS2068 Technical Manual.
By the way, I do hope no one is grumbling because of the renumbering trick. In doing this, our good friends at Time Designs have been able to reduce the total number of pages in the manual, and so perhaps they can avoid actually losing money on the venture.
And now, on to the good stuff!
Let's first turn to page 255 of the User's Manual that came with your TS2068. The memory layout shows two blocks called the Utility Function Dispatcher, and the Bank Switching Code. They originally come out of the EXROM, and are copied to RAM during the computer's power-on intialization. The two memory maps on page 254 refer to these as "RAM Resident Code", and show that they may reside in two possible memory locations. To make this easier, the EXROM contains a routine that can relocate the code for us. Well, almost. The "relocator" fouls up on a couple of routines when it moves them to high memory. We'll discuss how to fix these in a future installment. Nevertheless, a short look at them now will make other things easier for us to understand.
The function dispatcher is a prime example of the right pew in the wrong church. In most computers, CALLing ROM routines directly through their memory addresses is considered about as civlized as blowing one's nose on the tablecloth. This is because later ROM versions may change the locations of the subroutines, rendering your programs unworkoble. This was precisely what happened when Sinclair changed the ROM on the early ZX8ls. (If you remember this, you're a true "old timer".)
The "proper" way to get at ROM routines is to pass up your CALLs through an "Operating System" that can find the routines, no matter what ROM version is in place. This wouldn't give you access to all of the ROM, however, and so requires an extra measure of programming discipline.
Is it worth it? On1y when handled properly and consistently. A very similar kind of discipline allows many prngrams that run on a "plain vanilla" IBM PC to also run on the PC jr, and the PC-AT, which are all radically different from one another, from their disk systems, right down to their ROMs. It also allows the programs to run on the "PC Clones", that have VERY different ROMs in them. While this prngromming disciplioe means a bit more work, it has grent advantages.
The TS2068 Function Dispatcher is a scaled down attempt to mimic this portion of an operating system. As mentioned last time, it's likely that at least someone at Timex hoped to rewrite the ROMs. The Function Dispatcher may have been a way to insure software compatibility. By sending a "function number" to the dispatcher, the proper routine can be accessed. It also contains presently unused abilities to pass and receive data from the routines it controls. Those future ROMs may well have tapped this ability. Note that TM3.3.2 contains a reference to "the original TS2068" (as it describes OUR machines). Follow-on machines were certainly planned.
But we, Timex enthusiasts, ever the unruly lot, totally ignored the Function Dispatcher, happily CALLing anywhere we liked. While the Function Dispatcher might make it easier to get at the ROM if we were runmng in one of the (presently nonexistent) expansion banks, it's otherwise fairly useless.
We would only use the Function Dispatcher to protect our programs against ROM address changes. But instead, no one uses it, and no one is protected. Therefore, no one will market a ROM or EPROM with address changes, because precious little software will run on it. And therefore, we needn't worry about ROM chonges, and can CALL the RDM to our heart's content. It was a noble thought, Timex, but it was a bit like trying to domesticate a mongoose.
The block called the Function Dispatcher also contains some code that allows the maskable interrupt to work properly when the EXROM is switched in. It will also work with expansion banks, if they have a copy of the code at X0038 at their own location 0038. (The initialization code was supposed to copy this code into RAM expansion banks -- uofortunately, it misses a byte, and anyway errantly tries to copy from the RAM bank to the EXROM; a truly useless exercise.) The interrupt code makes considerable use of the rest of the RAM Resident Code to manage the necessary bank switching.
Following this, almost as an afterthought, is a copy of the NMI handler at Home ROM location 0066. This inclusion is somewhat perplexing, as the Home ROM already has it, the EXROM doesn't link to it, it's short enough to be easily included in any expansion bank, and it doesn't work, anyway. The widely publicived NMI bug, first seen in the Spectrum and perpetuated in the TS2068 Home ROM has been faithfully copied here. There may be some subtle reason for the NMI handler to be there, but it's more likely that a Timex programmer, feeling the pressure of overdue schedules, included it without actually understooding it. At best, it reserves space for some proper code to be put later, but to us it's fourteen orphan bytes of code that are NEVER used.
Following the Function Dispatcher is the Bank Switching Code, which will be quite useful in this series. This code is a bare-bones memory manager which, with a little bit of extra flesh (and a lot of debugging), would shield us from the "hardware realities" of bank switching. While it's fairly easy to write our own machine code to switch the standord banks, the expansion banks are another thing altogether. But by always using the Bank Switching Code, we should never have been able to tell the difference. The code contains portions to do standard bank switching, portions to access the exponsion hardware, and enough "smarts" to know when to do either. As such, bank switching is changed from an occasional migrane to a constant minor irritation.
Ironically, it would be better to describe the "useful stuff" next time, when we'll be concentrating almost completely on the system software. But as a quick description, the code allows us to switch banks, move bytes between banks, find out which banks own which chunks, do the equivalent of CALL and JP functions to other banks, and other necessary niceties. Flowchart 2 (which we'll discuss next time), shows how the BANK_ENABLE routine works. This does the actual bank switching for both standard and expansion banks, and after we've seen how the hardware would probably have worked, you can check the flowchart for an example of how the hardware and software mesh together.
As has been said, this code could have resided at two different locations. Normally, it starts at location 6200, but it can be relocated to F9C0. There are several reasons for this.
If we want to add code into the RAM, there are two basic places to put it and not interfere with a BASIC program being entered. One is above RAMTOP. This is so easy to do that it's the location of choice for most T/S programmers. Yet, it's almost as easy to clear a convenient memory nook down BELOW the BASIC program in memory. The RAM Resident Code can do either.
Now, the Spectrum has no RAM Resident Code, lots of programs for the Spectrum reside above RAMTOP, and the folks at Timex made a reasonable effort to convert Spectrum programs for the TS2068. (Almost ALL programs Timex released were first sold for the Spectrum.) As such, the low memory spot is preferable, as it avoids memory conflicts. This is, in fact, where we usually find the code.
Unfortunately, the convenient low memory area is right in the middle of the space used by the second display file for the extended display modes. There are hardware reasons for this. Some of these allow both display files to reside in just two memory chips, which must be faster (and hence, more expensive) than the rest. Also, the exact location of the second display file should have allowed them to employ some little used properties of dynamic RAMs to squeeze some extra speed out of them, when reading them for display data. Therefore, when the second display file is being used, the code is moved to the less preferable (from the designer's viewpoint) location above RAMTOP.
By the way, when you're switching chunks in and out, it's always necessary to have at least one RAM chunk available, to hold the machine stack. It's needed, among other things, to make CALL and RET commands work, and they work so well that we often forget about the stack altogether. The good folks at Timex sought to help us out in this regard, by moving the stack along with the RAM Resident Code. Since this code must be available, the stack always remains available with it, and we can happily forget about it, once more. The only disadvantage is that the stack size becomes limited (they allow us 512 bytes, or 256 entries). This is normally not a problem.
The ability to have the RAM Resident Code in two different locations has another advantage. Although the TS2068 only moves code to high memory when the second display file is active, you can move it (and the stack) there yourself. If you can choose to run it in either chunk 3 or 7, you don't have to tie up one of your precious eight chunks just to keep the RAM Resident Code available to you. Simply switch back and forth to whatever chunk your own code isn't using at the moment. (Of course, you'll have to keep track of where the RAM resident code IS, in any given situation.) Also, if you should return control to the TS2068 ROM, you'd do well to put the RAM Resident Code back where the computer expects to find it.
Last time, we looked at how to read and write to the bank switching registers in the extended bank switching hardware. We then saw a quick summary of what the registers did, with a promise to explain them in detail, this time.
To recap, there are four input and four output registers, which correspond to four memory-mapped I/O locations. We call the registers C0, A0, 80, and 40, and they sometimes are linked to memory locations C000, A000, 8000, and 4000, respectively.
Each expansion bank has its own register set. When we write to certain registers, every bank will "pick up" the information. In other cases, when we write to a register, the information goes only to a selected bank.
To further complicate things, only writing to some "registers" will actually cause data to be put in a conventional register. In other cases, it may only change certain bits in a register, or not go into any hardware register at all! The "Bank Switching Registers" form a motley crew of circuit functions that are as different from one another as the Marx Brothers, and are just as wild when we put them together.
Figure 1 is a block diagram of a "generic" bank switching SCLD. Note that in reality, a RAM bank SCLD would have included memory refresh and address multiplexing circuitry, for dynamic RAMs. A ROM bank SCLD would have a set of chip enable signals. But the figure does contain all of the Bank Switching Registers and these should be common to both SCLD types. It can then used to show how the bank switching scheme works. It also show how the odd bank switching philosophy selected by Timex would have allowed the SCLD chip to go into an inexpensive package with a very small number of pins.
Note that this is only a block diagram, not a complete circuit layout. Also note that it's based entirely on an analysis of what the ROM software is doing. If the designers at Timex intended additional functions not supported in the original TS2068 ROMs, we'll know nothing about them. Lastly, please note that the connection to the RESET signal is probably not what the Timex designers actually planned. It's included here to suggest that there has to be some way to "disarm" all the horizontal select registers when the computer is first turned on. Otherwise they'd start out filled with random bits, and numerous banks would all try to "takeover" the same memory chunks at power-on, with some very lively results. Actually, an odd bit of code in the initialization software suggests that each bank is "unlocked" after the Horizontal Select register is disarmed through software. This suggests that the SCLD should also contain some power on "lock-up" circuitry to keep each bank out of mischief until the computer straightens it out. We'll talk about this more when we look at the software that actually uses it. (See Flowchart 3.)
As we said last time, register data is sent to the Expansion Bank SCLDs one nybble at a time, to cut down on the number of SCLD pins. This means that the SCLD has to alternately steer the nybble into the right and left half of the byte it's reconstructing. We also said that sending 02 to register C0 will reset the nybble steering logic, just in case a noise pulse may have sent a "false nybble" out, messing up the steering of later nybbles.
But if this is all we do, it won't work. If the nybbles are not being read propely, then the 02 sent in to correct the problem won't get read either. This is why we said that the C0 register must interpret the 02 command, even if the nybble synchronization is faulty. It also has to be able to interpret it if it's sent as only a SINGLE NYBBLE (just the 2), since that's how the routines REAO_BS_REG and WRITE_BS_REG send it.
A "proper" implementation requires all of this, though it's a job to implement. Things get much simpler if we "bend the rules", just this once. Our little trick centers around the fact that all commands to the C0 register have "0" as their most significant nybble, only the "02" command has data line D1 set, and this command is only sent by the READ_BS_REG and WRITE_BS_REG routines, which send it in the single nybble version, only.
And so, if we agree NEVER to send the 02 command to register C0 except in the single nybble version, the hardware will be much simpler. Any time we write to the C0 register with the Dl line set, the nybble steering logic is reset. The ROM code is completely agreeable to this trick, and so the good folks at Timex may well have had the same idea. Figure 1 is drawn to reflect this simplification. Let's walk through it now.
The lower 4 data lines come in at the top, flowing to the Nybble-To-Byte Converter. Every time the select logic detects that we're writing to a Bank Switching Register, it sends the NYBBLE CLOCK signal, allowing the Nybble-To-Byte Converter to accept the nybble. Whenever the select logic detects that we're writing to register C0 with D1 set, it sends the C0-RESET-NYBBLE signal, which resets the nybble steering logic.
The functions mentioned so far are common to every bank. This means that if you're building your own expansion banks, and are putting more than one bank on a single board, they can share this circuitry. (Just thought you'd like to know.)
The Nybble-To-Byte converter reconstructs the original byte we intended to send. Whenever the "second nybble" is written in, the select logic sends out another signal. If the nybble is written to register C0, then the signal WR-C0 is produced. When it goes to register A0, then the signal WR-A0 is sent. Similar things happen for WR-80 and WR-40. Note that these signals must be timed so as not to occur until AFTER the Nybble-To-Byte converter has a byte ready to present.
Using this scheme, when we write to register 80, our value ends up in the Bank Number Access block. This block may also be shared. This works because each bank has its own number. If we wish to change the Horizontal Select byte for a certain bank, we first write the bank number to register 80 (Bank Number Access) and then the Horizontal Select byte to register 40 (Horizontal Select). Only the Horizontal Select register for the bank we have "accessed" will be changed. The bits are high active; that is, if a bit contains a "1", then its corresponding chunk is allocated to that bank.
Registers that cannot be shared have that property because they contain information that's unique to their own bank. As such, we'll refer to them as Unique Bank Registers. Those that can be shared will be called General Bank Registers. (Bank Number Access is General; Horizontal Select is Unique.)
A bank knows it's being accessed when the number in its Bank Number Access register matches another block called the Assigned Bank #. When they're equal, the B-Bit Comparator sends the ACCESS-THIS-BANK-1 signal, whtch makes it possible to write to the Horizontal Select register, or to read from any of the four read-registers in that bank. The Assigned Bank # register is set from a write to register A0, but only under a very special situation that we'll call the "setup mode". We'll discuss this in the section on the Daisy Chain. Ordinarily, writing to register AO does something very different.
When the system is in what we'll call the "normal mode", a write to register A0 sends the "Universal Deselect Byte" to all expansion banks. This looks a bit like a Horizontal Select byte, but has important differences. Each bit represents a memory chunk, just like a Horizontal Select byte, but if a particular bit contains a zero, each Horizontal Select register will leave its corresponding bit alone. If a particular deselect bit contains a one, then if ANY Horizontal Select byte has a one in that location, it RESETS it. As such, the Universal Deselect byte tells all banks which chunks they must give up.
So, if we want to give chunk 5 to expansion bank #07, we first make sure that the Dock and EXROM banks don't have it. (The BANK_ENABLE routine would first give this chunk to the Home Bank.) Then we send the hex value 20 (bit 5 set) to register AO. Now, if any expansion bank had chunk 5, it will have relinquished it. Next, we send 07 (the bank number) to register 80 (Bank Number Access) and finally we send 20 (bit 5 set) to register 40 (Horizontal Select). We have now given chunk 5 to bank 07.
Unfortunately, in the above example, we've also wiped out whatever value was originally in the Horizontal Select register. (Actually, even the BANK_ENABLE routine acts this crudely for all but the Home Bank.) If we wished to treat at least the Expansion Banks with a bit more dignity, we could have first read its Horizontal Select register by sending 07 (the bank number) to register 80 (Bank Number Access) and then reading the register pair 80 and 40. (Remember, the READ_BS_REG routine reads PAIRS of registers.) We would then have the Horizontal Select byte as it had already been set for that bank. We could then have only changed bit 5, and any other chunk that was already selected for this bank, would remain selected.
It's also possible to read the register pair C0 and A0, for the bank number presently being accessed. While the ROM software reads this pair, it only looks at bit 2 of the resulting byte. This happens to be bit 2 of register AO, and every bank has this bit grounded. If we look at the TS2068 schematic, we see that D2 line (and ONLY the D2 line) has a 10K pullup resistor. As such, if we put a bank number in register 80 and then try to read that bank's C0 and A0 register pair, the resulting value will have bit 2=0 if the bank exists, and bit 2=1 if not. This function is used during system initialization to find out how many banks are actually plugged into the system.
If all of this looks like a programming nightmare, that's because it is. Don't forget though, that the initialization software and the RAM Resident Code will normally handle it all for us. The only people who really need to know how to directly program the expansion banks are those who plan to build their own, and have to know how to debug them.
Since the bank switching SCLD only uses address lines A13-A15, there can only be a limited number of possible Bank Switching Registers. These are E0, C0, A0, 80, 60, 40, 20, and 00. Since only the top 3 bits are actually used, EO would be the same as F0, or E7, for example. Each of these corresponds to a single memory chunk.
But the possibilities are even more limited than this. What we've said implies that reading a register happens when we read a memory location from its corresponding chunk, and the memory mapped I/0 is enabled. But running machine code in that chunk also causes memory to be read. As such, code that can activate the memory-mapped I/0 cannot run in a chunk that corresponds to any register. The only routines that ever access them are WRITE_BS_REG and READ_BS_REG, which we walked through last time. These routines are part of the Bank Switching Code, and can be located in either chunks 3 or 7, so the corresponding registers EO and 60 must not be implemented in hardware. (Nor should the Bank Switching Code be relocated outside of chunks 3 or 7!!!)
Also, it's possible that an interrupt could occur during the short time that these routines enable the memory-mapped I/0. This would cause the keyboard routine in chunk 0 to be run before returmng, so register 00 cannot be implemented in hardware. This leaves register 20, which is not used, and has no apparent problem with being used. All of this is mentioned because, if you've implemented the necessary registers, it should be fairly easy to try to add more for your own use. This explanation (hopefully) shows that only register 20 is worthy of any consideration, whatsoever. But note that register 20 is comparable to memory locations 2000-3FFF. If we totally forget about using 20 as a new regtster, it would be possible for a ROM bank with just a 16K EPROM to contain a completely new and upgraded version of the Bank Switching Code in those locations. (The stack would have to go elsewhere.)
At the bottom of the diagram, we see a block called Chunk Select Logic. This compares bits A13-15, which define which chunk is being accessed, and the Horizontal Select byte, which define which chunks the bank "owns". The use of IOA5 tells it wether we're really accessing memory or just a bank switching register. If the TS2068 is accesstng one of this bank's chunks, then the ENABLE signal is sent out.
Note that this logic doesn't check /MREQB. If the TS2068 isn't accessing memory, then the ENABLE signal may switch back and forth, but it will do so harmlessly, since the memory select logic further downstream will sort it out. Howeever, the address lines settle out a full clock cycle before the /MREQB line does, and so this buys us extra switching speed. This is needed because ENABLE is used directly to generate the /BE signal, and this HAS to be applied fairly early on, but again is harmless if memory isn't being accessed. (Those of us who've used the /BE line in our own projects learned this the hard way; it just seemed polite to pass it on to save anyone else the trouble.)
The ENABLE signal should be sent out if IOA5 is high and A13-15 match the appropriate bit in the Horizontal Select Register. It also could optionally be sent out if IOA5 is low, A13-15 match the Horizontal Select, and the chunk in question is 3 or 7. (This would let the READ_BS_REG and WRITE_BS_REG routines run in an Expansion Bank without getting cut off in midinstruction when they switch IOA5. No, I don't know why you'd want to do this, but you may have some good ideas that I don't.)
Figure 2 shows an entire expansion bank, including the SCLD we've just discussed. The /BE signal is generated from the ENABLE line as an OPEN COLLECTOR signal, so that many banks can share the output. An alternate method in use in some products today to simulate a Spectrum Bus generates BE with a logic inversion and a blocking diode. This is also quite acceptable.
The Memory Decoding logic will then decode the bank's memory as normal, except for one, or possibly two, additional constraints. For the first, memory is only enabled if ENABlE is active. The second possible constraint is based on educated speculation, but is still, admittedly, a bit of guesswork.
We know that the TS2068 is basically an enhanced Spectrum. Whenever possible, Sinclair's design was used, and Timex did announce that it would release its own version of the Sinclair Microdrives. This device uses its own crude version of bank switching, werein it disables the Spectrum ROM and switches in its own when the code in the Error Handler (location 0008) is run.
The extended TS2068 commands, like LOAD *, SAVE *, FORMAT, MOVE, and CAT are implemented in the ROM almost exactly like they're implemented in the Spectrum. That is, if you know the command format, you can type them into a line of BASIC, and the TS2068 will accept them. Howeverv they're set so that when you try to RUN them, the error handler at location 0008 will be executed. The only way to make the commands work is to switch in another ROM when the instruction is run at 0008. It must then check the cause of the "error", and run an extended co",and, if one is pending.
There are two ways to do this with Extended Sank Switching. We could define another special bank number (perhaps FD) which switches into chunk 0 when the instruction at location 0008 is executed. But every other expansion bank would have to contain the circuitry to check this, and switch themselves in and out, adding cost and complexity. Alternately, we could put the checking and switching circuitry only inside the mitrodrive interface, and give it a way to disable all banks when it switclies in its un-numbered "Superbank".
The superbank method needs a signal that does to the expansion banks what BE does to the Standard Banks. The TS2068 has 3 backplane signals that are named but not wired into the computer. These are DZIN, DZOUT, and BUSISO. We'll see in a minute that DZIN and DZOUT are needed elsewhere, so let's speculate that BUSISO would have disabled the Expansion Banks. (I've heard mention that BUSISO was instead intended to tristate U15 in the TS2068, but the schematic says it isn't wired to that chip. For the moment, let's consider this is an unreliable rumor, but I'd we1come any evidence to the contrary.)
Getting back to our memory-decoding discussion, we may then guess that no memory would be enabled ii BUSISO were active. The diagram shows a "Special Buffer" at the BUSISO line, because the lack of a "bar" over its name suggests that it's high-true. This means that the buffer must "see" a low signal if no microdrive interface were plugged in, leaving it floating. This is opposite to what a TTL buffer would do, although some DTL structures would fit the bill nicely. Note that if the microdrive interface were part of the BEU, then BUSISO would never be floating and the special buffer would be unnecessary.
Now, all of this may be very nice, but there's still one glaring problem. When we want to send information to a Unique Bank Register, we must first put its number in the Bank Number Access register. If this matches a bank's Assigned Bank #, we can then access that bank's Unique Regtsters. But the Assigned Bank # is itself unique, so how do we get a value in there, in the first place? When we first turn the machine on, that register will be full of garbage. How do we find out what it is? Worse yet, what if TWO banks "power up" with the same Assigned Bank #?
It would seem we've painted ourselves into a corner.
To our rescue comes an incredibly oddball kludge called the Daisychain. The main purpose of this whackiness is to let us put a value into the Assigned Bank # register for each bank. Since we can't use the Assigned Bank # register to access the bank at this time, each bank contains a flip flop that's one bit of a shift register (the Daisychain). Ordinarily, each bank's flip flop contains a "0", but a single "1" bit is stepped through, from bank to bank. If a bank has the "1", then we can put a value into its Assigned Bank # register.
Figure 3 shows the BEU functions that are needed to add Expansion Bank capability. It will drop the /BE line if BUSISO is active, or if IOA5 is low and A13-15 indicate that the chunk being used is not 0, 3 or 7. This wi11 prevent the memory in the standard banks from trying to "answer" an attempt to read a Bank Switching Register. The rest of figure 3 is the start of the Daisychain.
The BEU contains its own form of the C0 register. It normally operates in what we'll unimaginatively call the Normal Mode. Everything we've described so far assumes this mode. However, if we send 00 to register CO, we reset all the bits in the Daisychain and enter what we'll call the Setup Mode. This switches flip flops in the BEU and all the expansion banks. Also, DZOUT at the BEU goes high.
But DZOUT at each expansion bank is still low! figure 4 shows how this can be. Unlike all other backplane signals, which are shared on a common bus, DZIN and DZOUT are not. This is necessary in order to retain the structure of a shift register. Unfortunately, this is not readily compatable with the normally used method of stacking additional items onto the backplane, which would short all the DZINs together and OZ0UTs together, and wouldn't match one OZ0UT with the next OZIN. In fact, it would seem that the most convenient method would use expansion banks on edge-connected cards, plugged into a motherboard, filled with female edge connectors.
By sending an 01 to register C0, we clock each flip flop in the daisychain, and the "1" bit moves into the next bank. When we're in the setup mode (and ONLY then) we can write the Assigned Bank # to register A0, and it will be put in the Assigned Bank # register of the bank that has the "1" in its flip flop. In this way, we individually access each Assigned Bank # register. When we're done assigning numbers, we send 04 to the C0 register, which clears all flip flops and puts us back into the normal mode.
If you haven't yet done so, read TM3.3.2, which gives a snail's eye view of the subject. The "proposed expansion banks" are the very same banks we've been talking about. The SYSCON table is a list and description of all the extra "memory" plugged into the TS2068. The LROS and AROS parts describe what you've got plugged into the Dock bank, and comprise 12 bytes. Note that each expansion bank takes up twice as many bytes, suggesting that the good folks at Timex planned to put a lot more "horsepower" into those guys.
One thing may appear just a bit distressing. The table description says there's room for only ELEVEN expansion bank entries. Well, it's even worse than this, because the space for eleventh entry is used as a scratchpad by the initialization software. (Possibly a bug.) But if we really want more, we should note that the system variable SYSCON contains the address of this table, and we can change this, and put a larger table anywhere we'd like. Each expansion bank has a chance to run some of its own code during initialization, and one of these can rewrite the table. But the hardware that contains this bank should also contain some fancy buffering circuitry for the additional banks, or there'll be TTL fanout problems, not to mention unacceptable capacitance on the bus lines. (Actually, if you try to figure out just how many TTL chips will be needed to replace one bank switching SCLD, you may find it unlikely that even ten expansion banks wil1 ever be run together at the same time.)
The table contains numerous options, and is laid out as follows: [For updated SYSCON table see Part 5. When SYSCON copy is placed in ROM two first bytes are skipped, so 02-04 mean the locations #0000..#0002, 0A-0C - #0008..#000A and 12-14 - #0010..#0012. JA]
SYSCON Table Configuration [12 bytes for the Dock Bank; 8 for AROS followed by 4 for LROS, See TM3.3.2 ] Expansion Bank descriptions follow. One 24 byte block for each bank 00 01=ROM Bank ; 02=RAM Bank ; 00=Bank Inactive 01 Bank #. MSB is set if bank is not yet renumbered The following is copied from locs 0000-0015 of ROM Expansion Banks: 02\ In RAM Banks; Chunks Available, hi true. For ROM will have bit 5 reset 03 + For ROM Banks, these 3 bytes may contain a JP 04/ instruction for RESET ? (But not used in TS2068 ROMs) [In part 4: "The OPEN # code address would be at SYSCON 03 & 04". JA] 05\ Address of the Close Channel routine. It is called with PRM_OUT=2, 06/ and Stream Number on the stack 07\ Address of initialization code (perhaps to open a channel attached 08/ to this bank, or to mark the bank inactive.) 09 Not used ?? 0A-0C A JP instruction to error handler? Not used in ROM 0D-0F Not used ?? 10\ For ROM banks: should have been the boot up address. Instead, because 11/ of a bug, the address of this table entry is the boot up address. 12-13 For ROM Banks. Address of the power-on initialization code (but may not reside in chunk 3) 14 Not used ?? 15 For ROM Banks: 00=Don't initialize ; 01=Initialize 16 For ROM Banks: Boot up priority. Low number=high priority. Home Bank=80 17 Interrupt Priority. RAM banks get 255. ROM get lower=higher priority More of these 24 byte blocks, as needed A single byte marks the end of table: :80H=End of Table
The portions marked as not used may have been reserved for future expansion, but at least one byte was probably set aside to identify the actual function of each ROM bank. This would allow us to find, at a glance, what additional functions were actually "squirreled away" in the extra banks.
The left hand column contains the SVSCON Entry numbers. For example, SYSCON 01 contains the bank #, and every bank has its own SYSCON 01. As such, the SYSCON Entry number is not a displacement into the SYSCON table, but the displacement into the entry for a particular expansion bank. Only some of the table entries are self explanatory. Each will be discussed as we wade through its use in the ROM code.
From here to the end of the series, you'll have the chance to double-check everything I've told you so far. All of my pictures, tables, and descriptions will have to be consistent with the Timex code. It's fully possible that I've missed something in my search through the ROMs, and I'll be counting on you to let me know if you see anything that looks "suspicious". Together, we can add whatever finishing touches are needed for a full description of the Extended Bank Switching.
Don't forget my promise last time that the software is fairly civilized, though somewhat amusing. If reading the hardware description has been as draining for you as writing it has been for me, we can take heart in the fact that it's all downhill, from here on!
Flowchart #1, given last time, is part of the very top level initialization code the machine runs when we turn it on. Part of the Home Bank RAM has already been intialized, and some system variables reflect this, but the memory map on page 255 of your TS2068 USER'S Manual shows "Machine Code Variables". The size of this is determined by the contents of the Dock Bank. (See TM5.1.2, TM126.96.36.199 for more information) and the system hasn't yet found out how much memory to set aside. Therefore, this, and the memory following it have not yet been set up. At this point, we check for extra memory plugged into the system:
At XO8E7 we set the initial location of the SYSCON Table. This has space for AROS, LROS, 10 Expansion Banks, and an 11th Expansion Bank area, which (possibly due to a bug) is used as a scratchpad. Its size is fixed, and if we need a larger table, we must move it somewhere else, ourselves. We then CALL X09F4 which actually builds the table (we'll flowchart this next time).
We then check the SYSCON Table for an LROS. If there is one, there are no machine code variables, so we finish setting up the system variables, and run the LROS according to its instructions (see TM5.1 for more information.)
If there is no LROS, we end up at X090F, checking for an AR0S. If we find one, we check its type (see TM5.1.2.). A BASIC AROS uses no Machine Code Variables, so we finish setting up system variables, and return to Home ROM, after setting a flag telling it to run a BASIC program out of the Dock bank. A m.c. AROS uses Machine Code Variables, which we insert and then finish initializing the system variables. We then run the AROS as required.
If there is neither AROS nor LROS present, we end up at X0918, where we can initialize the system variables. At X099A, we set up so that the main execution loop in Home ROM will run after initializing (an Expansion Bank can override this, if set up properly). We then point to SVSCON 00 for the first expansion bank, and enter a loop to check each bank.
In this loop, starting at X09AC, we check SVSCON 00. A value of 8O marks the end of the table, causing us to end the loop. If it's not 8O, then we check if SVSCON 00 has the value 00. This marks the bank as inactive, causing us to point to the next bank in the SYSCON table, and loop to X09AC.
If the bank is active, we get its number from SVSCON 01. Then from SVSCON 15, we get the Initialization Flag. If this flag is 01, then we will have already run some code in that bank when the SYSCON table was built (more on this next time) and this bank may also "take over" the system after we're done initializing. This depends on its "Boot Up Priority", which we will discuss in a moment. If the flag is not 01, then we point to the next bank in the SYSCON table, and loop again to X09AC.
However, assuming that the Initialization Flag was 01, we end up at X09C4, which gets SYSCON 16; the Boot Up Priority. (The lower the value, the higher the priority.) If this is the highest priority found so far, then we save it and continue. Otherwise we loop back to XO9AC.
If it IS a higher priority, we get SYSCON 10. (Note that in my flowchart I accidentally reversed the digits and called this entry 0l. SORRY ABOUT THAT!!! [Corrected. JA]) If the code were written properly the contents of SVSCON 10 would be the boot up address. (Where wed run after initializing.) Unfortunately, due to a bug in the ROM the address of SYSCON 10 is used instead. (This is a very nasty bug, but at least I can blame THIS error on someone else.) The new boot up address is saved, and we loop agaln to X09AC.
When we find a value of 8O at SYSCON 00, then we've reached the end of the table. We leave the loop, find the highest priority bank and boot up to the given address. (Default is Home Bank, at 0E2F; the Main Execution loop.)
That's the entire flowchart. I should point out one tiny "buglet" that also crept in. The box marked X09E9 should say "...Enabling 0,1,2,4,5, and 6 would...". I left out chunk 5 in a transcription error as I copied over my notes. [Corrected. JA] This shows once again that it was more than just my penmanship that began to fail near the end of that long flowchart! (Is my face ever red!)
If you want some extra things to do, there's plenty. Walk through Flowchart 2 and use its information to continue your own annotated disassembly of the bank switching code. Try to follow what it's doing with the Bank Switching Registers (it's a fairly simple example). If you can do that, then do your own disassembly and flowcharting of the GET-STATUS routine at 6405 hex. Don't disassemble it until after including corrections shown in TM6.5.2. The Expansion Bank portion doesn't change, but the rest is a real mess, and you won't get a feel for how the routine sorts out different banks unless you include the corrections.
Read through the listings of the RAM Resident Code in Appendix A of the Technical Manual, if you haven't yet done so, and also read TM4.1 in I/0 channels (yes, streams and channels figure into this subject, too).
Once again, feel free to write with questions or comments, and please include a SASE, if you wish a reply. I am Wes Brzozowski, 331 Janice St., Endicott, NY 13760. I also like phone calls, 607/785-7007...provided you don't call collect, and call before 9,30 PM, EASTERN time. See you next time!