The HAMS board

This is my variation on the design of the SuperAMS board. It uses the same mapper chip, but allows for more memory and more complete mapping. Its main features are: Hardware description
_Bus buffering
_CRU interface
_Mapping control
_The memory mapper
_DIP-switch settings
Building the board
_List of components
_Installing memory chips
_Summary of memory wiring
_Console modification
Software issues
_Switching pages
_Page numbering scheme
_Programming an EEPROM
_Transparent mode
_CRU map

Disclaimer. Read this first.

I actually have not built that board yet. Before I left the USA, I laid out a PCB, had it manufactured, and bought all the necessary chips. But then I moved to the UK, started my own lab, had a baby girl... and never managed to populate the board. As a matter of fact, I don't even have a TI-99/4A system to test it with. So, if you want to build the HAMS board, you are wellcome to, but I cannot guarantee that it will work. For all I know, it may fry your console or your PE-box, it may even explode and take your house with it!

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.
 

Hardware description

Bus buffering

As recommended by Texas Instruments, all lines to/from the peripheral bus are buffered on-card. However, given that the buffers have to drive an awful number of chips, I recommend using bi-CMOS-TTL chips, like the 74BCT244 or 74ABT244 rather than the the classical 74LS244. For the same reason, after DBIN is inverted with a 74LS04, it is buffered again by a 74ABT244 before being applied to the OE* pin of the memory chips.

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  


CRU logic

The CRU logic is very basic. A 74LS688 comparator, enabled by the CRUCLK* signal, is used to compare the CRU address to the address set with a DIP switch, and make sure it's in the format >1nxx. The output of the comparator enables latching of the A15/CRUOUT bit by the 74LS259 addressable latch. Lines A12-A14 are used to select a bit among eight. The RST* pin of the 74LS259 ensures that latch is reset upon power-up or console hardware reset, i.e. that all bits will return to zero. Some of them need to be active high be default, and are therefore inverted with 74LS04 inverters. Note that no provision is made to read back the status of the bits.
 
                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   +------+  

Mapping control

This circuit determines in which memory area the board should answer, based on CRU bits and DIP-switch settings.
 
            +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).
 

The memory mapper

The HAMS board uses the same memory mapper as the original AMS and SuperAMS, the 74LS612. However, you may want to get the CMOS version, 74HCT612, which may be faster. This may become an issue if you're planning to use my "zero-wait states" console modification. Refer to my SuperAMS page for a detailed description of the mapper.
 
                                                            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.
 

DIP-switch settings

The board comprises a 8-switch dual-in-line-package (DIP) switch, which is used to preset several options, as well as to choose the CRU address to which the board should answer. To find the CRU address, add up the weight of those switches that are open and add >1000 to the total. For instance, to get >1600 you would set the last 4 switches as follows: close-open-open-close.
 
# 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

 
 

Building the board

List of components

U1: 74LS612  or 74HCT612
U2: 74ABT245 or 74BCT245
U3, U4, U5: 74ABT244 or 74BCT244
U6: 74LS688
U7: 74LS259
U8: 74LS151
U9: 74LS138
U10: 74LS30
U11: 74LS373
U12: 74LS154 (two, if more than 8 Megs desired)
U13: 74LS125
U14: 74LS08
U15: 74LS32
U16: 74LS05
U17: 78M05
U20: 74ABT244 or 74BCT244
U100 to U107: upto 32 512K chip, either SRAM or Flash-EEPROMs (e.g. 29F040 or 29C040, see below)

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.
 

Installing memory chips

The first layer should contain only SRAM chips, for compatibility with the SAMS design.

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.
 

Summary of memory chips wiring

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).
 

Console modifications

If you would like the card to override the console ROMs or the console RAM "scratch-pad", you will need to perform a small modification on your console. Nothing drastic, just installing a couple of switches to disable the ROM/RAM and enable the data bus multiplexer. The switches are not strictly necessary: you could make the modification permanent. However, this would mean that your modified console can only work with a HAMS card in the PE-box. By using switches, you can toggle between a normal console and a modified one. I thus recommend that you install switches.

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.
 

Intstalling the ROM switch

When the switch connects the middle pole to the +5 volts terminal, the console ROMs are disabled and the data bus multiplexer is enabled for operations in the range >0000-1FFF.
 

Installing the RAM switch.

When the switch connects the middle pole to the +5 volts terminal, the console RAMs are disabled and the data bus multiplexer is enabled for operations in the range >8000-83FF.
 

Intalling a DPDT switch

Perform the two modifications above, just solder the wires to the different halves of the DPDT switch. Make sure +5 volts is on the same side for both.
 

Summary

This is how to wire the switch(es)
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                         
|===
 

Software issues

Switching pages

If you set CRU bit 0 and CRU bit 4 to '1' the mapper's registers appear at >4000-5FFF. If you leave CRU bit 4 as '0', the registers are only available through addresses >5FE0-5FFF. Since the latter addresses work in both cases, I suggest you always use them within your programs. The main purpose of CRU bit 4 is to enable compatibility with programs written for the SAMS card, which are likely to use addresses >4000-401F to access the mapper.

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-FFFF
Once 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 number decoding

The least significan digit of the page number is used to select chips within a given layer. This is necessary because it is the only digit that varies with the address when the mapper is in "transparent" mode. Thus, adresses >0000-0FFF in the TI-99/4a will appear as page 0, addresses >1000-1FFF as page 1, etc.
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-U107
The 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   1E000
The 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    3
To 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.
 
Layer
Chip #
Address in chip (>00000-7FFFF)
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
Page number
Offset in page

Programming an EEPROM

There are various types of EEPROMs. All have in common the fact that they come full of >FF bytes (binary 1111 1111 1111 1111) and that you can only turn '1' bits into '0'. Once this is done, the only way to go back is to erase the whole chip, or a block in the chip (the size of a block varies from chip to chip). Such chips are called "Flash" EEPROMs because block erasure is much more rapid than erasing one word at a time (stil it may takes several seconds to erase the whole chip).

There are two main types of Flash on the market:

It's entirely up to you to decide which type you prefer, and you may even have both types on board, as long as you don't mix chips within a given pair (e.g. U100 and U104).

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"

Transparent mode

When the mapper is in transparent mode, the pages appearing in memory are independent of the contents of the registers. Rather, they depend on the address where the memory is supposed to appear. The cartoon below indicates in which chip (and at which chip address) the various area of the TI-99/4A address space are mapped in transparent mode. Remember that chips go by pair, with the even byte in the top row and the odd byte in the bottom row. In normal mode, chips in the second layer will be used, but in SAMS-compatible mode (CRU bit 3 = '1') everything comes from the first layer.
  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.
 

CRU map

 
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.

The table below summarizes how to control mapping in the various memory areas:
 
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

 
Revision 1. 14/3/08. Preliminary. Board not build, not tested.


Back to the TI-99/4A Tech Pages