Background
The RAM & IO expansion boards are controlled by a native instruction named CTRL using a clever hack devised in Marcel's RAM & IO expansion thread (https://forum.gigatron.io/viewtopic.php?f=4&t=64). This instruction simply repurposes a nonsensical ST instruction that talks to the memory with both the write enable signal (/WE) and output enable signal (/OE) asserted. Without an expansion board, this instruction writes a more-or-less random byte at the specified address. The expansion board detects this, prevents the write-enable signal from reaching the RAM, and uses the sixteen bits of the address as a code to control some hardware.
In the last iteration of the original expansion board, these sixteen bits are used as follows:
- A0 controls the SPI clock and whether the Gigatron reads from the RAM or from the MISO lines.
- A1 is not connected and not usable because a smart optimization causes A1 to go up and down during SPI operations.
- A2..5 control four SPI device selection lines
- A6..7 control the RAM bank presented in the upper half of the address space (128KB = 32KB * 4)
- A8..14 are not connected but are not usable because they go up and down during SPI operations.
- A15 controls the SPI MOSI line.
- A0 controls the SPI clock and whether reading at address 0x0000 reads from the RAM or from the MISO lines.
- A4 controls the SPI clock polarity (in order to implement more SPI modes)
- A5 controls zero page banking, that is, the ram bank presented at addresses 0x80-0xff.
Although vCPU programs cannot issue the native CTRL instruction, they can call a native routine named SYS_ExpanderControl_v4_40 with a code in the accumulator vAC. Interestingly this routine ensures that bits A0 and A1 are always zero because the expansion board would completely disable the RAM and make the Gigatron unusable. It also saves the low byte of the ctrl code at location 0x1f8 where other code can see which bank is active, which spi device is selected, etc.
First part of the proposal
The first challenge is to find usable code space. We can observe that in the current scheme, A2 and A3 should never be low together because that would simultaneously select two SPI devices and potentially cause shorts. So the proposition is to declare that ctrl codes with zeroes in both A2 and A3 are a different space, triggering none of the usual functions of the ctrl code bits, but triggering instead new functions.
Code: Select all
Normal ctrl codes: m------- bbssSS-c where bits SS are 11, 10, or 01
- functions: as explained above
Special ctrl codes: xxxxxxxx pppp0000
- pppp represents a hardware function code
- xxxxxxxx control the hardware function pppp
The SYS_ExpanderControl routine should not store the low byte of special ctrl codes in address 0x1f8 because there are expectations about what the bits stored in 0x1f8 mean. Although new boards could recognize the special codes and make sure that they do not change the selected bank or the selected spi device when receiving a special code, existing boards cannot be expected to do the same. Therefore we also need to make sure that special control codes do not leave such boards in a bad state.
Both issues can be addressed by changing SYS_ExpanderControl as follows (just a proposal for discussion for now). This proposed routine first checks whether it was given a normal or special code. Bits A0 and A1 are cleared in both case. Normal codes are stored in 0x1f8 and issued with a ctrl instruction as usual. Special codes are not stored in 0x1f8. They are issued with a ctrl instruction, then are followed by a second ctrl instruction that reissues the code that was previously stored in 0x1f8. In other words, we make sure that cards that do not recognize the special codes are reset in the same state as before. They might briefly select two SPI devices at the same time, but I am hoping here that this will be harmless because we do not cycle the SPI clock during this time.
Code: Select all
label('SYS_ExpanderControl_v4_40')
ld(hi(sys_ExpanderControl),Y) #15
jmp(Y,sys_ExpanderControl) #16
ld(hi(ctrlBits),Y) #17
....
label('sys_ExpanderControl')
ld(0xc) #18
anda([vAC]) #19 check for special ctrl code space
beq('sysEx#22') #20
ld([vAC]) #21 load low byte of ctrl code in delay slot
anda(0xfe) #22 sanitize normal ctrl code
st([Y,ctrlBits]) #23 store in ctrlBits
st([vTmp]) #24 store in vTmp
bra('sysEx#27') #25 jump to issuing normal ctrl code
ld([vAC+1],Y) #26 load high byte of ctrl code in delay slot
label('sysEx#22')
anda(0xfe,X) #22 * special ctrl code
ld([Y,ctrlBits]) #23 get previous normal code from ctrlBits
st([vTmp]) #24 save it in vTmp
ld([vAC+1],Y) #25 load high byte of ctrl code
ctrl(Y,X) #26 issue special ctrl code
label('sysEx#27')
ld([vTmp]) #27 load saved normal ctrl code
anda(0xfe,X) #28 sanitize ctrlBits
ctrl(Y,X) #29 issue normal ctrl code
ld([vTmp]) #30 always load something after ctrl
ld(hi('REENTER'),Y) #31
jmp(Y,'REENTER') #32
ld(-36/2) #33
Another problem is to make sure that additional functions of the expansion boards are reset properly when the Gigatron itself is reset. Here I am proposing to use another property of the normal codes. A0 and A1 can only be set by the ROM. This only happens in the SPI routine. And this routine only sets them to values 00, 01, and 10. In other words, we can use a normal code with both A0 and A1 set to indicate a reset. The very beginning of the ROM could be changed as follows.
Code: Select all
# Setup I/O and RAM expander
ctrl(0b01111111) # Reset signal
ctrl(0b01111100) # Disable SPI slaves, enable RAM, bank 1
# ^^^^^^^^
# |||||||`-- SCLK \ b01=11 => RESET
# ||||||`--- n.c. /
# |||||`---- /SS0 \ b23=00 => SPECIAL CODE
# ||||`----- /SS1 /
# |||`------ /SS2 or /SPOL
# ||`------- /SS3 or /ZPBANK
# |`-------- B0
# `--------- B1
# bit15 --------- MOSI = 0
Hardware
One of the good properties of this proposal is that the special code can be detected by simply ORing A2 and A3, and resets can be detected by ANDing A0 and A1. But this is not that simple because this comes in addition to making sure that we have a ctrl code by ORing /WE and /OE, etc. A bit more logic is needed.
On the negative side, I was hoping to be able to ensure the gal-based expansion board would safely ignore the special codes by merely reprogramming the GALs without changing the PCB. It turns out that this does not seem possible because of the way the two GALs split the work. On the other hand, I believe it is possible to split the work differently in order to make sure that the same GAL receives A2 and A3 as input and is in charge of deciding whether to treat the ctrl code as a normal code (see below).
Code: Select all
gal1
INPUTS CTRLCLK /OE /WE A0 A1 A2 A3 A4 MISO0 MISO1 /NA15TO5 (11)
OREGS /SS0 /SS1 SCK SCLK (4)
OWIRES /OEPORT /OERAM /WERAM DPORT CTRLCLKOUT SCK0 (6)
CTRLCLKOUT = OE & WE & (A2 # A3)
SSO.D = !A2
SS1.D = !A3
SCLK.D = A0
SCK.D = A0 $ !A4
SCK0 = SCK & !SS1
APORT = NA15TO5 & !A4 & !A3 & !A2 & !A1 & !A0 & SCLK
OEPORT = OE & APORT
OERAM = OE & !APORT
WERAM = WE & !OE
DPORT = SCLK & ((SSO & MISO0) # (SS1 & MISO1))
---
gal2
INPUTS CTRLCLK A15 A14 A13 A12 A11 A10 A9 A8 A7 A6 A5 (12)
OREGS B0 B1 /ZPBANK MOSI (4)
OWIRES A15RAM A16RAM /NA15TO5 (3)
NA14TO8 = !A14 & !A13 & !A12 & !A11 & !A10 & !A9 & !A8
NA15TO5 = !A15 & NA14TO8 & !A7 & !A6 & !A5
B0.D = A6
B1.D = A7
ZPBANK.D = !A5
MOSI.D = A15
BANKEN = BANKEN = A15 $ (ZPBANK & NA14T08 & A7)
A15RAM = BANKENABLE & B0;
A16RAM = BANKENABLE & B1;
Curiously this is a little bit more compact that the current organization. It leaves three usable I/O pins in GAL2 that would be best used to do address decoding work because GAL2 has most of the address lines as inputs. But that will not work without changing the schematics and designing a new PCB.
Conclusion
Comments and suggestions are welcome!