S100 Computers

HomeS-100 Boards HistoryNew BoardsSoftwareBoards For Sale
ForumOther Web SitesNewsIndex   
  
An S-100 Bus Dual SD Card Board.  
  Dual SD card Board
Introduction
Our Dual IDE/CF card board first done back in 2011 has turned out to be a popular and reliable board. Over 300 of them have gone out world wide over the years.
However CF cards are now not often used these days. more common are the smaller SD cards.  This board utilizes two SD cards instead of two CF cards.
The board is driven by an onboard ESP32 S3 module.

Instead of the now rare HEX LED displays used on the CF Card Board, I have switched over to the now common OLED displays to show CPM Track/Sector etc.
Like our FPGA disk controller boards (e.g. the FPGA_DC Boards) all access to the SD cards is done via a few one byte commands sent to the board.
These commands are processed by the ESP32 and send/receive sector data to the S100 bus over two parallel ports. 
The two ports are completely configurable to any 8 or 16 bit port number with two dip switches. The default ports are status
80H and data 81H.
The following one byte commands control all SD card R/W sector access:-

CMD$INIT$DRIVE_A EQU 80H             ;Initilized drive A:
CMD$INIT$DRIVE_B EQU 81H             ;Initilized drive B:
CMD$SEL$DRIVE_A EQU 82H              ;**** (Re)select an already initilized drive A:
CMD$SEL$DRIVE_B EQU 83H              ;**** (Re)select an already initilized drive B:
CMD$SET$TRK$SEC EQU 84H              ;Set new current TRACK+SECTOR on current drive (new)
CMD$READ$SECTOR EQU 85H              ;**** Read data from the CURRENT sector (on current sector, drive).
CMD$WRITE$SECTOR EQU 86H             ;**** Write data to the CURRENT sector (on current sector, drive).
CMD$FORMAT$SECTOR EQU 87H            ;Format the CURRENT sector with E5's.
CMD$RESET$ESP32 EQU 88H              ;Reset the ESP32 CPU

It should be noted that we are accessing the sector directly. There are numerous examples of reading and writing to SD cards using MSDOS file names.  However such an arrangement really complicates
things for CPM. It would require a n MSDOS "disk file" be setup on the SD card and 512 byte sections of that file would be accessed using a file pointer.
Instead we directly access the sector using the Arduino ESP32 functions SD.readRAW(sector_buffer, sector) and SD.writeRAW(sector_buffer,sector).

Currently the software assumes up to 0FFH CPM like Tracks and 0FFH CPM like Sectors. These are converted into actual SD card sectors.

ESP32 Module

The ESP32 modules we will use need to have plenty of GPIO pins since we need two parallel ports and status lines to and from the S100 bus.
Here are two examples. The first is a common Chinese "generic" version on Amazon.  The second is the well known Waveshare company module:-
    
  ESP32-S3
   
  ESP32 module 2

There are quite a few others on Amazon etc. but it is very important to be sure the same GPIO pins on these modules (which are the same) is exactly the same on any other ESP32 module
you use (or else alter the Arduino.ino file, see below). Here are diagrams of the pinouts of the two modules I used here:-
 

  ESP32 S3 Pinout
   
Waveshare Pinout

Note the actual modules on the board are upside down for convenient access to the USB at the top for programming/monitoring  the module.
In the case of the dual USB port module the USB port on the LHS is used for programming.

OLED Module

The are numerous small OLED displays these days. I use here a 1.54 Inch OLED Module 12864 128x64 SSD1309 LCD Display. It is available from a number of suppliers.  I got mine from Amazon.
The ESP32 uses an I2C interface using pins
GPIO9 and GPIO8.  Programming these displays is quite complicated. Fortunately there is an ESP32 Arduino library to interface the units and send ASCII text.

OLED Picture
 
There seems to be a two types where the four connecting pins are GND,VCC,SCL,SDA and VCC,GND,SCL,SDA.  There is a jumper on the board (P3) to accommodate the power for both types. 
Check the voltage on pins P1 and P2 and jumper P25 to match. Only then add the OLED unit.

Note there are also other similar size OLED displays that have more pins and are not I2C driven.  They will not work with this board.
There are however larger versions of the 4 pin OLED displays. I have had mixed results getting these to display text completely correctly.

SD CARD Modules

The SD cards themselves are controlled by SD card adaptors.
There are two popular types shown here that are well tested on previous S100Computers boards. The interface will be described below.
  
SD Card Asdaptors

The Adafruit has a 5V to 3.3V level shifter so it could be run with just a 5V interface. However the SparkFun does not. All its pins must be 3.3V.
For safety we will drive both boards using
74VLC245 voltage level shifters.

The rest of the boards components are standard -- available from Jameco, Mouser, Digi-Key etc. For prototype boards I generally use "double swipe" IC sockets.
For a critical board like this I prefer to use "Machine Tooled" IC sockets.  However they are more expensive and you have to be particularly careful not to bend the actual IC pins. 
There is a slight complication for the ESP32 module socket.  The spacing between the two rows of 22 pins for both units above is slightly different.
The Waveshare module pins are about 0.1" closer.  So depending on your module use the appropriate board pin holes.
 
   ESP32 Sockets

  
You can either solder the ESP32 module directly to the board or cut to size single row 0.1"" sockets such as the Jameco #2168173 socket
.
          
  Socket

Board Construction

There is nothing particular special about constructing this board.  Use the normal construction process we have in the past. Be careful with LED and electrolytic cap orientations.
The longer lead goes into the square hole. Check also the orientation of resistor networks.
 
The Pololu 5V exist in a few forms. While the older ones (D24V25F5) are still available and uses P6, it seems Pololu is suggesting users use the newer D24V22F5
(5V, 2.5 Amp) units, it has a different pinout, use this one in P4. 
The ESP32 S3 module supplies enough current to drive all the 3.3V board chips.
   
Without any chips on the board pop the board into the S100 bus and check you are getting 5V going to each IC socket that uses 5V.
If this is OK install all IC's to the board, including the ESP32_S3 module  (in sockets
P2+P5). As mentioned above, there is a second socket P15 to accommodate some ESP32 modules that have a slightly wider spacing. (P15 + P5).

Note the ESP32-S3 is mounted on the board upside down so the USB port connection is easily available when the board is in the S100 bus.

Jumper K1 1-2 for an 8 bit port address. Set the two dip switches as shown here to select ports 80H and 81H.
  
  Dip Switches

  
Jumper P1 1-2 and 3-4 (vertical).
The most common OLED displays use P3 4-6 and 1-3.
K2 (board reset), is normally is 1-2.
For the SD cards jumper P5 1-2, 3-4,5-6 and 7-8.


How the Board Works
Before going further open the board schematic (see below) and study it to understand how the board works.

In summary:-
In the default configuration the four S100 bus ports are
80H (IN & OUT) for status bits and 81H (IN & OUT) for data ports.
 
   Scamatic 1
       

Because the ESP32 CPU is running at 120MHz data transfer and handshaking have to be bullet proof and simple!
 

Whenever the S100 bus writes a byte to port 81H the bus D00-D07 lines are latched into U9.
At the same time S100_WRITE_ENABLE  clocks the Flip Flop U6A which ends up as a high on the ESP32 GPIO20 pin.
The EESP32 continuously looks at GPIP_20. If it is high it realizes S100 bus data is available.
After the ESP32 reads the 8 bits on U8 it pulses its GPIO_2 pin low. This resets the U6A Flip Flop and now lowers GPIO20 indicating to the ESP32 the byte of data has been read.
This process is done reputedly for all data coming to the ESP32.

Whenever the S100 bus reads a byte send by the ESP32 to port 81H the U15 input lines (GPIO_35-GPIO_42) are held steady with the 8 bits of data.
The ESP32 GPIO_1 pin also sets the Flip Flop U6B  which goes on to raise bit 7 of status port 80H (DI7) of U14.
Once the S100 bus sees this it reads the data from U15 (S100_READ_ENABLE). This also resets the Flip Flop U6B (using U17B).
The ESP32 also looks at pin GPIO_21 on U10 which tracks when the data on U15 was read by the S100 bus.
Another byte of data from the ESP32 is never sent until the previous byte was read by the S100 bus.


This handshaking has to be rock solid because every sector (512 bytes) of data passes back and forth between the SD card and the S100 bus.
Because the ESP32 has a clock of up to 240MHz even with the overhead of Arduino code (see below) the data transfer process is very fast.
Note no (slowing) resetting bits on the S100 bus side is required.

The "standard" Arduino/ESP32 SD file library unfortunately assumes only one SD card.  
We have two SD cards!  While we can use the SD card adaptor CS* pin to select each individual SD card module, after much testing of different approaches I concluded that the
MISO and MOSI
signals cannot be mixed going to the same ESP32 pins - even using separate SD card CS* signals.
We must hide from the ESP32 the fact that the are two different SD cards out there. 
This can be done with a tri-state chip like a 74LS125. Two gates going to the SD card (MOSI) and two gates from the SD card (MISO).
The complication is we have to shift the ESP32 3.3V signals up to 5V for the 74LS125, and back down again for the SD card.
We use the GPIO_47 pin to select either SD card path.

Here is a schematic of this approach:-
The two card MISO signals interfere with each other before going into the ESP32.
We get around this by isolating the signal from each card with the following circuit:-
   
  Two SD cards
 
The ESP32 GPIO_47 pin controls which card CS* is active and which card MISO gets to return data to the ESP32 via GPIO_12.

The rest of the board is fairly straightforward, The OLED display uses an I2C interface from the ESP32 (pins GPIO_8 and GPIO_9).
There is also a two "spare" four pin sockets for a second I2C device.
I had intended to add one of the Real Time Clock modules (e.g. the Adafruit, PCF8523), but this is not currently implemented.
 

The Arduino ESP32 Code

One very nice thing about these ESP32 modules is that the popular Arduino IDE interface can be used to program them.
With this board you need to configure the Arduino interface/tools/board to be an
ESP32 S3 Module.
Hookup USB connection to your computer. You need to also let the Arduino IDE which computer "COM" port is connected to your ESP32.
The Arduino code utilizes a few libraries and .h files:-
 
#include "FS.h"
#include "SD.h"
#include "SPI.h"
#include "Wire.h"
#include "Adafruit_GFX.h"
#include "Adafruit_SSD1306.h"
#include "SSD1306Ascii.h"
#include "SSD1306AsciiWire.h"
#include "fonts\TimesNewRoman16.h"
#include "fonts\X11fixed7x14B.h"

If your current Arduino IDE does not have them you need to download them from the web.
If you are not familiar with using the Arduino IDE and the ESP32 you should first see a few YouTube demos. For example here.
The one tricky part is writing code to read and write to the S100 bus 8 bit parallel ports. (The ESP32 does not have a direct port input).
Here is the code:-

void Output_PARALLEL_Port(char PARALLEL_value){
gpio_set_level(GPIO_NUM_42, (PARALLEL_value & 0x80) >> 7);
gpio_set_level(GPIO_NUM_41, (PARALLEL_value & 0x40) >> 6);
gpio_set_level(GPIO_NUM_40, (PARALLEL_value & 0x20) >> 5);
gpio_set_level(GPIO_NUM_39, (PARALLEL_value & 0x10) >> 4);
gpio_set_level(GPIO_NUM_38, (PARALLEL_value & 0x08) >> 3);
gpio_set_level(GPIO_NUM_37, (PARALLEL_value & 0x04) >> 2);
gpio_set_level(GPIO_NUM_36, (PARALLEL_value & 0x02) >> 1);
gpio_set_level(GPIO_NUM_35, (PARALLEL_value & 0x01));
gpio_set_level(GPIO_NUM_1,HIGH);                                               // pulse U15 LE high
gpio_set_level(GPIO_NUM_1,LOW);                                                // return U15 LE low
}


char Input_PARALLEL_Port() {
char PARALLEL_value;
PARALLEL_value =
digitalRead(GPIO_NUM_4) +
(digitalRead(GPIO_NUM_5) << 1) +
(digitalRead(GPIO_NUM_6) << 2) +
(digitalRead(GPIO_NUM_7) << 3) +
(digitalRead(GPIO_NUM_15) << 4) +
// (digitalRead(GPIO_NUM_16) << 5) +
(digitalRead(GPIO_NUM_19) << 5) +
(digitalRead(GPIO_NUM_17) << 6) +
(digitalRead(GPIO_NUM_18) << 7);
gpio_set_level(GPIO_NUM_2,LOW);                                                // pulse U15 LE high
gpio_set_level(GPIO_NUM_2,HIGH);                                               // return U15 LE low
return(PARALLEL_value);
}

Once setup, the ESP32 sits in a continuous loop waiting for 8 bit commands coming from the S100 bus. They are:-
 
CMD$INIT$DRIVE_A EQU 80H             ;Initilized drive A:
CMD$INIT$DRIVE_B EQU 81H             ;Initilized drive B:
CMD$SEL$DRIVE_A EQU 82H              ;**** (Re)select an already initilized drive A:
CMD$SEL$DRIVE_B EQU 83H              ;**** (Re)select an already initilized drive B:
CMD$SET$TRK$SEC EQU 84H              ;Set new current TRACK+SECTOR on current drive (new)
CMD$READ$SECTOR EQU 85H              ;**** Read data from the CURRENT sector (on current sector, drive).
CMD$WRITE$SECTOR EQU 86H             ;**** Write data to the CURRENT sector (on current sector, drive).
CMD$FORMAT$SECTOR EQU 87H            ;Format the CURRENT sector with E5's.
CMD$RESET$ESP32 EQU 88H              ;Reset the ESP32 CPU

 
Each command has a specific format of an initial 33H byte, then a one byte command listed above and for sector R/W the sector number (0-FFFFH).
The hardware above clocks each byte back and forth between the S100 bus and the ESP32/SD card.
The two byte command with the first byte always being 33H is for safety. You don't want  an noise/incorrect byte getting by.
I never had this problem here however.

S100 Bus Test programs

In order to test this board I have written two short Z80 programs (
SD_IO.Z80
&SD_CARD.Z80) to run at 100H in RAM.

SD_IO.Z80
Load SD_IO.COM (see below), to RAM at 100H with the Master monitor (see below), to RAM at 100H with the Master monitor "X" command.
Then type G100, you should see:
 
  IO Test Signon
   
If you type "A" you should see the following display:-
  
  IO Test Display

This program is using the two core routines on the S100 bus and ESP32 CPU called Get Data() and SendData().

SD_CARD.Z80

GET_DATA:
      IN    A,(SD_CARD_STATUS)     ;Wait for character (GPIO_3 and GPIO_21)
      BIT  7,A
      JR   Z,GET_DATA
      IN    A,(SD_CARD_DATA)         ;S100 read enable will reset U6B GPIO_21
      LD    C,A
 GET_DATA1:
      IN A,(SD_CARD_STATUS)       ;Wait for character (GPIO_3 and GPIO_21)
      BIT 7,A
      JR NZ,GET_DATA1
      LD A,C
      RET


SEND_DATA:
      IN     A,(SD_CARD_STATUS)     ;Wait until tany previous character has been read
      BIT    0,A 
      JR     NZ,SEND_DATA
      LD     A,C                    ;Send Data, this will raise GPIO_20
      OUT    (SD_CARD_DATA),A
SEND_DATA1:
      IN     A,(SD_CARD_STATUS)     ;Wait until the ESP32 has reset U6A with GPIO_2
      BIT    0,A
      JR     NZ,SEND_DATA1
      RET



ESP32_DUAL_SD_CARD.INO

char GetData(){                        //Get an S100 bus Command or byte
char data;
while(digitalRead(GPIO_NUM_20) == 0);  // wait for data
data=Input_PARALLEL_Port();            // get second CMD byte
return(data);
}


char SendData(char data){               //Send an S100 bus Data
while(digitalRead(GPIO_NUM_21) == 1);
Output_PARALLEL_Port(data);
return(data);
}



It is absolutly essential that this program dispays exactly the text shown above continously.
Do not go further until you have this program working correctly.


SD_CARD.Z80
Load SD_CARD.COM (see below), to RAM at 100H with the Master monitor (see below), to RAM at 100H with the Master monitor "X" command.
Then type G100, you should see:-
   
  SD Card Signon
  
Make sure  you have an SD card in the A: (LHS) Adafruit or Sparkfun adaptor and type
"A".
This will allow the ESP32 to determine the SD card type and initialize it.
You should see the following on the OLED display:-
  
  OLED Display 1

This indicates the ESP32 has Initilized its library to interact directly with sectors on the SD card.
Next enter the "R" command.
You should see the following on the OLED display:-
  
  OLED Display 2

and the actual 512 byte sector contents on the console.
   
  Sector Display

Hitting the space bar will display the next sector. Hit the ESC key to return to the main menu.

Note in the default configuration the board assumes a CPM disk of
0FFH CPM tracks with 0FFH sectors/track.

CPM likes to have its directory disk sectors Initilized with E5's.   The
"F" command will do this.
There is no need to format the whole disk, just enough for a disk directort table.
Here is a sector formatted with the "F" command.

 
 
  Formatted Sector
  

For any one SD card reading and writing sectors is fast and very reliable.  Switching from an A: SD card to one in the B: "Drive" sometimes is problematic. 
The initialization goes fine but reading a sector on the "new" drive sometimes fails. It's not clear to me why.
I find its best to issue a "reset" CMD (CMD$RESET$ESP32 EQU 88H) when switching drives.  
Currently I'm also using the same SD cards in both drives.
   
  Gladstone 8GB SD Cards
 
Notes:-
You may notice that the GPIO pin numbers for Set_PARALLELL_Port_Input are not contigous. The are 18,17,19,15,7,6,5,and 4
I found that the
GPIP_16 on the Waveshare board did not seem to work directly as a generalized input pin. I used pin 19 insted.
It seemed to work fine on the generic board however.

A Production S-100 Board.
Realizing that a number of people might want to utilize a board like this together with a group of people on the  Google Groups S100Computers Forum, "group purchases" will be made in the future. 
I dont want to put out the board until an SD card version of CPM3 for the board is written.
Please see here
for more information on older boards.
   
    
The links below will contain the most recent schematic of this board.
Note, they may change over time and some IC part or pin numbers may not correlate exactly with the text in the article above.
 
MOST CURRENT  SD CARD_BOARD SCHEMATIC     
          (V1.7     2/18/2025)
MOST CURRENT SD_CARD_BOARD  KiCAD Files                (V1.7    2/18/2025)
MOST CURRENT SD_CARD_BOARD GERBER Files               (V1.7    2/18/2025)
BOM for SD_CARD_BOARD                                            
      (V1.7    2/18/2025)  
 

ESP32_Dual_Core_Test.ino                                                 Code to program the ESP32 S3 Module to R/W SD card sectors           (2/18/2025)

ESP32_Dual_SD_Board.ino                                                 Code to program the ESP32 S3 Module to R/W SD card sectors            (2/18/2025)
SD_IO.Z80                                                         Code to test the board data handshaking on the S100 bus      (2/18/2025)
SD_Card.Z80                                                                       Code to Read/Write SD card sectors from the S100 bus                         (2/18/2025)
SD _Card _Z80.ZIP  Folder                                                  Complete Folder of Z80 files                                                                  (2/18/2025)


Other pages describing my S-100 hardware and software.

Please click here to continue...

This page was last modified on 02/14/2025