VGA adapter internals.
The important feature is to have
a 25 MHz pixel clock to make LCD monitors relatively happy.So we need
to rescan the video at a rate not related to the Apple bus clock. One
solution is to scan a line at the Apple rate into a FIFO and display it
at a different rate while capturing the next line into a second
FIFO. Sinse we have a lot of RAM on board another approach that I
actually use is to just shadow every byte written into the Apple's
video RAM and scan it at whatever rate we like. Then again we have 256K
of RAM. Why limit ourself to the video buffer? We shadow every byte
written by the 6502 unless we have a better use for the RAM. What for?
For example to be able to make snapshots of the memory to the MMC
card. Of course we can emulate a big RAM card if we want and we have a
lot more memory for that.
MMC/SD interface
The second part of the default
configuration is a storage device using the MMC/SD flash memory card.
At the very basic level there is an SPI controller that can be directly
driven by the Apple's CPU. A byte written to a card's i/o address
starts an 8 bit SPI transfer. Then read back the result. This is good
for non-standard commands but too much trouble for a basic block read.
So there is a state machine that uses a 24 bit block address and
requires a single start command to read a 512 byte block into internal
memory on the CB card. The block size on an MMC card happens to be the
same as the ProDOS block, so a ProDOS driver would need to write 2
bytes of the address ( the high bits will always be zeros) and start
the transaction. The memory buffer is mapped into the $C800-$C9FF
space that becomes visible on the Apple's bus after accessing $CFFF to
turn of any competition and reading from the $CXyy space belonging to
CB's slot. This way a block of data can be read extremely fast. The
problem is that only the first 32MB can be addressed directly. But wait
- don't we have another register that holds the high order bits? Sure,
but somebody needs to set them up. I'm sure somebody will write a front
end application for the 6502 to do that. So far my default
configuration uses a different approach.
Soft Z80
I hate to waste resources so
seeing only a tiny fraction of the FPGA used was somewhat painful. So I
added a little functionality in the form of a soft CPU - a free
implementation of a z80 core - TV80. It's happy to run at 50 MHz.
Currently it is mostly used to initialize the MMC/SD card (instead of a
6502 program doing it) and allow the user to select an image to use.
That is a NIB image. After one is selected the combination of hardware
and code emulates a floppy. Just like the PseudoDisk.
One problem is that the programs runs on the soft CPU that has no
direct input devices. At least none present in every Carte Blanche. My
interim solution is watching the $C000 address on the bus and react to
some keyboard codes. For example if no disk is found after reset Apple
will sit in a loop waiting for keyboard input to the BASIC interpreter.
Z80 looks for left and right arrows and the ENTER key. Apple doesn't
mind these codes - it just opens another line with a "]" prompt. So if
we press the right arrow twice and then hit ENTER the third NIB image
from the FAT formated MMC/SD card will be selected. While this happens
the VGA shows the directory of the SD card with the current choice
highlighted. After the ENTER key is hit the screen changes to a copy of
the Apple screen. So is CB is in slot 5, you can type PR#5 and boot the
virtual floppy.
This is far from perfect. I'm open to ideas on how to improve it. Keep
in mind that we have a block of memory visible in the $CXyy and
$C800-$CFFF space that is preset at boot and can be rewritten from the
z80 side. For example if a file with a certain name is found on the
flash card it can be read into this space.
3 modes of "disk" operation
I have added a third and probably
most useful mode to the "disk" on SD card emulation - a file under
FAT16 emulating a ProDOS volume. So here is the summary of all the
modes.
1. No FAT is detected on the flash card. A raw ProDOS volume is
assumed. The soft z80 is not controlling the MMC/SD interface any more
and a 6502 can access the interface directly.
2. A FAT is detected and an image that does NOT have a .NIB extension
is selected. This is almost like the previous raw mode but the offset
of the file is added to whatever the 6502 is setting. So if there is a
file that is an image of a ProDOS volume on the card it will be used.
3. A file that has a .NIB extension is selected. Apple will see a DOS
3.3 ROM in the Cx00 space and z80 will monitor the "track" selected by
the 6502. The whole track is read into a buffer and each byte in
sequence will appear at the $C0XC address. Read only.
Last but not least the 6502 can use the SPI interface directly to send
arbitrary commands to the MMC/SD card. For example to read a huge file
as a stream.
Debug additions
While waiting for the production Carte Blanche to be ready I have added
some debugging capabilities to the board. I have borrowed parts of a
z80 monitor machine language code and added that with some
modifications to my code. A soft UART is used to communicate with it. A
$18 USB/serial cable (at 3.3V levels) is used to connect to a PC.
Only a few commands are implemented: dumping memory and reading and
writing the i/o regsters, but this is quite useful. Apart from being
able to debug the z80 program itself, the top 32K is mappped to the low
32K of Apple's memory. So you can examine say the zero page while Apple
is running.

The most useful new feature is a hardware breakpoint. The idea is very
simple: a pair of registers writable by the z80 contains an address in
Apple memory space. When a byte is fetched (by 6502) from this address,
INH signal disables the memory and a $00 (BRK) is fed to the bus
resulting in a break into the monitor even if the address is in the ROM
area. Even if Apple writes over this address. For example set the break
at $0801 and boot a floppy. This will cause a break after reading the
first sector. Yet another new feature is the trace buffer.
Another pair of registers holds a trigger address. All the bus
transactions are written into a circular buffer. After the tigger
address is hit you can watch 511 things that happened before it did.
For example this is extremely useful if you need to understand what
code writes over some memory. My current implementation is rudimentary
but there is plenty of unused logic to play with if needed.