Ok, you have been warned. If you're foolish enough to persist in building
it, you may download this zip file with contains
the "Gerber" files of the PCB and a summary of chip connections.
Similarly, the data bus needs a bidirectional 74ABT245 buffer. The DIRection is controlled by the DBIN signal from the peripheral bus, the ENable pin by the CardSel* signal generated by the card (see below).
The CardSel* signal is also connected via a 74LS125 to the RDBENA* line of the peripheral bus to enable the drivers in the connection card and the console-end of the cable. A 74LS125 is necessary, so as not to hold the line high when we are not using it, as another card may need it. The CardSel* signal enables the '125 which input is connected to the ground, the rest of the time the 74LS125 is in high-impedance (i.e. isolated) state.
Two of the remaining gates of the 74LS125 serve to buffer extra signal
lines, such as CRUCLK* and RESET* (see CRU interface below). The last gate
is not used, its inputs are connected to ground, its output is not connected.
74ABT244 +-----+ A0>---|A8 Y8|---A0 A1>---|A1 Y1|---A1 A2>---|A7 Y7|---A2 A3>---|A2 Y2|---A3 A4>---|A6 Y6|---A4 A5>---|A3 Y3|---A5 A6>---|A5 Y5|---A6 A7>---|A4 Y4|---A7 Gnd--|G* | +-----+ +-----+ A8>---|A8 Y8|---A8 A9>---|A1 Y1|---A9 A10>---|A7 Y7|---A10 A11>---|A2 Y2|---A11 A12>---|A6 Y6|---A12 A13>---|A3 Y3|---A13 A14>---|A5 Y5|---A14 A15>---|A4 Y4|---A15 Gnd--|G* | +-----+ '244 +-----+ MEMEN*>---|A6 Y6|---MEMEN* WE*>---|A7 Y7|---WE* ,---------|A5 Y5|----DBIN* | DBIN>---|A8 Y8|-----+-|>o-, | Gnd--|G2* | | 04 | | +-----+ | | '---------------------|-----' +-----+ | | DIR|<----' D0<--->|B8 A8|<--> D0 . | | . . | | . . | | . . | | . . | | . . | | . D7<--->|B1 A1|<--> D7 | OE*|<-------, +-----+ +----------< CardSel* 74ABT245 | RDBENA*<------------------<|--Gnd '125 |
74LS688 +------+ A0---|B1 A1|--Gnd A1---|B7 A7|--Gnd A2---|B5 A5|--Gnd A3---|B3 A3|--+5V 4k7 A4---|B6 A6|----------+---WWW---+---+5V A5---|B2 A2|---------+|---WWW---+ A6---|B4 A4|--------+||---WWW---+ A7---|B0 A0|-------+|||---WWW---' 74LS125 | A=B*|---, |||| CRUCLK*---|>----|G* | | |||| | +------+ | oooo DIP Gnd | oooo switch | |||| | ++++--Gnd ,----------------' | 74LS259 LED | +------+ ,-WWW--|>|---Gnd '----|G* | | A12----|S2 Q0|--+--Bit0 A13----|S1 Q1|-----Bit1 A14----|S0 Q2|-----Bit2 | Q3|-----Bit3 A15----|D Q4|-----Bit4 74LS125 | Q5|-----Bit5 RESET*---|>----|RST*Q6|-----Bit6 | | Q7|-----Bit7 Gnd +------+ |
+5V--+-+-+ | | | 74LS151 4k7 R R R +------+ Gnd----o o-------|-|-+--|D1 S0|--A2 | | +--|D5 S1|--A1 | | +--|D6 S2|--A0 | | '--|D7 | 4k7 Bit2---o o-------+-|----|D0 | ,--WWW--+5V Bit5--|>o----------|----|D3 G*|--+-o o---Memen* 74LS04 | | | A4--, | | | A5--=)>--=)>--o o--+----|D4 Y*| A3---=)>-' 74LS32 ,---|D2 Y|-----------+----MemSel* Bit6-' | +------+ | _ | =|)---CardSel* A3--| ) 74LS30 | |74LS08 A4--| ) '------------------, | A5--| ) | | A6--| )o--, Dbin--, | | A7--| ) | Bit7--=|)--=)>--' | A8--| ) | 74LS08 | 74LS32 | A9--| ) | 74LS138 | | A10-|_) | '08 +------+ | | Bit4----|>o--=|)----|S0 | | | 74LS04 A1---|S1 Y3*|------' | A2---|S2 | | | Y2*|---------------+----MapSel* A0---|G2A* | Memen*---|G2B* | Bit0---|G1 | +------+ |
At the heart of the circuit is a 74LS151 that splits the address space into 8 segments of >2000 bytes, thanks to the connection of its selection inputs S0-S2 to address lines A2-A0. For each chunk of >2000 bytes, a different input D0-D7 will be selected and applied to the output Y. A low level on Y enables memory (see next schematics below), whereas a high level prevents the board from answering to memory requests. Thus, mapping in the various memory area can be controlled by applying low or high signal to the various inputs. The G* enabling input is connected to the Memen* line, so that the board only reacts to memory operations. A SPST switch installed at the rear of the card and a pull-up resistor allow you to disable the board completely. When this switch is opened, G* is held high and the board does not answer any memory request (but still accepts CRU commands).
Input D0 corresponds to the console ROMs, addresses >0000-1FFF. It is connected to CRU bit 2, without any inversion. This means that mapping in this area is on upon power up (which is necessary if you want to replace the console ROMs). So that you can disable this feature, a DIP-switch is inserted in the line. When the switch is open, a pull-up resistor ensures that D0 will always be high. When the switch is closed, D0 is controlled by CRU bit 2.
Input D1, D5, D6 and D7 are tied together. They correspond to range of the traditional memory expansion cards, >2000-3FFF and >A000-FFFF. These cannot be controlled by CRU, but they can be enabled or disabled via a DIP-switch connected to the ground. Here again, when the switch is open, a pull-up resistor brings the inputs high and prevents the board from answering.
Input D3 corresponds to cartridge ROM banks (or RAMBO banks for some devices) at >6000-7FFF. It is controled by CRU bit 5, inverted by a 74LS04 gate. This means that mapping in this area is disabled upon power up, but can be turned on by setting CRU bit 5 to '1'. There is thus no need for a DIP-switch to disable mapping in this area.
Input D4 corresponds to area >8000-9FFF, which contains the console RAM (aka scratch-pad, at >8000-83FF), but also the access ports for many memory mapped devices: VDP, sound chip, speech synthesizer, and GROMs. Obviously, we don't want the board to answer to these addresses, but it could be useful to replace the console scratch-pad. Thus, input D4 is connected to a small circuit consisting of 3 cascading OR gates (74LS32), which combine CRU bit 6 with address lines A3 to A5. This ensures that the board will only answer when CRU bit 6 is zero, and the address is lower than >8400. Just like the console ROMs, CRU bit 6 needs to be on be default, so a DIP-switch and a pull-up resistor are used to disable this feature.
Input D2 requires a more complicated circuit, as it corresponds to the DSR space >4000-5FFF in which you can either access memory or the mappers registers. The mapper will need another selection signal than that of the 74LS151, and it is provided by a 74LS138. The various inputs of the '138 are connected to A0-A2, Memen* and CRU bit 0, allowing the decoder to react only to memory operations in the range >4000-5FFF, when CRU bit 0 is set to '1'. The S0 input of the '138 determines which of the Y2* or Y3* output will be selected, the former being used to select the mapper, while the latter provides the memory selection signal. S0 get its input from two sources, combined with an 74LS08 AND gate: CRU bit 4, inverted by a 74LS04 gate, and address lines A3 to A10, combined with an 8-input NAND gate. As a results, when CRU bit 4 is '1', the mapper is accesses in the whole address range >4000-5FFF. When CRU bit 4 is '0', the mapper only answers at >5FE0-5FFF.
The signal from output Y2* of the 74LS138 is applied directly to the CS* input of the 74LS612 mapper. It is also inverted by a 74LS04 and applied to the ME (map enable) input of the mapper. The signal from Y3* is applied to input D2 of the 74LS151, after being masked trough a 74LS32 OR gate with a combination of DBIN and CRU bit 7. This allows to disable memory read operations in the range >4000-5FDF when CRU bit 7 is '1'. This is necessary to program EEPROM chips that require an unlocking sequence, since the annoying habit of the TI-99/4A to perform reads before writes may scramble the unlocking sequence.
The memory selection signal MemSel* is combined with the mapper selection
signal MapSel* via a 74LS08 gate, so as to produce the card selection signal
CardSel* which will activate the data bus (see above).
74LS154 +-------+ MemSel*------|G1* Y0*|-----------------, A15---------, J9--o<|-+---|G2* . | | 74LS04 | | | . | | WE*---|>o---=|)--, | | . | | 74LS08 | ,-----------------|---|S0 . | | | | ,---------------|---|S1 . | | +-------+ | 74LS612 | | A15----|---|S2 . | | | LE*|--' +-----------+ | | | ,-|S3 Y15*|---------------, | | | | MO10|--' | | | +-------+ | | D7---|D1 Q1|-------|D0 MO8|----' | | | | D6---|D6 Q6|-------|D1 | +-----+ | | | | D5---|D3 Q3|-------|D2 | Bit3-|G* | lnk | | 10K +----------+ | | D4---|D4 Q4|-------|D3 M03|------|A2 Y2|--o o--+-|-WWW-, | CS*|--' | ,-|D0 Q0| D0---|D4 MO2|-|>o--|A3 Y3|--o o----+-WWW-+ +----------+ | | +-|D2 Q2| D1---|D5 MO0|------|A1 Y1|-+---------WWW-+ | CS*|------' Gnd+-|D5 Q5| D2---|D6 M01|------|A0 Y0|-|-+-------WWW-+ | | | '-|D7 Q4| D3---|D7 | +-----+ | | +-----+ Gnd | D7| |---D0 | | D4---|D8 | | '-|A7 Y7|-------|A18 D6| |---D1 Gnd---|OE* | D5---|D9 | '---|A1 Y1|-------|A17 D5| |---D2 +-------+ D6---|D10 MO4|------------------|A6 Y6|-------|A16 D4| |---D3 74LS373 D7---|D11 MO5|------------------|A5 Y5|-------|A15 D3| |---D4 | MO6|------------------|A0 Y0|-------|A14 D2| |---D5 A11---|RS0 MO7|------------------|A4 Y4|-------|A13 D1| |---D6 A12---|RS1 MO9|------------------|A2 Y2|-------|A12 D0| |---D7 A13---|RS2 MO11|------------------|A3 Y3|-------|A11 | | A14---|RS3 | +-----+ | | | | | A4---|A10 | | A0---|MA0 | A5---|A9 | | A1---|MA1 | A6---|A8 | | A2---|MA2 | A7---|A7 | | A3---|MA3 | A8---|A6 | | | | A9---|A5 | | -|C | A10---|A4 | | WE*---|STB | A11---|A3 | | DBIN---|R/W* | A12---|A2 | | MapSel*---|CS* | A13---|A1 | | MemSel*---|ME* | A14---|A0 | | Bit1-----------------|MM | WE*---|R/W* |-+ +-----------+ Dbin---|>o---|>------|OE* | 04 244 +----------+ (16x) 512K SRAM/EEPROM |
The mapper takes its input from the PE-box data bus. Which is a problem since the mapper has a 12-bit bus, whereas the PE-box has a multiplexed 2 x 8-bit bus. This is the reason for the 74LS373: it latches the first (most significant) byte when it is written and presents it to the D0-D3 inputs of the mapper. The D4-D11 inputs of the mapper are connected to the data bus, and will thus input both bytes in sequence since the STB pin is controlled by WE*, which is pulsed low upon writing. When the second byte is written, it overwrites the first one in the mappers memory. The mapper thus latches the least significant byte via D4-D11, and the most significant one (well, 4 bits of it anyway) via D0-D3.
The inputs RS0-RS3 determine in which register the data will be written. Since they are connected to A11-A14, the registers will thus map one word apart: >xxx0 through >xx1E. As described above, the dedicated MapSel* signal enables bus operations via the CS* input, whereas the MemSel* signal enables memory mapping via the ME* input.
The inputs MA0-MA3 determine which register should send its data to the outputs MO0-MO11. These inputs are connected to address lines A0-A3, so the register will be selected by the most significant nibble of the address: >0xxx, >1xxx, through >Fxxx. At least this is what happens when the MM input is high. Things are different when MM is low, which it is at power-up time since it is driven by CRU bit 1 (all CRU bits are low upon reset). When MM is low, the mapper is in "transparent" mode, the outputs MO0-MO7 are kept low, and the outputs MO8-MO11 reflect the values present on MA0-MA3.
The 12 outputs from the mapper, together with the 12 least-significant address lines from the PE-box bus, form a 24-bit expanded address, which corresponds to 16 Megabytes. Since memory chips are 512 kbytes each, we will need 32 of them, activated by two piggybacked 74LS154 decoders. The PE-box address lines A4-A14, and the mapper outputs MO0,MO1,MO4-MO7,MO9 and MO11 are connected to the memory chips address pins. Address line A15 and mapper's outputs MO2, MO8, and MO10 are connected to the S0-S3 selection inputs of the 74LS154 decoders. Output MO3 enables the bottom 74LS154 decoder through its G2* input. It is also inverted by a 74LS05 and presented to jumper J9. This is meant to control the piggy-backed 74LS154 decoder.
The memory chips are meant to be mounted in four layers. MO2 and MO3 control the layer number. Within each layer chips are arranged in pairs, one for the odd-numbered byte, one for the even-numbered byte (this simplifies writing to EEPROM chips). Line A15 serves as the byte selector. MO8 and MO10 determine which pair of chips will be used. This complicated layout is necessary so that, upon power-up, you can have EEPROM in some memory areas and SRAM in others.
You will note that MO0-MO3 are buffered by a '244 controlled by CRU bit 3. This is for compatibility with the original SAMS card. When CRU bit 6 is high (which is not the case upon power-up), the '244 is not conducting and all its output are brought low by four 10K pull-down resistors. This means that, in SAMS-compatible mode, layer 1 is the only one selected. In addition, only the lower 64K in each chip will be accessed, which gives us a total of 1 megabyte.
You may wonder why MO2 is inverted before the buffer. This is done so that, when the mapper is in transparent mode (i.e. MO0-MO3 are low), the second layer is selected. This way, the SAMS-compatibility layer (the first one) is not the same as the power-up layer (the second one). Also note the two links LNK1 and LNK2 on the layer selection lines. They're here to ensure that you do not select non-existent chips, in case you have less than 4 layers of chips. With only one layer, leave both links open: all requests will be redirected to the first layer. With two layers, bridge only LNK2 (the MO2 line). With 3 or 4 layers brigde both links. Beware that with 3 layers only, it is possible to select non-existent chips.
I am not sure that the second 74ABT244 buffer is really necessary. I
included it because I was afraid that the 74LS612 mapper would not be strong
enough to control 32 chips, although this should not be a problem with
the CMOS version. So it may be possible to dispense with that buffer, but
I haven't verified it. For the same reason, after DBIN is inverted with
a 74LS04, it is buffered by a 74ABT244 before being applied to the OE*
pin of the memory chips. The R/W* pin receives WE*, which has been buffered
by the same 74ABT244.
# | Open | Close |
1 | Ignore requests at >8000-83FF | Answer at >8000-83FF depends on CRU bit 6 |
2 | Ignore requests at >0000-1FFF | Answer at >0000-1FFF depends on CRU bit 2 |
3 | Ignore memory expansion space | Answer in memory expansion space |
4 | Not used | Not used |
5 | CRU address >x8xx | Ignored in CRU address |
6 | CRU address >x4xx | Ignored in CRU address |
7 | CRU address >x2xx | Ignored in CRU address |
8 | CRU address >x1xx | Ignored in CRU address |
SW1: 8-DIP switch
SW2: SPST switch
Console modification: two mini SPDT switches
R1: 6-SIP resistor network, 5x 10K
R2: 10-SIP resistor network, 9x 4K7
R3: resistor 100 ohms
R4: resistor 4K7
C1 to C19: 100 nF capacitors
C20: electrolytic capacitor, 100 uF
The board should support four layers of memory chips, each layer comprising 8 chips of 512 K. This gives us a grand total of 16 megabytes. Note that I haven't verified that the address and data bus drivers are strong enough to control that many chips. To be on the safe side, I recommend that you install 74ABT244 and 74ABT245 instead of the LS versions (the BCT version might work too).
You could use pretty much any type of 512K SRAM chips, in a 32-pin DIP or CDIP package. You may also use 512K Flash-EEPROMs, if you want to, also in 32-pin DIP packages. Before buying, make sure that the chip match the following pinouts:
SRAM EEPROM HAMS PCB +---+--+---+ +---+--+---+ +---+--+---+ |A18 Vcc| |A18 Vcc| |A18 Vcc| |A16 A15|< |A16 WE*|< |A16 A14|< >|A14 A17| >|A15 A17| >|A15 A17| |A12 WE*|< |A12 A14|< |A12 WE*|< |A7 A13| |A7 A13| |A7 A13| |A6 A8| |A6 A8| |A6 A8| |A5 A9| |A5 A9| |A5 A9| |A4 A11| |A4 A11| |A4 A11| |A3 OE*| |A3 OE*| |A3 OE*| |A2 A10| |A2 A10| |A2 A10| |A1 CS*| |A1 CS*| |A1 CS*| |A0 D7| |A0 D7| |A0 D7| |D0 D6| |D0 D6| |D0 D6| |D1 D5| |D1 D5| |D1 D5| |D2 D4| |D2 D4| |D2 D4| |Gnd D3| |Gnd D3| |Gnd D3| +----------+ +----------+ +----------+You will have noticed three differences in pinout between SRAM chips and EEPROM chips: pins #3, #29 and #31. This is quite unfortunate, but we have to live with it.
As you can see, the HAMS board uses a hybrid pinout. SRAM chips don't
care about address pin numbering (all addresses are equivalent), but EEPROMs
do (because you have to write a validation code at precise addresses),
so I decided that pin #3 would be A15, like in EEPROMs. No such compromise
was possible for the WE* pin, which means that you'll need to do some hand
wiring for either SRAMs or EEPROM. I reasonned that you will likely install
more SRAM than EEPROM, so the WE* pin matches the SRAM pinout. When installing
an EEPROM, you will need to cross-wire the WE* and A14 pins.
On the second layer, U100 and U104 (the two chips on the left) should be EEPROMs, all other chips should be SRAM. This allows for the presence of persistent code in the card DSR area (>4000-5FFF) and in the console ROM area (>0000-1FFF). Should you want to override the console ROMs, the computer would boot from these EEPROMs.
The third and fourth layers can contain any combination of SRAM and EEPROM that you fancy. Just make sure that chips that are in the same column are of the same type: U100 and U104, U101 and U105, U102 and U106, U103 and U107. This is because each pair of chips implement the even and odd bytes of the same word.
If you have more that one layer, you should bridge LNK1.
If you have more than two layers, you should bridge both LNK1 and LNK2. You should also piggyback a second '154 on top of the first one. Bend out pins #1 though 11, and 13 through 17, these will be connected to the memory chips (pin #22) with wires. Also bend out pin #19 and connect it to J9 with a piece of wire.
Memory chips in layer 2 should have their pin #22 bent out, rather than soldered to the underlying chips. Connect pins #22 of the piggybacked U100 through U107 to jumpers J1 through J8, respectively.
Memory chips in layer 3 and 4 should also have their pin #22 bent out. They should be connected directly to bent out pins of the piggybacked '154.
IMPORTANT: Remember that the pinout of EEPROMs does not exactly match
that of SRAMs. So, any time you install an EEPROM chip, you MUST bend out
its pins #29 and 31. Use short pieces of jumper wire to invert the connections
of these two pins. You will notice that the PCB contains small holes next
these pins, to which you can solder these wires. So, connect bent out pin
#29 to the hole next to pin #31, and connect bent out pin #31 to the hole
next to pin #29.
Layer Chip Connect pin #22 to ---- ---- ------------------ 1 Any Built-in
2 U100 J1 2 U101 J2 2 U102 J3 2 U103 J4 2 U104 J5 2 U105 J6 2 U106 J7 2 U107 J8
3 U100 74LS154 pin #1 3 U101 74LS154 pin #2 3 U102 74LS154 pin #3 3 U103 74LS154 pin #4 3 U104 74LS154 pin #5 3 U105 74LS154 pin #6 3 U106 74LS154 pin #7 3 U107 74LS154 pin #8
4 U100 74LS154 pin #9 4 U101 74LS154 pin #10 4 U102 74LS154 pin #11 4 U103 74LS154 pin #13 4 U104 74LS154 pin #14 4 U105 74LS154 pin #15 4 U106 74LS154 pin #16 4 U107 74LS154 pin #17
The wiring of the piggybacked 154 should look like this:
+---+--+---+ Layer 3, U100|1 24|] Layer 3, U101|2 23|] Layer 3, U102|3 22|] Layer 3, U103|4 U12 21|] Layer 3, U104|5 20|] Layer 3, U105|6 19|J9 Layer 3, U106|7 154 18|] Layer 3, U107|8 17|Layer 4, U107 Layer 4, U100|9 16|Layer 4, U106 Layer 4, U101|10 15|Layer 4, U105 Layer 4, U102|11 14|Layer 4, U104 [|12 13|Layer 4, U103 +----------+Unlabelled pins should be soldered to the underlying '154 (which I symbolized with green [ and ] marks).
You'll need two small SPDT switches (single pole, dual terminal). Alternatively, you may get a DPDT switch (dual pole, dual terminal), which would let you control ROM and RAM together. Personally, I prefer the idea of having two switches, as it gives you more flexibility, but it's up to you.
First, open your console. Refer to my console
surgery guide if you need to.
The switches are best installed at the back of the console, towards
the right (i.e. near the side port). Drill two small holes of the appropriate
diameter in the back of the console and fit the switches in. Make sure
they won't make contact with anything inside.
U504,hole #15------, U504,pin #15-----o o o----+--+5V | U507,pin #8------o o o----' U507, hole #8------'
And here is a map of the motherboard near the side port, with the
pins to cut marked with X and possible sources
+5 volts marked with 5.
__ __| | | |__________________________________________ | 74LS04 74LS03 | +-,_,-+ +-,_,-+ +--------------+ | | 5| | 5| | 74LS32 ,| | | | | | | U507 '| | | | | | +X-------------+ | | | | | +-,_,-+ | | | | | | 5| | | | | | | | | | | | | | | | +-----+ +-----+ | | | +-,_,-+ +-,_,-+ | | | | 5| | 5| | 74 | \___ | 74 X| | | | LS | ___| | LS | | | | 367 | |=== | 138 | | | +-----+ |=== | | | | +-,_,-+ |=== |U504 | | | | 5| |=== | | | | | | |=== | | | | | | |=== +-----+ +-----+ | | |=== +-,_,-+ 74LS138 | | |=== | 5| | | |=== | | | | |=== S | | | | |=== I | | +-----+ |=== D | | 74LS367 |=== E | | |=== | | |=== P | | |=== O | | |=== R | | |=== T +-----+ |=== 74LS244 |===
Now write the desired page number in appropriate register, according to the table below.
Write at Controls memory at -------- ------------------ >5FE0 >0000-0FFF >5FE2 >1000-1FFF >5FE4 >2000-2FFF >5FE6 >3000-3FFF >5FE8 >4000-4FFF >5FEA >5000-5FFF >5FEC >6000-6FFF >5FEE >7000-7FFF >5FF0 >8000-8FFF (only used upto >83FF) >5FF2 >9000-9FFF (not used) >5FF4 >A000-AFFF >5FF6 >B000-BFFF >5FF8 >C000-CFFF >5FFA >D000-DFFF >5FFC >E000-EFFF >5FFE >F000-FFFFOnce done, you can set CRU bit 0 back to '0' (and possibly bit 4 too). You may now enable mapping by setting CRU bit 1 to '1'.
Note that the only the least significant byte of a page number can be
read back. If you would like to keep track of the most significant digit,
you should save a copy of the 16 registers somewhere in memory. Fortunately,
with a total of 16 megs available, memory is not an issue!
*-------------------------------------- * This routine selects a page into a given address block * R0 = page number * R1 = address where it should appear (only first nibble considered) * R12 = CRU address of the card *-------------------------------------- SETPG SBO 0 access card in DSR space SBO 1 set mapping mode SRL R1,12 keep only first nibble SLA R1,1 make it a word MOV R0,@>5FE0(R1) set the page (use safe address) MOV R0,@REGS(R1) remember for later (optional) SBZ 0 DSR space off B *R11 REGS DATA -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1 * *-------------------------------------- * This routine finds which page maps into a given address block * R0 = page number. Negative if register doesn't match memorized value * R1 = address tested * R12 = CRU address of the card *-------------------------------------- SETPG SBO 0 access card in DSR space SRL R1,12 keep only first nibble SLA R1,1 make it a word MOV @>5FE0(R1),R0 get the page (use safe address) SWPB R0 CB R0,@REGS+1(R1) compare with memorized value lsb JEQ SK20 ok SRL R0,8 not the same: use register value NEG R0 but negated to indicate missing msb JMP SK21 SK20 MOV @REGS(R1),R0 return memorized value SK21 SBZ 0 DSR space off B *R11 |
Page Address Chip ---- ------- --------- xx0 00000 U100/U104 xx1 00800 U100/U104 xx2 00000 U101/U105 xx3 00800 U101/U105 xx4 01000 U100/U104 xx5 01800 U100/U104 xx6 01000 U101/U105 xx7 01800 U101/U105 xx8 00000 U102/U106 xx9 00800 U102/U106 xxA 00000 U103/U107 xxB 00800 U103/U107 xxC 01000 U102/U106 xxD 01800 U102/U106 xxE 01000 U103/U107 xxF 01800 U103/U107
NB Even bytes go into U100-U103, odd bytes into U104-U107The middle digit in the page number only affect the internal memory address within a given chip.
Page Address ---- ------- x0x 00000 x1x 02000 x2x 04000 x3x 06000 x4x 08000 x5x 0A000 x6x 0C000 x7x 0E000 x8x 10000 x9x 12000 xAx 14000 xBx 16000 xCx 18000 xDx 1A000 xEx 1C000 xFx 1E000The most significant digit in the page number encodes the most significant part of the memory address, as well as the chip layer. If you have only one layer, pages >400 through >FFF are not used. Leaving both LNK1 and LNK2 open ensures that all pages will map to layer one (i.e. pages >400, >800 and >C00 are equivalent to page >000). With only two layers of chips (LNK1 bridged and LNK2 open), pages >800 through >FFF are equivalent to pages >000 through >7FF. Be careful that with only three layers of chips (LNK1 and LNK2 bridged), pages >800 through >BFF will not map anywhere!
Layer 2 is selected by default upon power-up. Layer 1 is the only layer accessible when the SAMS compatibility bit is set.
Page Address Layer ---- ------- ----- 0xx 00000 2 1xx 20000 2 2xx 40000 2 3xx 60000 2 4xx 00000 1 5xx 20000 1 6xx 40000 1 7xx 60000 1 8xx 00000 4 9xx 20000 4 Axx 40000 4 Bxx 60000 4 Cxx 00000 3 Dxx 20000 3 Exx 40000 3 Fxx 60000 3To find the memory address within a chip (range >00000 to >7FFFF), add up the 'address' values obtained from all three tables.
The following assembly routines convert a page number + offset in page
into a chip number + address in chip, and conversely. They may become handy
when trying to determine whether a given page is RAM or EEPROM, or when
trying to write an unlocking code to a precise adddress in a given EEPROM
chip, respectively.
*-------------------------------------- * This routine converts chip:address into page:offset * R2msb = chip pair number >0-F (from left to right, bottom to top layer) * R2lsb+R3 = address in chip >000000-07FFFF * * R0 = page number >0000-0FFF * R1 = offset in page >0000-0FFF *-------------------------------------- AD2PG ANDI R2,>0F07 sanity check MOV R2,R0 MOVB R3,R0 SWPB R0 reconstitute chip address, top 2 bytes SRL R0,1 ANDI R0,>03F0 keep page bits 6-11 SRC R2,9 break down chip number JNC SK10 INCT R0 set page bit 14 SK10 SRC R2,1 JNC SK11 ORI R0,>0008 set page bit 12 SK11 SRC R2,1 JOC SK12 invert this one ORI R0,>0400 set page bit 5 SK12 SRC R2,1 JNC SK13 ORI R0,>0800 set page bit 4 SK13 SRC R3,12 now break down chip address JNC SK14 INC R0 set page bit 15 SK14 SRC R3,1 JNC SK15 ORI R0,>0004 set page bit 13 SK15 SRC R2,4 restore R2 SRC R3,3 restore R3 MOV R3,R1 get chip address SLA R1,5 keep only 11 bits SRL R1,4 make it an even offset B *R11 * *-------------------------------------- * This routine converts page:offset into chip:address * R0 = page number >0000-0FFF * R1 = offset in page >0000-0FFF * * R2msb = chip pair number >0-F (from left to right, bottom to top layer) * R2lsb+R3 = address in chip >000000-07FFFF *-------------------------------------- PG2AD ANDI R0,>0FFF sanity check ANDI R1,>0FFF CLR R2 SRC R0,2 test page bit 14 JNC SK1 INC R2 put it in chip number SK1 SRC R0,2 test page bit 12 JNC SK2 INCT R2 put it in chip number SK2 SRC R0,7 test page bit 5 JOC SK5 invert it C *R2+,*R2+ put it in chip number SK5 SRC R0,1 test page bit 4 JNC SK6 AI R2,>0008 put it in chip number SK6 SLA R2,8 put chip number in msb SRC R0,4 restore R0 MOV R0,R3 copy page number SLA R3,6 SRL R3,13 keep page bits 6-8 A R3,R2 put them in R2 lsb MOV R0,R3 ANDI R3,>0070 keep only page bits 9-11 SRL R3,2 with room for two more SRC R0,1 test page bit 15 JNC SK3 INC R3 make it lsb SK3 SRC R0,2 test page bit 13 JNC SK4 INCT R3 set it in chip address SK4 SLA R3,11 make room for page offset SRL R1,1 offset must be even A R1,R3 add page offset to chip address SLA R1,1 restore R1 SRC R0,13 restore R0 B *R11 done |
N.B. The routines use the following formulas, where P0-P15 are the 15
bits of the page number (P0-P3 ignored), and A0-A15 are the address bus
lines (A0-A3 ignored, select mapper's register).
Chip pair (from left to right, from bottom to top): P4 ~P5 P12 P14.
Chip row: A15. Top row is even byte (A15 low), bottom row is odd byte
(A15 high).
Address in chip: P6-P11 P13 P15 A4-A14.
|
Chip # |
|
Byte | ||||||||||||||||||||
P4 | ~P5 | P12 | P14 | P6 | P7 | P8 | P9 | P10 | P11 | P13 | P15 | A4 | A5 | A6 | A7 | A8 | A9 | A10 | A11 | A12 | A13 | A14 | A15 |
|
|
There are two main types of Flash on the market:
In order to write to a chip, or to erase it, you must pass it an intricate validation code. This is meant to prevent spurious writing operations from ruining the contents of the memory. Unfortunately, the validation code may vary from chip to chip, or from manufacturer to manufacturer. Practically, this means that you can't write an universal programming algorithm; your routines must be tailored to the type of chip installed. Flash-EEPROMs generally have a "read chip ID" command, but again the syntax of that command varies from chip to chip and between manufacturers...
Programming a 29F040
These examples are for STmicroelectronics version of the 29F040. The version produced by AMD uses the same scheme, except that the key addresses are >555 and >2AA (actually, any address ending with these numbers will do).
To write a byte into the chip:
Write >AA at address >5555
Write >55 at address >2AAA
Write >A0 at address >5555
Write the desired byte at the desired address.
You may check whether writing is completed by reading back the byte. The most significant bit (weight >80) will be inverted until programming is finished. Also, the next bit (weight >40) will toggle its value each time you read it. When programming is finished, it takes the assigned value. The next bit (weight >20) should be '0' during propgramming, if it becomes '1' an error occured (e.g. you tried to turn a '0' into a '1'). You must write >F0 to any address to clear the error condition.
To erase a block (64 Kbytes) or the whole chip:
Write >AA at address >5555
Write >55 at address >2AAA
Write >80 at address >5555
Write >AA at address >5555
Write >55 at address >2AAA
Write >10 at address >5555 to erase the whole chip.
Or write >30 at an address in the desired block (i.e. >x0000).
You may erase more than one block by writing >30 to another block. But you must hurry and do this within 80 microseconds of the previous write. Given that the average assembly instruction takes about 10 microseconds on the TI-99/4A, it won't be that easy... To get an idea, read a byte at any address: if bit >08 is set it's too late to send another block number. Note that 64K blocks are >10000 apart in chip memory, which means that you must alter bits 6 to 8 of the page number to alter them (bits >x38xxx). In other words, add >008000 to the page number to move to the next block.
You may check whether writing is completed by reading back the byte. The most significant bit (weight >80) will be '0' until erasing is finished and '1' thereafter. Also, the next bit (weight >40) will toggle its value each time you read it. When programming is finished, it stays as '1'. If the next bit (weight >20) becomes '1', an error occured. This is bad news because it generally means that the block is bad...You must write >F0 to any address to reset the chip.
You may suspend erasure, so that you can read data from another block
(you can't read data from the block being erased).
To suspend erasure, write >B0 to any address.
To resume erasure, write >30 to any address.
To abort erasure, write >F0 to any address. Be aware that this is likely
to result in invalid data in the block being erased.
With the AMD chip, if you attempt to read from the block being erased you will notice that bit >02 toggles, just like bit >40 (but it does not toggle during a write). When erasing is suspended, bit >40 stops toggling, but bit >02 continues. This feature is not provided by the STmicro chip.
To read the chip ID:
Write >AA at address >5555
Write >55 at address >2AAA
Write >90 at address >5555
Read manufacturer ID from address >00000: >20 for STmicro, >01 for
AMD.
Read chip ID from address >00001: >E2 with STmicro, >A4 with AMD.
Read block protection status from >x0002: >00 for unlocked, >01 for
protected.
Write >F0 to any address to return to read mode.
Note that protecting and unprotecting blocks can only be done by applying +12 volts to several of the chip's pins. The HAMS board makes no provisions for this, so you don't need to worry about block protection.
To reset the chip:
Write >F0 to any address.
This returns the chip to standard "read" mode. It can also be done to abort a command sequence, e.g. if you change your mind after entering the validation code. You must use this command to clear an error condition and to exit "chip ID" mode.
The following (untested) routine illustrates how to write a word to
a pair of 29F040 by STmicro (AMD chips are easier since the validation
code is written inside a single page).
*-------------------------------------- * This routine writes a word into a pair of ST 29F040 chips * R0 = page number * R1 = offset in page * R2 = word to write * R12 = CRU address of the card *-------------------------------------- WR29F MOV R11,R10 save return point MOV R0,R5 save for later MOV R1,R6 MOV R2,R7 BL @PG2AD get chip # + address ANDI R2,>FF00 keep only chip # SBO 0 access card in DSR space SBO 1 set mapping mode SBO 7 disable reading at >4000-5FFF LI R3,>5555 new address BL @AD2PG get page + offset MOV R0,@>5FE8 set page at >4000 MOV @HAAAA,@>4000(R1) write >AA to both chips LI R3,>2AAA new address BL @AD2PG get page + offset MOV R0,@>5FE8 set page at >4000 MOV @H5555,@>4000(R1) write >55 to both chips LI R3,>5555 new address BL @AD2PG get page + offset MOV R0,@>5FE8 set page at >4000 MOV @HA0A0,@>4000(R1) write >AA to both chips MOV R5,@>5FE8 set saved page # at >4000 MOV R7,@>4000(R6) write saved value to saved offset SBZ 7 enable reading again (optional: wait till done) SBZ 0 turn DSR space off B *R10 * H5555 DATA >5555 validation code HAAAA DATA >AAAA HA0A0 DATA >A0A0 code for "write byte" |
Programming a 29C040
This chip always programs 256 bytes at a time, starting on an address that is a multiple of 256 (i.e. >00000, >00100, >00200, etc). You must thus write all 256 bytes (in any order), never allowing for more than 150 microseconds between bytes. Once you stop writing for longer than this, programming begins. Any byte that you didn't set will have a value of >FF.
Note that there is no block erase, as programming automatically erases a sector before rewriting it. There is a chip erase command, however.
In addition, you can set a global protection command, which will cause the device to demand a validation code before programming a sector. You set the protection by sending a validation code prior to programming a sector (you must program a sector for the command to be accepted).
To enter write-protect mode:
Write >AA at address >5555
Write >55 at address >2AAA
Write >A0 at address >5555
Now write a 256-byte sector.
You may want to read back the last byte to find out when programming is finished. The most significant bit (weight >80) will be inverted while programming is proceeding. Also, the next bit (weight >40) will toggle every time you read it. Once programming is finished, both bits will match the value you entered (hopefully).
From now on, you will need to enter the validation code before you can program any sector (which means that you will remain in write-protect mode). The protection status remains active even after a power-down.
To exit write-protect mode:
Write >AA at address >5555
Write >55 at address >2AAA
Write >80 at address >5555
Write >AA at address >5555
Write >55 at address >2AAA
Write >20 at address >5555
Now write a 256-byte sector.
Here also, you can read the last byte to figure out when programming is finished (see above).
From now on you can write sectors directly, without having to enter a validation code. This is mostly useful when implementing a Flash disk. To implement ROMs, I recommend that you use the write-protected mode.
To erase the whole chip:
Write >AA at address >5555
Write >55 at address >2AAA
Write >80 at address >5555
Write >AA at address >5555
Write >55 at address >2AAA
Write >10 at address >5555
Erasing is fast, compared with a 29F040: only 20 milliseconds.
To read the chip ID:
Write >AA at address >5555
Write >55 at address >2AAA
Write >90 at address >5555
Read manufacturer ID from address >00000: >1F for Atmel
Read chip ID from address >00001: >5B for 29C040, >A4 for 29C040A
Read lower boot block protection status from >00002: >FE unlocked,
>FF locked.
Read upper boot block protection status from >7FFF2: >FE unlocked,
>FF locked.
Write >AA at address >5555
Write >55 at address >2AAA
Write >F0 at address >5555 to return to normal reading mode. (Or just
power down).
Locking up boot blocks:
The device allows you to irreversibly lock the first 16 Kbytes and/or
the last 16 Kbytes in the chip. This is meant to implement boot sectors
on Flash disks. I must stress that the operation cannot be undone: once
a block is locked you cannot change its contents. Ever! Also note that
the "chip erase" command is disabled once a boot block has been locked.
Make sure you really want to do this!
Write >AA at address >5555
Write >55 at address >2AAA
Write >80 at address >5555
Write >AA at address >5555
Write >55 at address >2AAA
Write >40 at address >5555
To lock the first 64 sectors (>00000-03FFF) write >00 at >00000.
To lock the last 64 sectors (>7C000-7FFFF) write >FF at >7FFFF.
The following (untested) routine illustrates how to write a 256-words
sector to a pair of 29C040:
*-------------------------------------- * This routine writes 512 bytes into a pair of Atmel 29C040 chips * It sets/leaves the chips in write-protect mode * R0 = page number * R1 = offset in page * R2 = address of data to write * R12 = CRU address of the card *-------------------------------------- WR29F MOV R11,R10 save return point MOV R0,R5 save for later MOV R1,R6 MOV R2,R7 BL @PG2AD get chip # + address ANDI R2,>FF00 keep only chip # SBO 0 access card in DSR space SBO 1 set mapping mode SBO 7 disable reading at >4000-5FFF LI R3,>5555 new address BL @AD2PG get page + offset MOV R0,@>5FE8 set page at >4000 MOV @HAAAA,@>4000(R1) write >AA to both chips LI R3,>2AAA new address BL @AD2PG get page + offset MOV R0,@>5FE8 set page at >4000 MOV @H5555,@>4000(R1) write >55 to both chips LI R3,>5555 new address BL @AD2PG get page + offset MOV R0,@>5FE8 set page at >4000 MOV @HA0A0,@>4000(R1) write >AA to both chips MOV R5,@>5FE8 set saved page at >4000 AI R6,>4000 turn offset into address LI R5,>0100 sector size (2 x 256 bytes) LP1 MOV *R2+,*R6+ copy loop DEC R5 JGT LP1 less than 50 microseconds/byte SBZ 7 enable reading again (optional: wait till done) SBZ 0 turn DSR space off B *R10 * H5555 DATA >5555 validation code HAAAA DATA >AAAA HA0A0 DATA >A0A0 code for "write sector" |
U100 U101 U102 U103 address +-------+ +-------+ +-------+ +-------+ in chip | >0000 | | >2000 | | >8000 | | >A000 | >00000 | to | | to | | to | | to | | >1FFE | | >3FFE | | >9FFE | | >BFFE | | | | | | | | | | >4000 | | >6000 | | >C000 | | >E000 | >01000 | to | | to | | to | | to | | >5FFE | | >7FFE | | >DFFE | | >FFFE | | | | | | | | | | | | | | | | | >02000 | | | | | | | | +-------+ +-------+ +-------+ +-------+ U104 U105 U106 U107 +-------+ +-------+ +-------+ +-------+ | >0001 | | >2001 | | >8001 | | >A001 | >00000 | to | | to | | to | | to | | >1FFF | | >3FFF | | >9FFF | | >BFFF | | | | | | | | | | >4001 | | >6001 | | >C001 | | >E001 | >01000 | to | | to | | to | | to | | >5FFF | | >7FFF | | >DFFF | | >FFFF | | | | | | | | | | | | | | | | | >02000 | | | | | | | | +-------+ +-------+ +-------+ +-------+As you can see, by installing EEPROMs in U100+U104 you can have permanent memory for both the console ROM space (>0000-1FFF) and the DSR space (>4000-5FFF) and RAM in the rest of the addressing space, which was the whole point of this intricate chip mapping in the first place.
Bit | Address | Effect |
0 | >1x00 | 0: Disable access to the DSR area at >4000-5FFF
1: Enable access to the DSR area at >4000-5FFF |
1 | >1x02 | 0: Mapper in transparent mode
1: Mapper in mapping mode |
2 | >1x04 | 0: Enable mapping at >0000-1FFF
1: Disable mapping at >0000-1FFF |
3 | >1x06 | 0: HAMS standard mode
1: SAMS-compatible mode. |
4 | >1x08 | 0: Map memory at >4000-5FE0, registers at >5FE0-5FFF
1: Access mapper's registers repeatedly in >4000-5FFF |
5 | >1x0A | 0: Disable mapping at >6000-7FFF
1: Enable mapping at >6000-7FFF |
6 | >1x0C | 0: Enable mapping to >8000-83FF
1: Disable mapping to >8000-83FF |
7 | >1x0E | 0: Enable reading from >4000-5FFF
1: Disable reading from >4000-5FFF |
All CRU bits are reset to '0' upon power-up or console hardware reset (e.g. plugging in a new cartridge), but not by soft resets (e.g. pressing the QUIT key). CRU bits can only be written to, not read back. If you wish, you can have your software save the current settings in a given memory page.
Bit 0 enables access to the DSR area, at >4000-5FFF. What appears in this area depends on bit 4 and bit 7.
Bit 1 controls mapping mode, just like it does on the SAMS card. In transparent mode, the mappers registers are not used. Instead, the page number is directly determined by the most significant digit of the address: >0000 selects page >000, >1000 selects page >001, etc. Since CRU bits are '0' upon power-up, this mode is active by default. This allows you to program meaningful page numbers into the various registers before switching to mapping mode.
Bit 2 enables mapping in the console ROM area, at >0000-1FFF. This requires a small modification of the TI-99/4A console: the installation of a switch allowing to disable the console ROMs. Since all CRU bits are reset upon power-up, mapping in this area is on by default. This is necessary, so that you can implement your own boot code. However, if you chose not to use this feature, you can disable it by opening DIP-switch #2 (labelled ROM on the board).
Bit 3, when set to '1', enters SAMS-compatible mode. In this mode, the most significant digit of the page number is not considered. As a result, all pages map to layer 1, which should consist only of RAM for compatibility with the SAMS board. For full SAMS compatibility, you should also set CRU bit 4 to '1'.
Bit 4 decides whether the area >4000-5FFF will display memory or the mapper's registers. By default it displays memory, which allows for the implementation of DSRs and for programming EEPROM pages. Registers only appear in the last 32 bytes, at >5FE0-5FFF. Setting bit 4 to '1' allows to access the mappers registers within the whole area >4000-5FFF, which is what the SAMS board does. Note that the most significant digit of the page number can be written, but cannot be read back. Thus, your software should save somewhere in memory a copy of the mappers's 16 registers.
Bit 5, when set to '1' enables mapping to the cartridge space, at >6000-7FFF. This memory area is also used by RAMBO devices, such as the Horizon ramdisk or my IDE card. Note that this bit is invertedso mapping in this area is off upon power-up (contrarily to bits 2 and 6).
Bit 6 controls mapping in the "scratch-pad" area, at >8000-83FF. Normally, the console SRAM chip answers in this area. Since the console only has >100 bytes RAM, it maps them 4 times at >8000, >8100, >8200, and >8300. Setting bit 6 to '0' causes the HAMS board to answer in this area. It does not, however, repeat the same >100 bytes 4 times like the console does. Rather, it implements the full range of >400 bytes. Be aware that this may confuse some poorly written software. Most softwares use the >8300 address, but if one were to write a value at >8300 and expect to read it back at >8200, it would not work. This feature also requires the installation of a switch in the console. Since bit 6 is '0' at power-up time, this feature is on by default, but can be disabled by opening DIP-switch #1.
Bit 7 allows for programming of EEPROMs. In most cases, programming requires passing a command and a validation code to the EEPROM. Unfortunatley the TI-99/4A is in the habit of reading a word before writing it (at least this is the case with most instructions). This may result in scrambling the validation sequence and prevent writing to the EEPROM. By setting bit 7 to '1', reading is disabled in the >4000-5FFF area, while still allowing you to write to an EEPROM page that has been set to appear there. Because each word maps to two chips (one for the even byte, one for the odd byte) there will not be any issue related to the fact the the TI-99/4A always writes two bytes at a time.
Note that mapping in the memory expansion area (>2000-3FFF and >A000-FFFF) cannot be disabled by software. You can disable it by opening DIP-switch #3, in case you'd like to use the special features of the HAMS board together with another memory expansion board.
As you can see, the board has the ability to map in the whole addressing range of the TI-99/4a, not just the memory expansion space. This allows you to map memory in the cartridge space (>6000-7FFF), to install DSRs in the card (at >4000-5FFF), to override the console ROMs (at >0000-1FFF) and the console "scratch-pad" RAM (at >8000-83FF). The latter two require the installation of switches inside the console.
Mapping is controlled by CRU bits, as well as with DIP-switches. The DIP-switches control the default mapping, i.e. what happens upon power-up when all CRU bits are reset. An open switch disables mapping, a closed switch enables it, under control of the CRU.
Address | CRU | Dip switch |
>0000-1FFF | Bit 2 = 0 | 2 |
>2000-3FFF | - | 3 |
>4000-5FE0 | Bit 4 = 0 | - |
>6000-7FFF | Bit 5 = 1 | - |
>8000-83FF | Bit 6 = 0 | 1 |
>8400-9FFF | N/A | N/A |
>A000-FFFF | - | 3 |