PC-card V1
This project enables you to attach a 16 or 8 bit ISA-card to your C64 or C128.
DISCLAIMER
Copyrights
In 1988 I built an interface that enabled me to attach 8-bit PC-cards to my C64. My idea was to attach a MFM-harddisk but this failed as I had to write my driver for it. A year later I build a 16-bits version. But I lost this card during a move :(
In 1999 I built a $10 IDE-interface. I got many positive reactions but decided not develop it further due to the fact that a lot of C64-software simply could not cooperate with such a device.
In April 2001 I had some serious requests for both interfaces. The requesters for the IDE interface were pointed to the PC-card interface and agreed that this would satisfy their needs as well. What is my personal interest in this? In 1990 network- and VGA-cards were expensive, nowadays they aren't. In fact most of the ISA-bus cards I own, I got for free :)
The ISA-bus has 24 addresslines that makes it capable of addressing up to 16 MB of memory and (theoretically) up to 64 KB of I/O. The C64 has 16 addresslines and therefore only is able to address up to 64 KB. But a lot of this 64 KB already is occupied by memory, ROM and I/O. The only free space left to use for our own purposes we can find from $DE00 to $DFFF: a meagre 512 bytes. This leaves us no alternative then to use pageswapping.
What is pageswapping? You can compare pageswapping with having a C64 with a lot of floppies. Although you have loads of information to your disposal, you only can access it by one floppy a time. If the data you need resides on another floppy then the one you currently are using, you have to exchange it.
In our case we are able to connect up to 16+ MB of RAM, ROM and I/O to our C64 through the ISA-bus but we are only able to access 256 bytes a time. Only 256 bytes, what about the other 256?
A window of 256 bytes supplies 8 addresslines. This means we need to supply the other 16 ourselves. Therefore we have to add at least some I/O-registers. Beside these, we need some registers for purposes that will be explained later. To be able to DMA, we need two 8237's that also have a number of internal registers of their own. In other words: we simply need to an address range for all these registers. FYI: 64 bytes should cover the whole range.
I had some discussions about which other area(s) could be used beside the original ones. Some examples:
Personally I prefer NOT to use one of the original free pages as using them disables you to use the REU and/or other cartridges. The second SID and some other projects I know of use $D7xx. The SCPU uses some of the demirrored VIC-pages. So not much choice left :(
FYI: from this moment on I will use "I/O" and "WINDOW" when referring these areas.
Whatever your personal choice is, I supplied a simple hardware solution to choose the wanted areas: two jumpers enable you to connect I/O to IO1 and WINDOW to IO2 of the expansionport. You want your own area's? No problem, just remove the jumpers and connect a two-pin header and two wires to the freed pins. The wires must be connected to the active (L) outputs representing the area's you want to use.
What about the software? The above solution means that many combinations of addresses are possible. A solution is to provide the addresses as parameter and to use indirect addressing ie. LDA ($xx),Y. But this will slow down the performance in several ways. IMHO there are other solutions:
Another remark about the size of the window: suggestions have been made to downsize it to 128 bytes. Other suggestions have been made to use two demirrored pages of the VIC-II. IMHO this is not such a good idea. Although both have their advantages at first glance, I personally found out with my old design that these solutions both created problems seen from a software point of view:
To make sure the timing is according PHI2, one of the enable-inputs is connected to the inverted PHI2-signal.
During a DMA-transfer, output HLDA of the first 8237, U2-B4c, is (H). This signal is inverted by U21a-B6a, a 7406 OC-inverter. This signal is fed to the DMA-input of the C64.
The 8237's run on a 4 MHz clock derived from the DOT-clock. This done using a 393, U36b-C4c. If nothing is done, the read- and write-signals outputted by the 8237 use this speed as well (remember that the 8237 was meant for the 4.77 MHz 8088 in the first IBM-PC's). This means we need to synchronise the 8237 with PHI2 using the Ready-input.
To make sure that the 8237 covers the whole high part of PHI2, we need a mechanism that detects both edges of PHI2. The VIC-II sometimes needs to disable the CPU and signals this by negating BA. To make sure a DMA-transfer doesn't mess up this process, we first AND PHI2 with BA (U37b-B7) and use the result for activating the Ready-input.
During a memory-to-memory transfer the 8237 first reads and then writes the byte using an internal temporary buffer. The problem will be reading from the C64. The moment PHI2 goes (L) and releases the Ready-input, this will not be the moment the 8237 stores the info as well. So the data has to be buffered in some way. We'll see we need this buffer as well when transferring data from/to the ISA-bus. The solution is to place the 8237's BEHIND these buffers. This also means that the buffers have to be steered as if addressed by a WINDOW-access.
The 8237 is able to transfer data in blocks of maximal 64 KB. This means it must be able to manipulate 16 addresslines. It is capable of driving the first eight, A0..7, itself. For driving the other eight, A8..15, it needs the support of an extra 8-bit latch: the 74ALS573. As said we'll use two 8237's like in a PC: one will take care of 8-bit transfers, the other of 16-bit transfers. The last is archived by connecting the (generated) addresslines A0..14 of the 8237 to A1..15 of the ISA-bus. A0 must be kept (L) during a 16-bit transfer. As U1-A4a, the 16-bit 8237, already provides A8, the used 573 ,U7-A3c, only has to provide A9..15. The 8th bit is used to generate a (L) A0 during a 16-bit transfer.
U5-B3c is the 573 A8..15 latch towards the ISA-bus for the 8-bit 8237. Another NAND-gate, U6c-B3c, provides the same function as U6b.
The ability to DMA with the C64 created an instant problem: how do we tell the system that the source or destiny is the C64 or the ISA-bus? And what about the moments that the C64 is or is not involved in a DMA transfer?
The goal is to be able to DMA from/to the C64 as well. A 245, U18-A8, is used to buffer the addresslines A0..7 in both directions. The DIR-input is fed by the signal coming from U21a, the one also going to the DMA-input of the expansionport of the C64.
During a DMA transfer the 8237 has to take care of the R/W-line of the C64. For this the output of U3d is NANDed (U6d-B7a) with the output of U37b, (PHI2 * BA). The result is ORred (u52a-B5c) with the output of U21a which output on its turn is used to enable a 125, U43a-A6b, which buffers MEMR.
The moment the C64 is not involved in a DMA transfer, the databus, addressbus and the R/W-line towards the C64 are disabled. Seventeen 33K resistors pull the addresslines and R/W-line (H) (not drawn). This causes the Kernal-ROM or underlying RAM to output the data stored at address $FFFF which is completely harmless (AFAIK).
For generating the addresslines A8..23 for the ISA-bus, we'll use a pair of 573's for every eight bits. Why a pair? I need an 8-bit latch to output the data. But I also need some means to be able to read what was written in the past. For this purpose a 541 could do as well but I choose the 573 so I can piggyback it on top of the first 573; it saves some valuable space and wiring.
The C64 and 8237 are only capable of addressing 64 KB. So I needed a mechanism that enabled me to transfer data between separate 64K-blocks. My solution is to use two pairs of 573's for A16..23: one pair is only activated during a read-action (U27-D8c, U28-D7c), the other during a write-action (U30-C8a, U31-C7a).
The C64's R/W line is either driven by the CPU or the 8237. Transferring data from the C64 towards the ISA-bus is a write-operation for the 6510, but a read-operation for the 8237. The technical solution is an EXOR-gate, 74LS86 U19a-A7a, that combines the R/W-signal coming from the C64 or 8237 and the signal towards the DMA-input of the expansionport. This last signal tells the EXOR-gate to invert the original R/W-signal when a DMA-transfer is going on.
The SHBE-signal is a signal generated by the CPU. In that case you would say that a 80286+ cannot exchange data with an 8-bit only card. Indeed, it cannot. That a PC can is made possible by a 245 buffer that leads the data read from D0..7 to D8..15 or vica versa.
Remark: I have read some info that mentioned that SBHE also can be generated by a card. For the moment I don't know of any card capable of doing so.
Then there is another problem with 16-bit transfers. Let's have a look at the following program:
In the above example register X is incremented. But I can imagine examples where it is preferred to decrement X during the loop. In that case the word must be read when A0 = 1.
Some experiments of my own made me draw the following conclusions:
The last conclusion means that reading an odd 16-bit I/O address will give you an other register and not, as with reading on odd 16-bit memory address, the upperbyte of the even address. The only solution I have to read this upper-byte is to reserve a C64-address just for reading the contents of the buffer that stored the databits D8..15 during the initial read of the word. As the above is also valid for writing, another C64-address must be reserved for writing the buffer towards D8..15 of the ISA-bus.
Two pairs of 573's, U12..15-D2, latch the data. How are they clocked or enabled?
In case of U12 we want to avoid that the latch is clocked during a 16-bit I/O operation. On the other hand it need to be clocked during a specific C64 write. We NAND "7w" and the output of U26c, (MEMR * MEMW), using U41b-D7, and AND it with PHI2 using U51d-D5.
U15 and U12 need to be enabled during:
U14, D0..7 to C64, is enabled during:
- (No DMA), (8-bit card) and (C64 read) U20b-D6d
U13, D8..15 to C64, is enabled during:
- (No DMA), (16-bit card), (C64 write), (Memory card) and (A0=1) U47a-D6d
Enabling U12..15 will cost us a lot of gates. A logical thought is using a PAL or a GAL, but unfortunately I cannot program them :( However, an other idea is using an EPROM as the Enable signals aren't that time critical. In the worst case, that is if the idea won't work, I can replace the EPROM with a little add-on card.
SBHE must be (L) during a 16-bit DMA transfer or the opposite of addressline A0. This realised by NORring the AEN-signal of U1 with A0 using U53c-A2b. A 125 (U43c-B1d) takes care of tristating it.
This means we need an extra bit in the Control register to tell the circuit there is (or isn't) a 16-bit DMA transfer going on through the 8-bit 8237 (U2). This bit is fed (through the OR-gate U3c-C1d) to U39a-B3a for disabling the IOxx-lines (see later) and to U20c for disabling the clock inputs of U13 and U14.
Remark: the above means we "borrow" the odd address for our own purposes. This means that the above mechanism cannot be used for DMA transfers with an odd I/O-address. (Don't know any, to be honest. Soundcard, SCSI ???)
The 6510 uses the PHI2 signal to tell attached IC's when the data- and addressbus is valid. The PC uses the read- and write-signals mentioned above to perform the same trick.
The C64 must be able to address the 8237's. Being Intel-IC's they need IORD and IOWR as inputlines. This means that the 156 must perform its function during an access of one of the 8237's as well. To make sure that IORD or IOWR are activated independent of the state of the I/O-bits, they are ANDed with the two CS-lines, U37d-A5a, U51a-A5a and U51b-A5c.
I already mentioned the fact that a DMA transfer can be made from/to the C64. In that case the MEMx-lines towards the ISA-bus must be disabled to prevent faulty behaviour of attached memory-devices. This is realised by two OR-gates, U42a-A2 and U42b-A2, which OR the MEMx-signals with the output of U16.
The ISA-bus lines SMEMR and SMEMW are only activated if the address is under $100000. The reason for this is to prevent that an old 8-bit card, which only can decode 20 addresslines, writes/reads data when in reality an address above $0FFFFF is outputted. This can be realised by decoding A20..A23 using three OR-gates, U54b..d-A2. The output of the last one is combined with the outputs of U42a and U42b using again two OR-gates, U42c-A2 and U42d. The result is fed to SMEMx.
The six generated signals are fed to a 541 buffer, U57-A2a, for several reasons:
Remark: the R/W-line has to drive a lot of inputs. IMHO it cannot hurt to buffer it with some left over gates: see bottom.
When do we have to execute a refresh? In a PC the CPU or a DMA-transfer is halted for a moment to allow a refresh-cycle. During any transfer where the C64 is involved, the CPU or 8237 cannot do anything the lower half of PHI2. So my idea is to use this lower halve of PHI2 as the moment to activate the refresh.
How to refresh the DRAMs? The keyword is timing. The 4164-12 64K*1 DRAM needs 120 ns RAS-time, read: access time. But before (or after) it needs an extra 90 ns recovery time. We can archive this by delaying the read/write-signals for the ISA bus by 125 ns. Why 125 ns? This is exactly one complete Dotclock-cycle. In 2 MHz-mode this will leave us with another 125 ns for the RAS.
But this also applies to an ordinary read- or writecycle. An extra condition is that the read- or writecycle ends at the falling edge of PHI2. This simply means that the recovery time has to be situated at any edge of PHI2.
Hardware solution:
The Dotclock is fed to an 393 4-bit counter, U36b-C4c, to generate a 4 MHz signal. This clock is fed to two 74's, D-flip-flops (U8b-C4d, U8a-C3c). PHI2 is fed to the Data- and Clear-input of the first 74, U8b. An inverted PHI2 is fed to the same inputs of the second 74, U8a.
In case CLK = 2 MHz, the result is:
It is essential that the generated 4 MHz-signal and PHI2 run in phase as seen in the above figures. This can be realised by giving the 393 a reset pulse the moment PHI2 becomes low. R3, C1 and U32c-C5 take care of this.
Remark: older PC-cards can be equipped with 150 ns DRAMs. When in 1 MHz, you probably won't experience problems. But in the 2 MHz mode only have 125 ns accesstime and that is not enough! So be warned.
IRQ1
IRQ2
Some friends suggested to use 8259's, Intels Programmable Interrupt Controller, but I couldn't see any real advantages. IMHO I even needed more hard- and software then with the above simple design.
IOCHRDY
0WS
CLK
BALE
OSC
RESET
AEN
DRQx, DACKx
MASTER
U52a, an OR-gate, can be replaced by a 02 or 260 (NOR-gates) plus an EXOR-gate.
One EXOR gate can be used to buffer the R/W-line.
What is it
Remark 1: when I mention the C64, unless otherwise stated, I also mean the C128.
Remark 2: IMHO you get the best result with this project by using a CPLD or FPGA.
DMA
Do we need DMA? One thing is sure: to be able to use 1.44 MB drives you'll need it. In 1989 I had no interest in DMA. Now the goal is to include DMA using two 8237's. Why the 8237? For two simple reasons:
* I have a dozen of them laying around
* you can find enough docs about it everywhere
Speed
My goal was to be able to let it run at 2 MHz so it could be used by a C128 in 2 MHz mode. Problem: the C128 always accesses the I/O-area, $D000-$DFFF, in 1 MHz-mode.
To be honest, not until writing Beta version 1 I haven't thought about using the free memory ranges available in the C128-mode. I'm not familiar with the behaviour of the bus when in C128 mode so I first have to do some research on that before offering a C128-version of this interface.
Until then the design is focussed on use with a C64 or a C128 in C64-mode.
CMD's SuperCPU
I simply don't know if the SCPU can be used with this card. Both need the expansionport but I don't see any problem when using a expander. What I don't know is how the SCPU behaves when accessing an address. In some cases it accesses the C64, in others it doesn't. If you map the PC-card to a range the SCPU doesn't access, it simply won't be seen at all.
65816
I want use the 65816's ability to address up to 16 MB of course. This will be covered at the article PC-card Extra.
Schematics
You'll find them here.
Used codes with IC's
All used IC's can be of the 74LS, 74ALS or 74F type. I refer to a IC with its own code plus the place where to find it in the schematic.
Example 1: 32, U3C-A5d. This is a 74LS32, identified by U3, third gate, found in quadrant A5, bottom right.
Example 2: 573, U14-D2b. This is a 74ALS573, identified by U14, found in quadrant D2, top right.
The position inside a quadrant is not always given for various reasons.
Theory and hardware
If you are unfamiliar with the so called ISA-bus of the PC, please read The ISA-bus first.
* demirroring the VIC-II video chip will free up to 3 pages: $D1xx..$D3xx
* demirroring the SID audio chip will free up to 3 pages: $D5xx..$D7xx. But only $D700 can be used to remain compatible with the C128.
* demirroring one or both CIA's which frees up to 2 * 240 bytes for the I/O part.
I personally favour one of the freed VIC-pages for the window and a part of the CIA-page for the I/O.
* the programmer of a tool or driver also provides a small program that accepts two addresses as input which will change all addresses of the main program where needed.
* provide the sources and compile them with the wanted addresses.
* an other number then 8 causes problems with shifting
* 128 bytes hinder you to exploit the full potential of indexed addressing
* with 512 bytes: accessing the second page roughly costs the same amount of instructions as doing a pageswap. No gain at the end.
Decoding the I/O-area.
I assume that used area is 256 bytes wide. A 139, U29b-A6c, enables you to choose a 64 bytes window by means of a jumper. Using a demirrored CIA-area means that the line already represents a 64 bytes wide area. In that case you must think of setting the jumper according this area! Another idea is to connect the generated signal directly to a pin leading to the enable-input of U29a-A6c.
The second half of the 139, U29a, takes care of dividing the 64 bytes in four parts of 16 bytes: two are used for the 8237's, one for the rest of the I/O-registers and one is unused.
A 154, 4-to-16 decoder, U44-A6a, combines three addresslines and the R/W-line to 16 negative outputs for addressing all needed buffers and latches:
0w: writing control register U9-B6c
0r: reading control register U10-B6d
1w: writing addresslines A8..15 register U24-C7c
1r: reading addresslines A8..15 register U25-C8c
2w: writing addresslines write A16..23 register U28-D7c
2r: reading addresslines write A16..23 register U27-D8c
3w: writing addresslines read A16..23 register U31-C7a
3r: reading addresslines read A16..23 register U30-C8a
4r: reading IRQ buffer 1 U49-C4
5r: reading IRQ buffer 2 U48-C4
7w: writing D8..15 -> ISA-bus register U12-D2c
7r: reading D8..15 -> ISA-bus register U13-D2d
4w, 5w, 6r, 6w not used.
DMA
The 16-bit ISA-bus has seven DREQ/DACK lines and that's why we'll need two
8237's. Like a PC we'll use the second 8237 for 16-bit transfers only.
First the mechanism has to be activated by either MEMR or MEMW during a DMA-transfer. An AND-gate, U26c-D5d, combines these two signals. The output of U26c is fed to a NOR-gate (U22d-C1) which output is fed to the READY-inputs of the 8237's.
The output of U26c is also fed to the data-input of a 74 flip-flop, U40a-D1. This 74 is clocked by the output of U37b, (PHI2 * BA). In this way U40a detects the rising edge of PHI2 but only then when the VIC-II doesn't access the bus. The negative Q-output is fed to the data-input of the second half of this 74, U40b.
This 74 is clocked by the inverted PHI2. In this way U40b detects the falling edge of PHI2.
The Q-output of U40b is fed to the above NOR-gate, U22d, as well. At the falling edge of PHI2, the output of U22d becomes (L) allowing the 8237's to go on. The moment the 8237 ends the MEMW- or MEMR-action, the output of U22d becomes (H), clearing both the flip-flops through the inverter U32F-D1.
This solution means that the second 8237 is only capable of transferring 32K words. I have the schematics of the IBM AT that I used as reference. But one page is missing: the one covering DMA. So I have no idea how IBM solved this.
The 573 is enabled by output AEN of the 8237. But this signal has to be inverted first. We use a NAND-gate, U6b-A3c. Negating one of the inputs of the NAND-gate tri-states the 573. One is AEN of course. The other input comes from a AND-gate, U37c-B3c, which ANDs the PHI2 and the signal coming from the MASTER-input of the ISA-bus as input. Negating this last line means a card wants to control the bus. The use of PHI2 means that the address is only valid during the positive half of PHI2. This is necessary for the refresh of DRAM's and the fact that the VIC-II controls the C64 during the lower half of PHI2.
The solution to the first question is using two bits of a register to tell whether the source or destiny is the C64 or an ISA-card.
The solution to the second question is that when the C64 is not involved in a DMA transfer, we present the C64 a safe situation like reading an address that has no further influence to the system in any way. In this case I had $FFFF in mind.
These register bits are combined with the MEMx-lines using two NOR- and one OR-gate (U22c, U22b, U3d - B6a/b). The result is used to disable the MEMx-lines towards the ISA-bus (see later) and to serve as one of the inputs for U46c.
The 245 is enabled by NAND-ing (U46c-B7) (PHI2 * BA), MASTER and the signal coming from U3d telling the C64 is involved (see later).
U17-A8-c, a 573, latches the addresslines A8..15 towards the C64 during a DMA transfer. Its clock input is fed by ASTB. Its enable input is fed by U6c as well but an OR-gate, U3b-A7c, first ORs this signal with the one coming from U46c.
The addressbus
The C64 can generate the addresslines A0..7 by itself but the other 16 have to be generated by I/O. As already mentioned, a 245 bi-directional buffer (U18-A8) takes care of A0..7.
First we have to find out whether a memory read- or write-operation is going on using U26c. This signal is used to enable a 139, 2-to-4 demultiplexer, U45a-D7d. The other inputs for this 139 are MASTER and MEMR. When MASTER is (L), neither one of the 573's are allowed to be enabled.
Both the needed outputs of the 139 are ANDed (U26a-D7d, U26b-C7b) with the read-signal for its 573-counterpart. This last construction enables reading the contents of U28 and U31.
U24-C8: C64 -> ISA-bus A8..15
Clock: I/O - write "1w" through 04 inverter, U42d-C7d
Output enable: only when PHI2=(H), MASTER=(H) and no DMA : NAND U46a-C7d
U25: read ISA-bus A8..15
Clock: (H)
Output enable: I/O - read "1r"
U31: C64 -> ISA-bus A16..23
Clock: I/O - write "2w" through 04 inverter, U42a-C7d
Output enable: MEMR = (L) and MASTER=(H), or I/O-read of U30
U30: read ISA-bus A16..23
Clock: (H)
Output enable: I/O - read "2r"
U28: C64 -> ISA-bus A16..23
Clock: I/O - write
Output enable: MEMW = (L) and MASTER=(H), or I/O-read of UCx17
U27: read ISA-bus A8..15
Clock: (H)
Output enable: I/O - read "3r"
The databus (1)
To make sure the C64 can drive the databus outside the C64, a 245, U4, will buffer it.
Enable during:
* I/O
* WINDOW
* DMA, when the C64 is involved Direction:
* R/W from C64
* MEMW during a DMA-transfer
To enable the 245, an 3-input AND gate, U20a-B7c, combines the output of U6d with the enable signals for I/O and WINDOW. Its output is fed to the enable-input of the 245.
The databus (2): 8-bit transfer registers
This sounds easy: the C64 is a 8-bit system so it must be able to handle 8-bit transfers. But unfortunately it isn't that simple. Let's have a look how a 16-bit card handles 8-bit operations:
SHBE A0 Transfer
0 0 word
0 1 byte on D8..15
1 0 byte on D0..7
1 1 will never occur
The above means that we need a kind of T-junction which enables us to read/write from/to D0..7 or D8..15 depending on addressline A0 and what type of card it is.
How does a PC know that a card can handle 16-bit transfers? The card signals this by negating MEMCS16 or IOCS16 according the type of operation. I have thought about providing some means to detect the fact that a card used one of these lines. But IMHO the Eyeball MK 1 should be sufficient enough :)
The databus (2): 16-bit transfer registers
The trouble I ran into with my initial the design was caused by real 16-bit transfers. First I found out that some transfers MUST be 16 bits wide.
Example: an 16-bit IDE-interface only accepts 16-bit I/O transfers. In case of the IDE-harddisk there is an additional problem. Data can only be handled sectorwise: it has to be received/sent in portions of 256 words. And this data has to be read/written in a row from/to the SAME address. This means that after reading this address the next read will make the harddisk output the next WORD. A C64 can only perform 8-bit transfers so we must provide a temporary 16-bit latch for storing the word.
.eq WINDOW = $DF00
START ldx #$00
lda WINDOW,x
sta $2000,x
inx
bne START
When A0=0 the WORD is read from the ISA-bus and stored in the buffer, the Accu is filled with the lower-byte. When A0=1, the only thing that should happen is that the Accu is filled with the contents of the register that stored the upper-byte.
This means that we need some means to tell the system that A) we want a 16-bit transfer and B) in what direction the addresscount goes.
To worsen the problem, writing is done in the opposite way: when incrementing X, the actual write has to be done when A0 = 1, when decrementing X, the actual write has to be done when A0 = 0.
* all memory related cards (videocards, the buffers of networkcards) are able to perform 8-bit operations
* the only 16-bit-only operations I know of are I/O operations
* 16-bit I/O operations can happen on odd addresses as well (??? no proof)
+----------------------------------------- DMA (0 = no)
| +--------------------------------------- Source (0 = C64)
| | +------------------------------------- Destination (0 = C64)
| | |
| | | +----------------------------------- 8/16 bit (0 = 8-bit)
| | | | +--------------------------------- ReaD/Write (0 = write)
| | | | | +------------------------------- IO/MEM (0 = MEM)
| | | | | | +----------------------------- A0 C64
| | | | | | |
| | | | | | | +------------------------- 573 D0..7 to ISA Clock U15
| | | | | | | | +----------------------- 573 D0..7 to ISA Output En. U15
| | | | | | | | | +--------------------- 573 D0..7 to C64 Clock U14
| | | | | | | | | | +------------------- 573 D0..7 to C64 Output En. U14
| | | | | | | | | | | +----------------- 573 D8..15 to ISA Clock U12
| | | | | | | | | | | | +--------------- 573 D8..15 to ISA Output En. U12
| | | | | | | | | | | | | +------------- 573 D8..15 to C64 Clock U13
| | | | | | | | | | | | | | +----------- 573 D8..15 to C64 Output En. U13
| | | | | | | | | | | | | | | +--------- MEMR / IORD
| | | | | | | | | | | | | | | | +------- MEMW / IOWR
| | | | | | | | | | | | | | | | | +----- SBHE
| | | | | | | | | | | | | | | | | | +--- A0 ISA
0 1 2 3 4 5 6 0 1 2 3 4 5 6 7 8 9 A B
--------------+------------------------
0 - - 0 0 - 0 | 1 0 - 1 - - - 1 1 0 - 0
0 - - 0 0 - 1 | 1 0 - 1 - - - 1 1 0 - 1
0 - - 0 1 - 0 | - 1 1 0 - 1 - 1 0 1 - 0
0 - - 0 1 - 1 | - 1 1 0 - 1 - 1 0 1 - 1
0 - - 1 0 0 0 | 1 0 - 1 - - - 1 1 0 1 0
0 - - 1 0 0 1 | - - - 1 1 0 - 1 1 0 0 1
0 - - 1 0 1 0 | 1 0 - 1 0 0 - 1 1 0 - 0
0 - - 1 0 1 1 | 1 0 - 1 0 0 - 1 1 0 - 1
0 - - 1 1 0 0 | - 1 1 0 - 1 - 1 0 1 - 0
0 - - 1 1 0 1 | - 1 - 1 - 1 1 0 0 1 0 1
0 - - 1 1 1 0 | - 1 1 0 - 1 1 1 0 1 - 0
0 - - 1 1 1 1 | - 1 1 0 - 1 1 1 0 1 - 1
1 0 - 0 - - 0 | 1 0 - 1 - - - 1 * * 1 0 * = depends on 8237
1 0 - 0 - - 1 | 1 0 - 1 - - - 1 * * 0 1
1 0 - 1 - - 0 | 1 0 - 1 - - - 1 * * 0 0
1 0 - 1 - - 1 | - - - 1 1 0 - 1 * * 0 1
1 - 0 0 - - 0 | - 1 1 0 - 1 - 1 * * 1 0
1 - 0 0 - - 1 | - 1 1 0 - 1 - 1 * * 0 1
1 - 0 1 - - 0 | - 1 1 0 - 1 - 1 * * 0 0
1 - 0 1 - - 1 | - 1 - 1 - 1 1 0 * * 0 1
1 1 1 - - - - | - 1 - 1 - 1 - 1 * * * -
All the above equations are only valid for a valid read- or write-operation. For U12..15 this means they have to be clocked when PHI2 = (H) and one of the MEMx- or IOxx-lines = (L). This is realised by U39b-C4c and U20c-C3c. PHI2 is needed because the timing of the MEM/IO lines aren't guaranteed when generated by a 8237.
- (No DMA) and (C64 write) U22a-C6b
- (DMA) and (Source = C64) U53a-C6b
under the condition MASTER=(H).
An OR-gate, U54A-C5b combines outputs and the result is NANDed (U41c-C4a) with the signal coming from MASTER.
- (No DMA), (16-bit card), (C64 read) and (I/O card) U33a-D6
- (No DMA), (16-bit card), (C64 read), (Memory card) and (A0=0) U23b-D6a
- (DMA), (8-bit card) and (Destination = C64) U33b-D6b
- (DMA), (16-bit card), (Destination = C64) and (A0=0) U55a-D5b
The last line means I am not able to DMA with 16-bit IO-cards (for the moment, see later).
All the above outputs are NORred using U47b-D5b.
- (DMA), (16-bit card), (Destination = C64) and (A0=1) U23a-D5b
* a C64 I/O-read "7r"
The outputs of U47a and U23a are ORred (U53b-C5b) and its output is ANDed with "7r" using U56a-C5b.
16-bit I/O and DMA
I first thought that it was impossible to DMA from/to IDE but then I got the following idea: use the 8-bit 8237 and disable all certain features when addressline A0=1:
* disable the IOxx-lines towards the ISA-bus
* A0=0 towards the ISA-bus during a read or write
* disable the clock input of U13 when reading its contents
* take care of what operation happens during a read or write.
U19b-C1d EXORs addressline A0 and IOWR:
A0 IOWR | result
-----------+---------
0 0 | 0
0 1 | 1
1 0 | 1
1 1 | 0
The output is fed to U3c, the OR-gate mentioned above, which causes that the bit only is effective during a I/O-read or -write when A0=1.
U37a-B1b ANDs the bit with A0 to guarantee A0=0 during the transfer. U43b makes sure that A0 can be tristated if needed.
The Read- and write-signals
The PC is capable of addressing memory and I/O on a very low level: the CPU has separate read- and write-lines to do that. The 6502-compatible CPU's don't have this feature. Therefore I/O in a system with such a CPU is called "Memory mapped I/O".
As the C64 doesn't have this feature, we have to create it. My original solution was using one bit but Pasi came with the idea to use two: one for writing, the other for reading. This enables one to read from I/O and to write to memory (or the other way around) without the need to toggle the bit between every action.
This means we have to transfer PHI2, R/W and the two bits into MEMW, MEMR, IOWR and IORD. My idea is to use a 74LS156 (U16-A4c), a 3-to-8 demultiplexer with OC-outputs.
R/W IOR IOW | function
---------------+----------
0 0 0 | IOWR
0 0 1 | MEMW
0 1 0 | IOWR
0 1 1 | MEMW
1 0 0 | IORD
1 0 1 | IORD
1 1 0 | MEMR
1 1 1 | MEMR
The OC-outputs enables us to "AND" the outputs with the same functions without using extra gates. Disable the 156 and it behaves like it was tristated which enable us to perform DMA-transfers as the 8237 must be able to control these lines as well. Pull-up resistors make sure of valid levels in all other situations (not drawn).
* to make sure that the signals are strong enough to drive all the busses
* adapt the timing for DRAMs
* it enables us to disable these signals towards the ISA-bus when:
* MASTER becomes (L)
* the C64 needs to access the 8237's
* reading/writing the D8..15 latch after a 16-bit I/O-access
This is made possible by enabling the 541 through a 4-input NAND-gate, U39a-C4. This NAND-gate is fed by the output of U37d (8237 access), the output of U3c (16-bit DMA), MASTER and the RAS-signal (U8b, see REFRESH).
The Control Register
This register supplies some bits for special purposes:
* bit 0: WINDOW: I/O- or memory-transfer, read (I/O = 0)
* bit 1: WINDOW: I/O- or memory-transfer, write (I/O = 0)
* bit 2: DMA-source (C64 = 0)
* bit 3: DMA-destination (C64 = 0)
* bit 4: 8/16 bits DMA transfer (8 bit = 1)
* bit 5..7: future use
Again I use a pair of 573's, U9-B6 and U10-B6.
U9: C64 -> store
Clock: I/O - write "0w"
Output enable: (L)
U10: read stored value
Clock: (H)
Output enable: I/O - read "0r"
The REFRESH
This line is used to signal an ISA-card that there is a valid refresh address on the bus. The IBM-AT uses a 74LS590 to do the job. Unfortunately the 590 is difficult to get. But some 393-counters plus buffers will do as well.
AFAIK the PC only refreshes A0..A7 which is enough for the 41256 (256K*1) or equivalents. But this is not good enough for the 44256 (256K*4) and bigger. IMHO it won't hurt the bus to expand the addressrange up to A11 which even allows us to refresh 16 MB modules! Three 393's, UA35a, UA35b and UA36a, all in C2, provide A0..11. These lines are buffered through two 541's, U34 and U38, which are enabled by PHI2.
The idea is that the 4 MHz signal clocks the state of (the inverted) PHI2 into the 74. The 74 is reset when needed by (the inverted) PHI2. Both signals will look like this:
__ __ __ __ __ __ __ __ __ __
Dotclock: | | | | | | | | | | | | | | | | | | | |
__| |__| |__| |__| |__| |__| |__| |__| |__| |__| |__
_____ _____ _____ _____ _____ __
4 MHz: | | | | | | | | | |
|_____| |_____| |_____| |_____| |_____|
_____ _______________________
PHI2: | | |
|_______________________| |________
___________ _____________________________
Output /Q: | | |
(REFRESH) |_________________| |__
_____________________________ ________
Output /Q: | | |
(ACCESS) _____| |_________________|
Remark:
* REFRESH is the negative output of the 74, U8a, fed by the inverted PHI2
* ACCESS is the negative output of the 74, U8b, fed by PHI2
__ __ __ __ __ __ __ __ __ __
Dotclock: | | | | | | | | | | | | | | | | | | | |
__| |__| |__| |__| |__| |__| |__| |__| |__| |__| |__
_____ _____ _____ _____ _____ __
4 MHz: | | | | | | | | | |
|_____| |_____| |_____| |_____| |_____|
_____ ___________ ___________
PHI2: | | | | |
|___________| |___________| |________
___________ _________________ _________________
Output /Q: | | | | |
(REFRESH) |_____| |_____| |__
_________________ _________________ ________
Output /Q: | | | | |
(ACCESS) _____| |_____| |_____|
The signal coming from output /Q named ACCESS is used in the first place to steer any memory-transfer where the ISA-bus is involved: this signal ensures us of enough recovery time after a refresh. This is realised by using this signal to steer U39a, the 541 that buffers the ISA-bus read- and write-lines.
The Interrupts
The ISA-bus outputs 11 active (H) IRQ-signals. The C64 can only handle one active (L) signal. The transformation is done by two 5-input NOR-gates, U50a-D3c and U50b-D4d, an inverter U32f-D3c and a 3-input NAND-gate, U46b-D4c. We also feed the IRQ's to two 541's, U48-D4d and U49-C4b, so the C64 can check which IRQ actually triggered the circus.
To make sure that unused lines aren't treated as an interrupts, we'll pull the inputs low using 3K3 resistors.
Remark for myself: what about using 74HCT541's? Maybe I then can increase the value of the resistors.
This register, U49, reads the state of the first eight available IRQ's: 3, 4, 5, 6, 7, 9, 10 and 11.
Output enable: I/O - read "4r"
This register, U48, reads the state of the last three available IRQ's: 12, 14 and 15. The other 5 bits are reserved for future use.
Output enable: I/O - read "5r"
The other lines
IOCHCK
The input IOCHCK is connected to the NMI-input.
This signal tells the CPU to stop. The 6510 has an input for this purpose but unfortunately this input is not provided at the expansion port. Except some cards I built myself, I don't know any commercial card using this line.
The 6510 has no equivalent at all for this input. In fact it is running at "zero wait states" all the time. So we simply do nothing with this input.
I have no idea which cards use this input. For the moment I use PHI2. If needed one can use the DOT-clock.
This output tells the card when the address is valid. Therefore we'll feed this output with PHI2.
This signal is, AFAIK, only used by the CGA-card. We'll feed this output with a signal from a 14.318 MHz crystal-module.
This line will be fed by the C64's RESET-signal through an inverter, U11b-B8a.
A (H) during an IORD or IOWR signals that not an I/O-operation but a DMA-transfer is going on. So when no DMA is in progress, this line remains (L). It is connected to the HLDA-output of the master-8237.
To be connected to the 8237's.
Signal to gain complete control of the bus during a DMA, a card can pull this line (L). Used to disable all bussignals. For the moment I don't know of any card capable of using this line.
Used IC's, codes etc.
code: part: type: function: place:
U1 8237 DMA A4a
U2 8237 DMS B4a
U3 abcd 32 4*2-OR A5d, A7c, C1c, B6a
U4 245 bi-directional buffer B7c
U5 573 8-bits latch B3c
U6 abcd 00 4*2-NAND A6a, A6a, B3a, B7a
U7 573 8-bits latch A3c
U8 ab 74 D-flip-flop C3c, C4d
U9 573 8-bits latch B6c
U10 573 8-bits latch B6d
U11 bcdef 04 6*inverter B8b, B8b, C8c, C7, C7b
U12 573 8-bits latch D2c
U13 573 8-bits latch D2d
U14 573 8-bits latch D2b
U15 573 8-bits latch D2a
U16 156 3-to-8 demultiplexer, OC A4d
U17 573 8-bits latch A8
U18 245 bi-directional buffer A8
U19 ab 86 4*2-EXOR A7a, C1c
U20 abc 11 3*3-AND B7c, D6d, C4a
U21 ab 06 6*OC-inverter B6a, D5
U22 abcd 02 4*2-NOR C6b, B6b, B6b, D5d
U23 ab 260 2*5-NOR C6b, D7b
U24 573 8-bits latch C7c
U25 573 8-bits latch C8d
U26 abcd 08 4*2-AND D7d, C7b, D5d, C1
U27 573 8-bits latch D8d
U28 573 8-bits latch D7c
U29 ab 139 2-to-4 demultiplexer A6c, A6c
U30 573 8-bits latch C8b
U31 573 8-bits latch C7a
U32 abcdef 04 6*inverter C7d, C6d, C5d, D3d, A2b, D1b
U33 ab 27 3*3-NOR D6d, D6b
U34 541 8-bits buffer C2b
U35 ab 393 4-bit binary counter C2a, C2a
U36 ab 393 4-bit binary counter C2a, C4d
U37 abcd 08 4*2-AND B1a, B7a, A5a, B3d
U38 541 8-bits buffer C2b
U39 ab 20 2*4-NAND B3a, C4d
U40 ab 74 D-flip-flop D1, D1
U41 abc 00 4*2-NAND C5d, D7d, D4c
U42 abcd 32 4*2-OR A2c, A2c, A2d, A2d
U43 abc 125 4*buffer A5a, B1a, B1c
U44 154 4-to-16 demultiplexer A6a
U45 a 139 2-to-4 demultiplexer D7d
U46 abc 10 3*3-NAND C7d, D4d, B7
U47 ab 260 2*5-NOR C6b, D5b
U48 541 8-bits buffer D4d
U49 541 8-bits buffer C4b
U50 ab 260 2*5-NOR D4d, D3d
U51 abcd 08 4*2-AND A5a, A5c, A5c, D5b
U52 a 32 4*2-OR B5c
U53 abc 02 4*2-NOR C6d, C5b, A2b
U55 a 260 2*5-NOR D6b
U54 abcd 32 4*2-OR C5b, A2a, A2c, A2b
U56 a 08 4*2-AND D5d
U57 541 8-bits buffer A2a
Not used:
00 - 1
02 - 1
04 - 1
06 - 4
08 - 3
27 - 1
32 - 3
86 - 2
125 - 1
139 - 1
260 - 1
Practical remarks
As you can see, only one AND-gate is used of U56. It seems very logical to replace this AND- gate by a NAND-gate and an inverter which are left over.