SD-card cartridge#
Introduction#
Perhaps the most elegant solution for loading CAS files into your P2000T is via a cartridge that hosts an SD-card slot. Even the smallest SD-cards have enough capacity to store the complete P2000T tape archive. Reading from such an SD-card is non-trivial and requires both a hardware solution to interface with the I/O port of the P2000T as well as a software solution to navigate through the files hosted on a FAT32 partition. This page explains both parts in more detail.
Note
The SD-card cartridge uses I/O port 0x60-0x6F
and requires a P2000T which has
this port available. There appear to be a number of hardware devices which also
use this port and these devices will be mutually incompatible with the
SD-card cartridge. For this reason, the newest variant of the SD-card (rev6 and
newer) uses I/O port 0x40-0x4F
.
Tip
This page contains a lot of technical details which can in principle be ignored by the non-interested reader. To get started working with the SD-card cartridge, the following sections are the most relevant:
There is also a manual available in Dutch which can be found via this link.
For a review on the SD-card cartridge, have a look at the video below:
Preparation#
The SD-card cartridge works very similar as compared to the data cartridge. A modified BASICNL cartridge is prepared that loads a LAUNCHER program from the ROM chip on the SD-card cartridge into memory. Next, this launcher program is executed which interfaces with the SD-card. To set-up this infrastructure, one needs to prepare a SLOT1 cartridge with the modified BASICNL rom and one needs to flash the launcher program on the ROM chip of the SD-card cartridge.
Note
If you have purchased an SD-card cartridge (kit), your cartridge set is most likely ready to use and this section is only relevant if you want to update the firmware.
Caution
The data cartridge also uses a modified BASICNL ROM, however this modified BASICNL ROM is a different one and the two types are not interchangeable.
Summarizing, the following is required to start working with the SD-card cartridge.
A SLOT1 cartridge with the modified BASICNL application.
A SLOT1 cartridge with the flasher application.
The SD-card cartridge with a FAT32-formatted SD-card and the launcher application flashed to its internal ROM.
Creating the SLOT1 cartridges#
First, we need to construct two SLOT1 cartridges, being the modified BASIC cartridge and the flasher. The BIN files for these two cartridges can be obtained via the links below.
Caution
Verify whether you have a 0x40
or a 0x60
version of the cartridge. The
0x40
cartridges are the newest and these cartridges have 0x40
written
on the case.
Latest variant 0x40
Deprecated variant 0x60
Upload these two BIN files to different ROM slots on the ZIF cartridge. A detailed guide on the procedure can be found on the page describing the ZIF cartridge.
Tip
Make a note which slot you have used for which cartridge file.
Flashing the launcher onto the ROM chip#
Next, we need to prepare an SD-card and place the LAUNCHER.BIN
on this SD-card
in its root directory.
Important
If the SD-card is not yet formatted, format the SD-card using a FAT32 filesystem.
The SD-card cartridge only works with FAT32 formatted SD-cards. When formatting the SD-card, it is highly recommended to use default settings for the cluster size.
Place the LAUNCHER.BIN file in the root directory of the SD-card. The latest version of the LAUNCHER.BIN file can be downloaded via the link below.
Caution
Verify whether you have a
0x40
or a0x60
version of the cartridge. The0x40
cartridges are the newest and these cartridges have0x40
written on the case.After downloading the file, do not forget to change the name to
LAUNCHER.BIN
, otherwise the flasher program will not recognize (find) the file.
Latest variant 0x40
Deprecated variant 0x60
Insert the SLOT1 cartridge with the flasher and the SD-card cartridge in
SLOT2 and turn on your P2000T. Hit any key to start interfacing with the SD-card
and search for a file called LAUNCHER.BIN
. If such a file is found, the file
will be copied to the internal ROM chip of the SD-card cartridge. Any previous
data corresponding to previous versions of the launcher will be automatically
wiped. After copying the file to the ROM chip, a CRC-16 checksum is automatically
generated and used for verification purposes.
Launching CAS programs from the SD-card cartridge#
Programs can be loaded from the SD-card and executed using the launcher. To load the launcher into memory, the P2000T requires a modified BASICNL cartridge (see the instructions under preparation on how to prepare such a cartridge) in SLOT1. This modified BASICNL cartridge will copy the launcher from the internal ROM chip on the SD-card cartridge to memory and run it.
Loading a program from the SD-card is fairly easy. There are only three commands
one needs to be know: ls
, cd
and run
.
cd
is used to navigate to (sub)directories.ls
is used to show a listing.run
is used to start a program.
Per directory, all items inside the directory have a number (called an index)
which starts at 1. On execution of a ls
command, these indices are shown. To
change directory, type cd
followed by the index number. For example, cd 4
will go to the fourth entry. If that entry is a subdirectory, the launcher jumps
to that folder. To run a program, one types run
followed by the number. For
example, run 107
will run the 107th entry in the folder. In the example shown
in Figure 6, this corresponds to the Tetris
program.
Note
Users familiar with Linux or MS-DOS operating systems are used to writing the actual filename or directory name when using commands such as
cd
andrun
. A different approach using indices was intentionally chosen to save upon typing but also to save upon more complex (and large in size) routines in the launcher application.The images as shown below might differ with respect to the newest version, the procedure however remains the same.
Tip
Instead of ls
, one can also run lscas
. The difference is that the latter
command will also read the headers of the .CAS
files and mention the name
of the tape and the number of blocks.
Once the CAS file is loaded into memory and executed, one will not return back to the launcher once the BASIC program is terminated. To restart the launcher, simply reset the machine.
Running PRG files from the SD-card#
Besides running CAS files, it is also possible to execute special .PRG
files.
These files are loaded from the SD-card and placed into memory at position
0xA000-0xDCFF
.
Important
.PRG
files will only work if one has at least a 16 KiB memory expansion.
In contrast to CAS files, upon termination of these programs you will return
to the launcher and one can open another .PRG
file or a CAS file. Launching
a .PRG
file is completely synonymous to launching a CAS file, simply use
run <index>
where <index>
refers to the index of a .PRG
file. The
launcher will automatically verify the integrity of the .PRG
file by
calculating its checksum. Only upon a valid checksum will the program start.
Currently, there are not many .PRG
programs made, but one program that is
potentially useful and relevant is CASDUMP.PRG
. This program allows the user
to copy files from the cassette tape to the SD-card. See
this section <#creating-copies-of-cassettes-tapes>
_ for more information.
Copying cassette data to the SD-card#
To copy the contents of a cassette tape to the SD card, first download the
program CASDUMP.PRG
via the link as shown below and place it on the SD-card.
Next, ensure a directory called DUMPS
exists on the SD-card and is located
in the ROOT directory of the SD-card. You will need to create this directory
on your computer or laptop. Be sure it says DUMPS
without any trailing spaces
and preferentially all in capitals.
Insert the SD-card into the SD-card cartridge and execute the launcher
application. Navigate to the folder containing CASDUMP.PRG
and run it
via run <index>
where <index>
refers to the index of CASDUMP.PRG
.
Upon execution, the Cassette Dump program will verify whether it can find a folder
called DUMPS
and if so, the cassette dumping routines can start. A number
of screenshots showcasing the process are provided in Figure 7.
At the very start, the program will ask the user whether they want to perform
cassette file extraction using automatic or manual mode. In the
automatic mode, the program aims to read the whole cartridge and copy over
any program it can find. In manual mode, for every file encountered, the user
will be asked whether they want to store the file. In Figure 7,
the process corresponding to manual mode is shown.
A CAS file has a 16 character description whereas the SD-card uses the old
MS-DOS 8.3-style notation. As such, Cassette Dump will take the first 8
characters of the 16-character description and checks the SD-card whether a such
a file already exists. If not, the cassette data is written to this file. If it
does exists, the program tries to rename the file by replacing the last
character by a 0
. If this file also exists, it will replace the last character
by a 1
, and so forth, until all options have been tried. Any invalid
characters, e.g. a question mark ?
will be automatically replaced by valid
8.3-style characters.
Tip
After copying a number of files over from cassette to the SD-card, it is highly recommended to rename your files and reorganize them in a different set of folders.
Cartridge commands#
Below, an overview of the commands is provided.
Command |
Description |
---|---|
|
List contents of current folder |
|
List contents of current folder, listing contents of CAS files |
|
Change directory |
|
Run .CAS file |
|
Performs a 120-byte hexdump of a file |
|
Provides location details of a file |
|
Performs a quick test on the read/write LEDs |
|
Show current position of the stack pointer |
|
Perform a 120-byte hexdump of main memory starting at |
|
Perform a 120-byte hexdump of cartridge ROM starting at |
|
Perform a 120-byte hexdump of cartridge RAM starting at |
Technical details#
Cartridge features#
In Figure 8, the schematic of a SLOT2 SD-card interface is provided. This schematic is fairly complex and we will break it down into several independent components.
Parallel to serial conversion#
Communication with SD-cards proceeds via the SPI protocol. The SPI-protocol is inherently a serial protocol, yet the Z80 uses a parallel interface. One way for the Z80 processor to communicate with the SD card is via a process called bit banging. This is rather inefficient as we basically use only 1 out of the 8 bits every time the Z80 executes an instruction. More efficient would be to use the full 8 bit databus every time an instruction is executed. To accommodate this, we make use of one parallel-in-serial-out (PISO) and one serial-in-parallel-out (SIPO) shift register (74HC165 and 74HC595, respectively).
When communicating with the SD-card over the serial connection, we need to be able to transfer or receive 8 bits of information in the same time it would normally take to complete one instruction on the Z80. Thus, the parallel to serial interface needs to operate at a much higher clock frequency than the Z80. In this implementation, we make use of a timer circuit driven by a 16 MHz oscillator (which frequency is halved via a 74HC163 counter chip) to drive both the shift registers as well as the CLOCK signal of the SPI interface. Herein, the shift registers are fed the inverted CLOCK signal which is delayed by half a clock cycle with respect to the regular CLOCK signal. The SD-card receives the regular CLOCK signal, ensuring that the SD-card is half a clock cycle ahead of the shift registers.
The SPI protocol is a synchronous protocol, so essentially bits of data are exchanged between the host device (master) and the SD-card (slave). In our implementation, this becomes effectively 8 bits or 1 byte at a time. To transfer a byte from the Z80 to the SD-card, first the byte is transferred to the PISO register after which a timer circuit is started that exactly emits 8 clock pulses, transferring the byte from the PISO register to the SD-card. Simultaneously, the SIPO register receives 8 bits from the SD-card and stores them. The Z80 can then retrieve the byte of data by reading from the SIPO register. This synchronous protocol implies that when the Z80 only needs to receive data from the SD-card, e.g. when it receives a block of data, it still needs to keep sending new bits of data to the SD-card. The elegancy of this design is that the Z80 can keep on doing stuff (e.g. loading the next instruction) while the SD-card circuitry is running in parallel.
Note
The implementation used in the schematic above is based on this source. It should be mentioned that a 74HC123 multivibrator has been added to add a little delay to avoid ‘double-triggering’ of the timer circuit.
I/O selection#
The SD-card cartridge hosts a number of devices and chips which all require
their own I/O address for efficient communication. By means of two 3-to-8
line decoders (74HC138),
the lower byte of the I/O address is used to interface
with at most 16 devices. Note that only 11 of the 16 lines are being used. All are wired
to I/O ports 0x60-0x6F
and the upper byte is being checked by means of a
74HC85
chip. An overview of all the port numbers is provided below.
Port number |
Function |
R/W |
---|---|---|
|
Serial read/write |
R/W |
|
Start clock circuit |
W |
|
Set MISO pull-up/down |
R/W |
|
Set/unset CS on SD-card |
R/W |
|
LEDs |
R/W |
|
not used |
N/A |
|
not used |
N/A |
|
not used |
N/A |
|
Lower address byte |
W |
|
Upper address byte |
W |
|
Bank register ROM |
W |
|
Bank register RAM |
W |
|
ROM chip |
R/W |
|
RAM chip |
R/W |
|
not used |
N/A |
|
not used |
N/A |
Interfacing with the devices proceeds via the
IN and
OUT instructions. For example,
to send 0xFF
to the SD-card and read back the resulting byte, one would use:
ld a,0xFF
out (0x60),a ; set all ones
out (0x61),a ; start timer circuit, value in register a is not used
in a,(0x61) ; receive result from SD-card
RAM and ROM chips#
The SD-card cartridge uses a SST39SF010 and a 62128 as the ROM and RAM chips, respectively. Both these chips have a capacity of 128kb. The ROM chip is used to store the launcher (more information on this later) and the RAM chip is used for intermittent storage of the CAS programs. As the I/O interface of the Z80 only allows us to use 8 bits at a time, the addresses for these chips are stored in two 8-bit registers (74HC273) and one 2-bit register (74HC74). The former two chips store the lower and upper byte of the address and the latter chip is used for banking. Each bank thus corresponds to 64kb of memory. Note that both the ROM and RAM chip share the 16-bit address registers which in practice does not result in any difficulties.
Reading from the ROM and RAM chips proceeds in a fairly equivalent fashion. The
bank is set by writing a 0
or a 1
to address 0x6A
for the ROM chip and
0x6B
for the RAM chip. Next, the 16-bit address is set by writing the lower
byte to 0x68
and the upper byte to 0x69
. After setting the address, one can
read from either 0x6C
for the ROM chip or from 0x6D
for the RAM chip. For
example, reading a byte at address 0xAA55
from the ROM chip would look as follows.
ld a,0x00
out (0x6A), a ; set first bank (bank 0)
ld a,0x55
out (0x68), a ; set address lower byte (0x55)
ld a,0xAA
out (0x69), a ; set address upper byte (0xAA)
in a,(0x6C) ; read from chip address 0xAA55
Writing to the RAM chip is fairly easy and only requires an OUT instead of
an IN instruction. An example where the value 0x00
is written to address
0xAA55
is provided below.
ld a,0x00
out (0x6B), a ; set first bank
ld a,0x55
out (0x68), a ; set address lower byte (0x55)
ld a,0xAA
out (0x69), a ; set address upper byte (0xAA)
ld a,0x00
out (0x6D), a ; store byte in A into RAM chip at 0xAA55
Writing to the ROM chip is a bit more involved and requires a sequence of instructions. More details on this sequence can be found in the datasheet of the SST39SF0x0 family of chips.
SD-card#
Using the hardware set-up as explained above, we can interface with our SD-card. Communication with an SD-card proceeds via sending a series of command words to the SD-card upon which the SD-card responds. These command words are sent via the I/O port.
The exact procedure is provided in the (official) document “Physical Layer Simplified” which can be found via this page. Referring to Figure 4-2 in this document, the process of initializing an SD-card such that we can read blocks from it goes as follows.
Set CS to low on the SD-card
Send at least 74 pulses of
1
to the SD-cardSend
CMD0
to the SD-card ({0|0x40,0,0,0,0,0x94|0x01}
) to put the card in SPI mode and check for a validR1
response.Send
CMD8
to the SD-card ({8|0x40,0,0,0x01,0xaa,0x86|0x01}
) to send the interface condition (argument0x1AA
) and check for a validR7
response.Send
CMD55
to the SD-card ({55|0x40,0,0,0,0,0x00|0x01}
) for an application specific command and check for a validR1
response.Send
ACMD41
to the SD-card ({41|0x40,0x40,0x00,0x00,0x00,0x00|0x01}
) to send the host capacity support information (argument0x40000000
) and check for a validR1
response.Keep on repeating steps 5 and 6 until a response of
0x00
is being received. This typically takes roughly 2-3 calls.Send
CMD58
to the SD-card ({58|0x40,0,0,0,0,0x00|0x01}
) to read the OCR register and read theR3
response.The card is now ready to receive
CMD17
commands which request 512 byte blocks from the card.
Tip
The whole procedure is quite involved and I can warmly recommend to read upon this excellent guide when one is interested to implement such an interface yourself.
FAT32 file system#
The SD-cards used for this cartridge utilizes the FAT32 protocol for file storage. To navigate through the folders and access the files, we need to understand how this storage protocol works. Luckily, FAT32 is relatively simple and well-documented. We will not provide a detailed explanation of the storage format (see also the tip below if one is interested), yet suffice with a brief overview of how files are retrieved from a FAT32 filesystem.
The first 512 bytes of the SD-card, i.e. the master boot record (MBR), is loaded. The MBR contains information on the partitions.
From the MBR, the logical block address (LBA) of the first partition is read.
From the partition table, the number of bytes per sector, the sectors per cluster, the reserved sectors, the number of FATs, the sectors per FAT, and the LBA of the root directory are read and stored for later use.
Once the LBA of the root directory is established, we can start reading the metadata that describes the files and subdirectories that reside in the root directory. For files, these provide links to the actual file data. For subdirectories, these provide a reference to where the metadata for the content that is stored in that subdirectory.
Based on the file metadata, a sequential list of clusters can be constructed which contain the actual file data. These clusters are traversed and all the sectors for each of these clusters is being read and transferred to the P2000T.
Tip
An excellent tutorial on the FAT32 storage protocol can be found here.
See also the Wikipedia entry on FAT32.
PCB lay-out#
Below, an image of the PCB (backside, unpopulated and populated) is shown. As can be readily seen, there is not much free space left on this PCB. At the center, one finds the SST39SF010 chip seated in a PLCC32 socket while directly above the 128kb RAM chip is found. On the right top, the 16 MHz oscillator is located. On the left top, a SD-card interface board is mounted. This board does the level shifting between the 5V lines on the PCB and the 3.3V lines on the SD-card. Directly to the right of the PLCC32 socket, the 2x74HC273 buffer chips are found which store the address bus for both the ROM and RAM chips.