As I mentioned before, I plan to implement CMOS transmission gates in my digital logic simulator.
However, while driving back from dropping Jean off at the nursery this morning, it struck me that they wouldn't be as trivial as I'd naively thought, for an obvious reason; if a change occurs on one data line, then the gate will cause a corresponding change to its driver on the other data line, after a small gate delay. However, the gate will also be bound as an output on that line, meaning that it will then be told of its own change when the scheduler performs it, so it'll end up making the same change on the output driver of the original input line, meaning it locks the line in that state; when any other drivers try to assert another logical state, there'll be a driver conflict.
I see four possible solutions.
A third type of device < -> line connection
Right now, devices have a number of input pins and a number of output pins, each of which can be individually bound to a single line. Anything bidirectional has to be handled as a pair of pins, one input and one output, connected to the same line. For example, my SRAM device has data inputs and separate data outputs; and they can be joined to the same bus for a bidirectional interface, or wired independently. But perhaps I could introduce a third class of pin, bidirectional pins, which are handled specially; bidirectional pins would not be notified of changes to the line they cause. That way, a transmission gate could have two bidirectional pins, and whenever it receives a change on one, it copies the change to the other bidirectional pin. It'd keep track of which side was being driven and make sure the driver on that side was in the HI-Z state so as not to cause a collision.
Line bridging support
Perhaps I should make it possible to 'bridge' two lines. Pick one arbitrarily as the master, and the other as the slave, and inform them both of this fact. Then when the simulation loop has applied changes to one or more drivers of either line, the master is told to compute the line's new state; it asks the slave to compute the state of its drivers, then merges this with the state of its own drivers to deduce a shared state, which it then tells the slave to inform its connected devices of, before informing its own. The transmission gate would then work by, when the control input was asserted, bridging its two lines together, and unbridging them when the control input drops.
Perhaps I can get by without transmission gates. They're quite low level things, mainly useful in designing flip-flops and multiplexers, so I can probably just define higher-level devices that use them and be done with it. However, I did see high-level use for them in routing signals on bidirectional busses, which would otherwise have to be done with pairs of tristate buffers that listen to the read/write control signals on the bus and configure themselves appropriately, requiring extra logic, and being slower when implemented in silicon.
Using weak logic
As I sit writing this, I realise there's probably a simpler solution; I'll need to think longer to see if there are any showstopping gotchas. That's to use my weak logic system I designed, avoiding the need for any infrastructure changes at all. Under this system, the transmission gate would have (apart from the control input) two input pins and two output pins, designed such that each bidirectional interface is made by joining an input pin and an output pin to the same line. Any line state change notification from either input pin would be ignored if it matched the last state notified, to prevent changes bouncing back and forth between the two lines, but if not then the input line state would be weakly driven onto the opposite output pin. That way, if side A was in the 1 state, the transmission gate would output a weak 1 to side B; and if nothing else was driving side B (as should be the case in a bus), B would then end up in state 1. When the notification of B's resulting state change came back to the B input, it would be seen to be the same as the last state change sent through the gate, and ignored.
Likewise with state 0, and likewise if the sides are reversed.
It gets interesting if something on A drives a 1 and something on B drives a 0 (or vice versa), in which case, the transmission gate will be weakly driving a 1 onto B and a 0 onto A, which will have no effect and not raise an error, even though in a real circuit this would be shorting Vdd and Vss. So the transmission gate itself will need to notice that state and make an error message, I think.
Hmmm, that may be the correct solution... The only downside is that one cannot use weak logic for anything else on those lines, but I suspect one shouldn't be using weak logic around transmission gates anyway, due to the extra line resistance they cause.
Although I still think I'll introduce a weak notion of a bidirectional pin, even if just for neatness; as syntactic sugar, binding a bidirectional pin number N to a line will just bind input pin N and output pin N to the same line (input and output pins have separate numbering schemes).