A 50% size replica of my favourite 8-bit computer
To make the experience fit your profile, pick a username and tell us what interests you.
So SD card support has been extremely frustrating, to the point of complete despair. I'm still unable to make it work as I wanted it to, but I guess I managed to discover a narrow path, which if followed carefully allows safe passage from initial directory loading to loading file content, almost always. I'm still unable to show OSD and access SD card at the same time.
In the meantime I have found some really useful bits of code that should be standard in ESP32 SDK but they aren't:
better_readdir() is particularly important because stat() in FatFs is excruciatingly slow, it takes 1-3 seconds just to get one file size. Why? Don't know.
I also made good use of Graphics from @bitluni ESP32Lib. Excellent library, not perfectly optimised but easy to make modifications to. I implemented BGR233 canvas and added clipping. My OSD buffer is thus fairly small as it's in 8-bits. This allows it to be stored in DRAM. It's copied to screen using routine similar to the one that copies the main Vector-06c buffer. That's not ideal because it's fairly CPU-heavy, but I just love it when it's shown next to the main screen.
This also made me add support for 1:1 horizontal scaling of the main screen, which is switched on when OSD is displayed. It could also be made an optional mode without OSD.
Curious bit: a program that only has nops, for example when it's empty RAM, is a worst case for the emulator because this means maximum amount of instructions per scanline. When something like this is executed, the OSD becomes really reluctant to react to user input. Some programs even have flicker at the top left corner of the screen. Something to deal with at some point later.
So far only ROM loading is implemented.
So I though that loading files would be a trivial thing. So I implemented some basic ListView-like visual control for the OSD and started debugging it. Strange.. suddenly there are sdcard errors all over the place. Sometimes they are immediately after the start, sometimes later. But most frequently they appear after invoking the OSD. And if I invoke the OSD automatically at the boot time, the errors increase when the program executed by the emulator seems to create a heavier load on the emulator.
I tried a ton of regular things you do when trying to weed out a software error. All my stacks were silly oversized. I juggled around initialisation routines. Moved things between tasks and played with task priorities. Even sacrificed proper screen updates, giving SD card task the highest priority. But alas:
Searching for this kind of stuff also does rarely give useful input because every time it's someone forgot to connect a wire on a breadboard or something. But one of the discussions that I found mentioned power issues. 0x109 and CRC errors tend to be symptomatic to poor power delivery.
I examined the board and found buggerall capacitors:
There are some near the buck-boost converter near the USB-C connector and there's exactly 1 (one) 100nF capacitor near the ESP32 module. That's all. I suddenly remembered how this board sometimes doesn't want to boot, or wants me to plug it to another port on the hub, or seems to boot but then enters some weird loop and you need to replug it again...
Since yesterday I'm a happy owner of a brand new optical stereo microscope which I was eager to try out. Here's what I added:
220 tantalum and 10uF ceramic to the right of R15:
10uF ceramic near the ESP32 module. It has internal caps but it doesn't hurt having more:
Yes, it did fix the problem. SD card reads, no random inexplicable errors.
although it does seem to help, it's not ideal anyway. There are still errors. They seem to appear less frequently when I power this board with a powerbank rather than USB hub.
SD card problems persist and I'm not saying that they can only be explained by aliens, but it's aliens. I don't know if the capacitors helped anything at all now. Stuff is janky as usual.
Now for the ranty log...
So the idea was that the mini keyboard uses a Pi Pico as a kind of universal interface adapter, something that you can plug in into a PC and have a little keyboard for your v06x emulator and simultaneously be able to attach it to v06x-mini board via SPI interface and use it with the esp32 hardware emulator. The board layout actually even allows it to be plugged in directly into the original steamy DIP-packagey Vector-06c -- however in this case PiPico should not be soldered down as it's not 5V tolerant and scanning the keyboard would most likely damage it. So it's amazing and should be easy, right?
Oh so wrong...
SPI keyboard matrix scanning
rp2040 SPI peripheral is fundamentally broken in every possible regard, at least when you're trying to implement a slave device. On top of it there's a very raw driver in the SDK which introduces another layer of brokedity. After a day or so trying to figure out why I can't receive 2 bytes, I found out that in order to be able to transfer more than one byte in one CS assertion, you absolutely must use SPI mode 3.
That's not really a problem, why not use mode 3 indeed. After a while though it turns out that in mode 3 somehow the bit position tends to get garbled and it results in a bit-shifted message. I can't figure out a way to consistently deliver it. The only solution that I can find is to automatically reset SPI peripheral on the PiPico if the message is not right. And it seems to work.
So the protocol at this point would be something like this. When a program on the host v06x encounters an OUT instruction that selects columns, it retransmits this selection to the PiPico. E.g. "0xe5 0xfe" to select column 0. Later IN instruction sends a request "0xe6 0x00" and receives rows in return. For some other technical reasons I had to add a third byte to be able to reliably get the data back.
So all of this is minor technical details, something you always deal with when putting relatively unknown pieces together. What matters is that albeit with more complexity and slightly less pretty than initially imagined, stuff works.
Enter the second peripheral.
So there's also an SD card, which is a must have. There are more free pins on ESP32, but on this board it is connected to the same bus, using all the same wires as the external SPI peripheral, except for the CS pin obviously. That was a known from the beginning, and it should never be a problem. SPI slaves hi-Z their I/O when CS is not asserted and have no impact on the bus whatsoever.
I'm really glad that the Espressif SDK has SD/MMC card and FAT components conveniently standardized. Huge kudos to them for doing that. So I have put together my first little test that would just list files on the card and print them in the console. However, the card would not mount. After verifying all pin connections and some frustrating searching for similar errors, I remembered about the keyboard. Some long troubleshooting hours later I realised that SD card works when I disconnect MISO pin from the keyboard, and only then. No matter what I would do, MISO line connected to PiPico with initialised SPI peripheral (completely regardless of mode) would mean total and complete bus sabotage. Beautiful.
I have long suspected that eventually I would have to use a custom SPI peripheral built using PIO. So this seemed like a good moment to try. But after some research, and much later than I should have, I found that the only example doing that that I could find needs consecutive pins in an arrangement not compatible with the standard SPI peripheral pins. Which I very much need because...Read more »
I noticed that it's not immediately obvious which hardware platform I'm using, because it's not exceedingly common. It's impossible to remember its name because is "ESP32-8048S050". It's cheap and yellow and it shares DNA with the better known "Cheap yellow display", but it's not the same. This one has a 800x480 IPS screen and it's driven directly by ESP32-S3 LCD peripheral, thus allowing me full control of refresh rate, making it as close to 50Hz CRT as possible.
Now for the updates...
I was afraid it's not going to be possible because of timing constraints.
But it turns out it's alright. I already implemented the key optimisation for this in the early stages of porting. The pixeling is done in pairs. So that each pair of pixels, which are always the same in 256-pixel mode, requires only one palette RAM access. So I already had the 2-pixel palette in place.
All I needed to add was some palette processing for the 512-pixel mode. The values for left-pixel would be replicated into every index on the right side, and the values for the right-pixel would be replicated into every index on the left side. When the mode switches, precalculated palette is copied in place swiftly. The main raster loop didn't need to be modified at all !
There's something about this version of BASIC called Бейсик-Корвет that I find magical. It's the font and the fact that it's hi-res and black and white and just a bit of childhood sparkle.
A video short featuring giant hands working tiny keys:
This is a feature not really well supported in the emulators. The matter of fact is that it's an output line directly connected to a 8255 pin and as such it is subject to all the regular abuse like PWM. Strangely there are no programs known to me that would implement LED breathing for example. The emulators tend to just take the whatever value and display it at the end of a frame, if they bother to display it at all.
In v06x-mini I initially tried to update it immediately, but for now I resorted to just output the state at the end of the frame like other emulators do. The reason for this is very slow SPI communication with ESP32 drivers, which takes at least one FreeRTOS tick to complete. I think implementing SPI link using SPI HAL should not be a problem, but it's a lower-priority task for now.
The keyboard is attached via SPI, clocked at 8MHz. Pi Pico is SPI slave. Unlike most emulators that connect a PS/2 or USB keyboard, the keyboard matrix is scanned by the actual software. So each OUT to the column select port is sent over to the keyboard, and each IN from the rows port is then channeled back. There are exceptions for the keys that are off-matrix, those are scanned once per frame because some programs poll them frequently and SPI transactions are slow.
Some takeaway points from this stage:
Please correct me if I'm mistaken in any of the above points, or point to solutions. I'm particularly interested in lower-level, more barebone access to ESP32-S3 SPI peripheral. For example, I can't properly output LED status in due time without hiccupping the main loop because it must wait for a whole FreeRTOS tick. It's not a problem in 100%-o(n) cases because nobody uses keyboard LED for anything useful, but I know that it's not right and it bothers me.
Things that need to be done for this thing to be a useful Vector-06c:
Not many games use 512 pixel mode. It is essential for everything MicroDOS or CP/M. It's not the most amazing stuff v06c-wise, but it's sometimes also the most advanced stuff. The problem with it is that it 1) adds a conditional to the very very tight loop of the pixel filler and 2) can't use the same pixel-filling trick that I'm using for the regular 256-pixel mode. In other words, I'm not sure if it's possible to do at all.
8253 support is currently limited, beeper is not supported at all. I need to implement them differently, taking notes from the AY implementation from ZX-ESPectrum project.
SD card support is just dead boring because it also means some UI to be usable and that's some completely separate project that I don't really find entertaining.
A case... I have some cardboard boxes.
Its destined to be used with v06c-mini. Meanwhile...
Bullet points summary:
I'm considering this a major milestone because audio can be very resource-hungry in a tight loop of an emulator running on a microcontroller. I had to rework a huge part of v06x in order to make it work on this board. I started with something like 1/4 of desired frame rate and in the end of optimisation I had it running at 65fps instead of required 50. This was very handy because now I can burn those extra cycles on audio support.
v06x has relatively simple sound: it samples outputs on every clock, then the result is downsampled to 48kHz using polyphase rational resampler. So that all strange PWM effects on the 8253 and Beeper sound nice in emulation. Unfortunately it's impossible to call timer on every clock cycle on ESP32 so the audio is processed in chunks and sampled twice per video line, that's 624 samples/frame, or 31200 samples/second.
This works for most regular sounds, but breaks when a music routine goes for a frequency above sample rate. I'm just cutting down those frequencies for now.
AY support is mostly borrowed from the amazing ZX-ESPectrum project. It's cleverly done because it processes the required amount of samples on demand, so only before an OUT instruction and at the frame end. This reduces overhead a lot. Unfortunately because of what I believe is a bug in the LCD driver, I can't fully offset the processing in one fat call without altering my emulator loop significantly. Instead, I broke the processing into 6-line chunks, just like video. This still stresses out the system a bit too much near the top of the screen, but in most cases it's not visible.
I should borrow some of the ideas from ZX-ESPectrum for my 8253 and beeper implementation. But that's for the future. For now I think keyboard support should be the next most important milestone.
Some updates on the project that has been mostly dead for a few years.
The mini keyboard
More or less as originally planned, but I printed the keycaps in resin at JLCPCB. Filled in the engravings with gouache water-based paint, wiped off the excess and fixed it with a transparent acrylic spray. The legends look legendary, especially for their tiny size.
The switches are the same as originally planned, SKPMAME010. They feel squishy but for a little toy keyboard they are very nice, they even have pretty decent travel. There is some binding between the caps, but I'm sure it can be sorted out by filing the edges.
part of it is giving me more trouble. I was having high expectations from the kit commonly named
5.0inch ESP32-8048S050, it's available in China for around $35. I liked it because it uses a very nice 800x480 LCD panel that can do 50Hz, which is very desirable for v06c, and ESP32-S3, which has its own LCD panel controller which is flexible enough to do just what I need. It also has most things already in place so I don't have to bother with my own board, rare connectors, SD card slot.. It's all assembled here in one very pretty package, which I can only recommend -- just probably not yet for the purpose of Vector-06c emulation.
The reality of using it is a bit harsh. While I was able to make it scale v06c picture at 50Hz using crude integer nearest-neighbour interpolation, I've been struggling to make v06c emulator work at the required pace. Vector-06c has a programmable palette, which makes it finicky to emulate and these details consume CPU cycles in the main emulation loop. They also don't let the scaler use the bitplane buffer directly, so I can't offload the palette work to the scaler core without losing compatibility with various raster and multicolour effects.
Pictured above, it is running Vector-06c emulation at 50Hz though so there is some promise. There's no I/O and no sound. I can push it to 53Hz. That's some headroom, but not enough to squeeze in 8253 timer/counter. ESP32 was shown to be a good emulation platform previously, but with v06c it could be that it has met its match and we need more power.
Speaking of more power, there is an interesting bit of kit called Tang Nano 9K FPGA Development Board GOWIN GW1NR-9 RISC-V HDMI https://aliexpress.com/item/1005004147687144.html -- it's an FPGA of strange Gowin variety with its strange set of tools, which makes me reluctant to try it. But it seems to have all the oomph needed to run vector06cc (my Vector-06c FPGA emulation project). What makes it really attractive is that it also has a 50-pin RGB panel connector and even ships with a display. Definitely something to keep an eye on.
I think the mini-keyboard part could be valuable on its own. I'll be publishing the design files after I verify that it maybe actually works. Stay tuned.
Ready to be ruined by misaligned decals:
Now that I have such a beautiful stencil, the rest should be easy, just spray paint through it. I remember from some previous experiences that spray particles would try to reach behind the stencil and that would make the edges fuzzy.
For the first test, I used a couple of drops of silicone glue to hold the stencil as close to the key as possible. The result looked promising:
However I didn't like using the glue. I decided to stretch the foil across the keys using two test prints as supports. Here's just a nice picture of sun projecting through the stencil:
(We have sun)
I used some tape, perhaps some other things, but the foil would never stay put. In the end I decided to just use some weights (don't put too much weight on it).
The stencil prints always have these annoying connecting bits which I also had to add, or the stencil would not hold itself together. I touched them up using acrylic white paint on a tip of a smallest brush that I could find.
Become a member to follow this project and never miss any updates