Zork On Esp32 With Vga, Ps2 And Sd

About the project

The 80's are back in text.....

Project info

Difficulty: Moderate

Platforms: ArduinoSparkFun

Estimated time: 3 hours

License: GNU General Public License, version 3 or later (GPL3+)

Items used in this project

Hardware components

Breadboard (generic) Breadboard (generic) x 1
Flash Memory Card, SD Card Flash Memory Card, SD Card x 1
SD breakout board SD breakout board x 1
10uf capacitor 10uf capacitor x 1
Capacitor 100 nF Capacitor 100 nF x 1
Through Hole Resistor, 150 ohm Through Hole Resistor, 150 ohm x 1
Resistor 1k ohm Resistor 1k ohm x 4
Through Hole Resistor, 270 ohm Through Hole Resistor, 270 ohm x 4
female ps2 connectors female ps2 connectors x 2
sound plug sound plug x 1
SparkFun Logic Level Converter - Bi-Directional SparkFun Logic Level Converter - Bi-Directional x 1
Audio / Video Cable Assembly, VGA Plug Audio / Video Cable Assembly, VGA Plug x 1
ESP32 lolin ESP32 lolin x 1

View all

Software apps and online services

Wire Stripper & Cutter, 26-14 AWG Solid & Stranded Wires Wire Stripper & Cutter, 26-14 AWG Solid & Stranded Wires
github - source code github - source code
Arduino IDE Arduino IDE

Hand tools and fabrication machines

Soldering iron (generic) Soldering iron (generic) x 1

Story

How about a game of ZORK on esp32, using PS2 keyboard and a 14 inch VGA screen?

For thus of you who never played Zork, it’s a text based quest, came out in the early 80’s.There is no graphics, and you type in your commands. It used Z-machine instructions as the game data, there for allowing any machine with Z-interpreter to play.

This project actually started 9 years ago, when I came across this post on the Arduino forum http://forum.arduino.cc/index.php?topic=100743.0The user Louis Davis did an amazing work of taking an existing Z- interpreter and make it work on an Arduino mega with SD card.

The thing was, that you had to connect it to a computer and play over the serial monitor.

So I made several attempts to make a standalone unit. Adding a PS2 keyboard was easy, but finding a proper output, and get it all to work together was too complicated, and I just stopped trying at some point. Fast forward to few weeks back, I came across another amazing work, which is the fabGL library for ESP32. http://www.fabglib.org/index.htmlAllowing you to basically turn the ESP32 into a small computer, with SD, PS2 mouse and keyboard, sound engine and the cherry on top – VGA output!

Getting the space invaders example working on the VGA screen with sound was surprisingly simple and defiantly it was fun playing. Now all that retro, brought Zork back to my mind and then It clicked – I can finally get that project I was dreaming of to work. I forked the original project and spend 2 days in finding the right combination of libraries and settings to get the code to compile and work. If you want to try it out https://github.com/talofer99/AZIPThe next step, which forced me to make more adjustments, was to make it work on the ESP32. Now since I have made several attempts in the past, some of the code adjustments were ready for me, just had to copy them from old projects I kept (lucky me).I was thrilled when I first got to play it on the new setup.Now let’s go over the setup.I took a VGA cable cut off one end and wired it up to a breadboard friendly connector. I used this schematics to figure out the pin out http://www.fabglib.org/conf_v_g_a.htmlI used the 8 color setup, with one pin for each color, connected via a 270Ohm resistor.

I used PS2 with Arduino in the past, so I had a pair of female connectors with pin breakout ready. I use a logic level convertor, since the PS2 is 5V and the esp32 is 3.3V. And added 1K pull-ups on the 3.3V side.

You can use the schematics on the site, to help you with the PS2 connector pin out.http://www.fabglib.org/conf_p_s2.html

The SD card is connected to the spi bus. And last and not least is the sound, which here I used a cut cable for.

You can use this schematics to set it up http://www.fabglib.org/conf_audio.html

When it comes to the code, I took the original AZIP and added the fabgl on top of it, I do want to point out a few thigs

The SD CONFIG #define SD_CONFIG SdSpiConfig(SS, SHARED_SPI, SD_SCK_MHZ(16))Without this I could not get the code running, but with other SD or breakout this might have to adjust this. I adjusted the VGA pinout to free the 2 SPI pins for the SD, that the original setup was using and this is my pinout displayController.begin(GPIO_NUM_21, GPIO_NUM_22, GPIO_NUM_4, GPIO_NUM_17, GPIO_NUM_15);The PS2 mouse and keyboard are left on their original pinout. The processreadfromsd takes care of the output.

void processreadfromsd(int c) {
Serial.write((int) c);
//(n) new line && end of lettersInRowCounter with space
if (c == 10 || (c == 32 && lettersInRowCounter >= 71))
{
Terminal.write('r');
Terminal.write('n');
//Serial.println(lettersInRowCounter);
lettersInRowCounter = 0;
}
// not sure why 13 was used in palces but we ignore it
// 91 & 93 are [] which comes aroudn system command
else if (c == 13 || c == 91 || c == 93)
// DO NOTHING
Serial.println("IGNORE ....");
else
{
l ettersInRowCounter++;
Terminal.write(c);
}
}

I added a limit of 71 craters per line, ignore a few craters that exists in the system replay like when you type something that system do not recognize.

The processpromptline is responsible of the input from the keyboard.

// Process prompt line
String processpromptline() {
// set color to green
Terminal.write("e[32m");
String returnPromptline = "";
// read the next key
char ckb = 0;
// start do (while ckb != '/n')
do {
// if KB avilable - process it
if (Terminal.available()) {
// we delay the GLCD so the read will be betetr
ckb = Terminal.read();
byte availableInTerminal;
Serial.write((int) ckb);
boolean addAndPrint = false;
switch (ckb)
{
case 17: //scroll lock on
case 19: //scroll lock off
case 9: // tab
//DO NOTHING --> THIS IS IGNORE LIST
break;

case 27: // ESC
availableInTerminal = Terminal.available();
if (availableInTerminal)
{
Serial.println("availableInTerminal = " + String(availableInTerminal));
ckb = Terminal.read();
Serial.print("NEXT READ IS - ");
Serial.println((int) ckb);
//command ([)
if (ckb == 91)
{
Serial.println("YES It is a command ");
for (uint8_t i = 1; i < availableInTerminal; i++)
{
ckb = Terminal.read();
// its up arrow and nothing else was typed
if (ckb == 65 && returnPromptline.length() == 0 && lastPromptline.length() > 0)
{
Serial.println("UP ARROW");
returnPromptline = lastPromptline.substring(0, lastPromptline.length() - 1);
Terminal.write(returnPromptline.c_str());
}//end if

} //end for
}
// F1->F4
else if (ckb == 79)
{
ckb = Terminal.read();
Serial.print("F");
Serial.println((byte) ckb - 79);
}//end if
}
else
{
Serial.println("JUST ESCAPE!!");
}//end if
break;

case 127: // back space
// remove the last carecter typed
if (returnPromptline.length() > 0)
{
// remove carecter from the prompt line
returnPromptline = returnPromptline.substring(0, returnPromptline.length() - 1);
// set the courser back
Terminal.write("be[K");
}
break;

case 13: // enter
addAndPrint = true;
Terminal.write("rn");
break;

default: // all rest
addAndPrint = true;
break;
} //end switch

if (addAndPrint)
{
returnPromptline += ckb;
Terminal.write(ckb);
}


}// end if
} // end while
while ( ckb != 13 );

// set color back to white
Terminal.write("e[97m");

// save last
lastPromptline = returnPromptline;

// return prompt line
return returnPromptline;
} // end String

I had to add some logic, to process all the possible keys.for example I ignore scroll lock and tab as you can see here.

All the special keys start with ESC, some have square bracket right after, like the arrow keys and some have other special keys like the F keys. I used the up arrow key to allow you to get the last line typed – make life easier when you play. And here you can see the implementation of backspace.

The next big thing I did in the code was to allow to choose a file from a list. I changed the original GAME.DAT file to its proper name and downloaded 2 more zork games. The getFileName will return the name and length of the file, and true as its value if the file is not a folder and it’s not the memory game file.

boolean getFileName(SdFile *file, char * fileName, byte *fileNameLen)
{
// get name & short name (no .DAT)
*fileNameLen = file->getName(fileName, 15);
if (!file->isDir() && memcmp(fileName, MEMORY_FILE_NAME, sizeof(MEMORY_FILE_NAME)))
return true;
else
return false;
}

This was used in both the list of the files, and then to get the right selected file to open for the game.

The code is available AT https://github.com/talofer99/AZIP_ESP32

There are a few more things I would like to add, and I would probably make a proper board for it, so stay tune for updates.

Schematics, diagrams and documents

fritzing_bb_M8rg33tnw0.png

Code

Github

Credits

Photo of talofer99

talofer99

Maker @ heart

   

Leave your feedback...