A couple of years back, I stumbled upon an intriguing project created by  MCUHacker01, which involved an ESP8266 PC-XT Emulator. I was truly fascinated by the idea of a compact ESP8266 capable of booting up a DOS system. However, at the time, I was heavily occupied with other projects, so I made a mental note to return to this endeavor in the future.

I'm aware that numerous experts have already transitioned to using the ESP32 for PC-XT emulation, with some impressive projects that boast VGA output and increased speed. I intend to explore these projects in the future. However, for now, I'm content with making the most of the ESP8266 boards I currently possess and maximizing their capabilities.

A week ago, I came across a Hackaday.io article authored by Steve L, who had been actively working on the same project since 2018/2019. Seeing that the project was documented on Hackaday.io, I decided to share my own project here as well. I only wish I had discovered Steve's project logs earlier, as many of the challenges I encountered were thoroughly addressed in his project. It would have saved me a considerable amount of time spent troubleshooting. Nevertheless, I consider myself fortunate to have tackled these issues independently, which allowed me to gain a deep understanding of how an IBM PC-XT operates. In fact, I even managed to push the project a step further than Steve did.

The major breakthrough occurred when I incorporated the ESP-PSRAM64H SPI PSRAM into the project. However, due to the limitation of establishing communication between the ESP8266 and the PSRAM using a single-line SPI protocol, the RAM access speed was less than ideal. In comparison to the "standard" IBM PC-XT running at 4.77MHz, the final emulator's speed ranged from 30% to 80%, and in some cases even up to 90%. Since many games heavily rely on RAM access, they ran at a significantly reduced pace. Simpler games like PC-man and Paratrooper, which happened to be personal favorites from my childhood, ran nearly at full speed. Additionally, I managed to perform some tasks with Lotus 1-2-3. However, due to the ESP8266's limited GPIO availability, I couldn't emulate a printer port to produce printed output of my work.

Regarding the I/O ports, I successfully replicated a serial port using the two remaining GPIOs. Since there were no additional GPIOs available (though I briefly contemplated utilizing GPIO10), I had to forgo hardware flow control. Consequently, the sluggish emulator was only capable of receiving serial data at a rate of approximately 300 bits per second (bps). I established a connection between the Serial I/O port (3F8) and an ESP01s Serial Wifi Modem, crafted a basic terminal program with QBASIC, and managed to connect to several Bulletin Board Systems (BBSes).

An additional ESP01s was required to handle the WiFi connection since the primary ESP8266's WiFi function had been configured to operate in deep sleep mode. This adjustment was made because the ESP8266's WiFi radio consumed valuable heap memory, causing a slowdown in emulator performance and generating a high number of interrupts that significantly impacted video generation.

If you've successfully built a similar setup, I'd greatly appreciate your comments and feedback. I'm more than willing to provide assistance if you encounter any issues while attempting to get the emulator up and running. Furthermore, please feel free to share any code improvements you've made, as this collaborative effort can contribute to enhancing the overall project.

---   

Youtube Videos:

---

List of Features added:

- Added PSRAM as the emulator RAM.

- Disk image address change to 0x100000. Possible of a 1.44M or 2.88M floppy disk image;

- Video mode 2 and 3, 80x25 text mode: Text is still white, and I added a 1-bit background (black or grey);

- Added video mode 0 and 1, 40 x25 text mode: White text, 1-bit background (black or grey);

- Added video mode 4 and 5, 320x200: 2-bit greyscale;

- Added video mode 6, 640x200: B/W only;

- Included character ROM for ASCII 128-255;

- Added cursor display for video mode 0, 1, 2 and 3; 

- Reactivated built-in BASIC ROM;

- Enabled disk WRITE; 

- Added accurate timer ticks (18.2 ticks/second);

- Added PC speaker output (using PWM feature of EPS8266);

- Added PS2 Keyboard code;

- Added Serial port (COM1) emulation;

- Rewrote the MMU swap for the emulated RAM;

---   

RAM:

The introduction of PSRAM marked a pivotal moment in the project. While I couldn't procure the ESP-PSRAM64H, I managed to acquire some LY68L6400 chips (which I was informed were essentially the same). Initially, the addition of the PSRAM didn't result in a speed boost for the emulation, and I was on the verge of abandoning the project. However, after dedicating a significant amount of time to studying and experimenting with the SPI protocol, I successfully switched from the read instruction (0x02) to the faster read instruction (0x0B) and pushed the clock speed to the maximum of 80 MHz. The original read instruction (0x02) was limited to a maximum clock speed of 33 MHz. Furthermore, with the PSRAM in place, the emulator no longer required the ERASE instruction, prompting a rewrite of the entire MMU swap system with smaller page sizes. Following testing, a page size of 256 bytes proved to be adequate. I allocated 8 pages for read operations and 8 pages for write operations. These adjustments reduced heap memory usage from 16K to 4K, making extra memory available for video-related tasks.

Additionally, I came up with a clever idea: I successfully transferred the character ROM to the flash ROM, thereby freeing up an extra 2K of heap memory for experimentation. While reading the character ROM directly from the flash proved insufficient for the video generator's speed, the concept behind this approach was as follows:

  1. The character ROM is only required in text video modes 0-3, where a mere 4K of video memory is necessary.
  2. Upon transitioning to these video modes, the character ROM gets copied from the flash ROM to the video memory (specifically, screenmem[14336-16383]).
  3. The video generator can then efficiently retrieve the character ROM from its location in the video memory.
  4. When switching to video modes 4-6, the character ROM in the video memory can be safely discarded.
  5. Upon reverting to video modes 0-3, the character ROM is once again copied from the flash to the video memory (this is a one-time operation that is slower, taking approximately 1 second to complete), ensuring swift access for the video generator.

---

Video Generation:

MCUHacker01's work on the composite video generator was truly remarkable. I made every effort to fine-tune it, resulting in a slight speed boost for the emulator. The incorporation of a blinking cursor and a grey background in video modes 0-3 did introduce a minor slowdown in the code, but I deemed these features essential. The cursor is invaluable for indicating my current typing location, and the grey background proves indispensable in certain software applications, such as Lotus 1-2-3.

Please take note: When uploading the Arduino emulator code, it's necessary to disconnect the video filter circuit from GPIO3 (RX).

---   

Disk Handler:

The initial code consisted of the disk READ functionality but lacked the disk WRITE capability. I believed that including the WRITE function was crucial, even if it's just for the fun of recording high scores in games (only kidding!). Thanks to the PSRAM addition, I could conveniently locate space to store the 4K memory from the flash before erasing it. This allowed me to replace a portion of the 4K with new data and subsequently write the updated or original data back to the flash. This solution addressed the challenge Steve encountered when attempting to implement disk WRITE functionality.

Relocating the emulated RAM from the flash ROM allowed me to shift the start address of the Floppy disk image from 0x200000 to 0x100000. This change enabled me to create and integrate a 2.88MB MS-DOS boot disk image, providing more space for games. However, the shared code presently utilizes a 1.44MB disk image, as they are readily available online. My decision to omit an SD card was primarily due to running out of GPIO pins for the CS pin. I was not inclined to forfeit either the PC-speaker (connected to GPIO4) or the Serial I/O (utilizing GPIO5 and GPIO16). Nevertheless, during my spare time, I am contemplating an "upgrade" to the flash ROM, increasing it to 16MB. This enhancement would provide a substantial 12MB "hard disk" capacity for the emulator. (EDIT: As of January 26, 2022, I have successfully executed this upgrade!)

---

Keyboard Code:

The original code lacked the keyboard functionality, and what little keyboard-related code it did contain was not functioning properly. Consequently, I had to start from scratch. Since I had no prior knowledge of how an IBM PC-XT handled keyboards, it took me over a week to determine how to input keyboard data into the emulator. I eventually discovered it was quite straightforward: I placed the keyboard scancode in portram[0x61] and then invoked doirq(1). Since scancodes differ from ASCII codes, a translation process was necessary.

I initially began the project with serial input for my keyboard until I purchased an inexpensive PS2 keyboard. Thus far, the keyboard code has worked quite effectively. However, it's worth noting that it's not flawless. Some games, like Alley Cat, directly read keyboard input, making the emulated key input less than ideal, primarily due to the Make/Break translations. Fortunately, the majority of other games and applications do not require this level of precision, so we are still in good shape.

A crucial point to remember: the PS2 keyboard operates on 5 volts, not 3.3 volts.

----

Serial I/O:

I've also incorporated a Serial I/O at 3F8h, with the hope of utilizing it for a practical real-world application (which I have yet to test). To experiment with the serial port, I connected it to an additional ESP-01s, serving as a "verySlowSerialModem" for internet connectivity. I must emphasize, it's very slow! The emulator's sluggishness is likely due to the terminal software being written in QBASIC. I even encountered data loss at a rate exceeding 300 bps. As a remedy, I increased the SoftwareSerial buffer size to 4K (though I'm not entirely sure where the 4K came from, likely the IRAM). This adjustment prevented data loss as long as the buffer wasn't fully utilized.

To compound matters, I ran out of GPIO pins for hardware flow control. Consequently, I intentionally reduced the ESP01s modem speed to 300 bps. Surprisingly, this led to a fun experience of accessing BBSes and performing HTTPS GET requests to read news from text-based news sites like https://lite.cnn.com/en.

By the way, the baud rate can be adjusted from 50 bps to 19200 bps. As long as the SoftwareSerial doesn't run out of buffer capacity, it will continue to function, albeit at a slow pace, until all data is processed. Perhaps a more capable terminal software could help improve the experience.

For my intended purpose, which involves a baud rate of 9600 bps and software-controlled data flow, it should work adequately, even if it's at a snail's pace.

---

PC Speaker:

Given the absence of hardware Programmable Interval Timer (PIT) counters, I found a solution by making use of the Pulse Width Modulation (PWM) function of the GPIO in the ESP8266. This allowed me to generate sounds at various frequencies.

---

The Code:

The code I've provided is rather messy and patchy. It was developed using Arduino IDE version 1.8.13. If you have any suggestions or ideas on how to enhance the code, please consider sharing them with the community.

Since I cannot share my MS-DOS disk image due to copyright constraints, I recommend obtaining a suitable disk image online for testing purposes. The image should be a 1.44MB floppy disk with an appropriate DOS version, which can be copied to the ESP8266 flash ROM at 0x100000 (refer to the attached picture).

For those interested in using a 2.88MB floppy disk image, you will need to modify the code by changing "disk[drivenum].sects = 18;" to "disk[drivenum].sects = 36;" within the "insertdisk" function.

---

Acknowledgements:

Jan Ostman (MCUHacker01): The author of the original ESP8266 emulator code, which was amazingly well written and started this project;

Mike Chambers:  The author of the widely adopted FAKE86 code;

Cnlohr and Hrvoje Cavrak- Authors of the code for composite video generator in ESP8266.