Creating a small BKM-15R compatible unit

April 7th, 2022

Since I looked into the BKM-15R protocol, I thought it cool to implement it in a small unit, that would give the most minimal required from the controller, meaning input keys, menu navigation and of course power. Enter the BKM-15R-mini.

The BKM-15R-mini

This unit is based on a small PCB with the physical buttons which an ESP32 Devkit V1 board and a ENC28J60 module plugs into. The ESP32 at the same time gives the option of a small WiFi enabled webserver to emulate the “full” BKM-15R.

The “naked” PCBs. The ESP32 module is “raised” by a normal set of 2.54mm/0.100″ pitch female headers.
The ENC28J60 is raised further to clear the RJ45 plug.

It might be a good idea to put some insulation between the RJ45 plug and the base board. Either a bunch of kapton tape or something like this.

The base board without the ESP32 and ENC28J60 module.

The board features a kill-switch to disable WiFi if you only want the physical buttons, and don’t trust the air. You can also comment out all WiFi code in the Arduino project 🙂

The first time it is booted up, it looks for WiFi credentials in the persistent storage. If none are found, it goes into a configuration mode, where it creates an access point called “BKM-15R-Setup” and “adminadmin” as the code/PSK (at least at this time of writing, check the code for changes). When connected to this, browse to http://192.168.4.1 where you will see a very rude setup page, to enter SSID and PSK for the WiFi it is meant to connect to. After saving and restarting, if this needs to be changed, press the small “FORCE_SETUP” switch while hitting the “RESET” switch, to force it into configuration mode.

When connected to a WiFi, browse to whatever IP it has been given (you need to check this on your router, or whatever), and you’ll see a representation of the BKM-15R.

The BKM-15R-mini web interface

This is of course not very phone friendly, and might change over time, but here it is…

With the physical keys, you should be able to do most stuff, albeit a bit cumbersome, as setup will need to be done through the menu, but if you utilize the channel stuff of the BVM, it’s pretty powerful.

The ESP32 module can be found quite cheap on the internet, and the same for the ENC28J60 module Note that it should be the “large” version with mounting holes, at least that’s the only version I’ve mechanically tested.

The Kicad project for the base board can be found here: https://github.com/skumlos/bkm-15r-mini

The code for the ESP32 board can be found here: https://github.com/skumlos/bkm-15rduino
Note that it is based on Arduino framework (hence the rduino postfix, credit goes to Aaron Bonham for the original idea, as I stole this naming-scheme from his bkm-10rduino project).

The 3D printable case can be found here: https://www.thingiverse.com/thing:5344611

Reverse engineering the BKM-15R

January 27th, 2022

This post is still being worked on, but for the sake of sharing, I’ve already made it public so people can start trying some of it. I will update it as I figure out more stuff…

You can find a quick C implementation of this written for the Linux terminal at https://github.com/skumlos/bkm-15r-app

You can also find an implementation for ESP32+ENC28J60 at https://github.com/skumlos/bkm-15rduino

So related to the BKM-68X, an almost just as important unit to have when it comes to the BVM-A series, is the remote control, the BKM-15R. The BKM-15R is a “weird” inbetween unit, switching from the serial RS422 transmission of the BKM-10R remotes, to Ethernet/LAN connections, and apparently “quickly” evolved to the BKM-16R and newer for Sonys professional LCD series. As far as I know, only the BVM-A series use the BKM-15R, and nothing else. While the BKM-16R *should* work for BVM-A monitors, allegedly the function buttons of it do not, which is kinda a bummer, even though you *can* reach most if not all, of that functionality through menus. Still not so cool though. So what to do? Let’s map the BKM-15R and figure out if we can emulate it!

So to analyze network traffic, the infinitely awesome WireShark application comes handy. Since we need to listen in on a LAN connection, the easiest way is to find a managed switch that supports Port Mirroring, so a third unit will get the packets coming in and out of the network ports of either where the monitor is attached to the switch, or where the remote is. By doing this, we don’t interfere with the communication at all, we simply get to see everything going through the interface in question.

The remote has two modes, peer-to-peer and LAN, selected by a switch on the back. Peer-to-peer is when the remote is connected directly to the monitor by a network cable (straight through) and LAN mode is the more esoteric version, where the remote lives on a network with other units. The same goes for the monitor. Everything below relates to remote and monitor being in peer-to-peer mode. While I suspect it should work just as well in LAN mode, I have not tested any of that. Everything here is tested with an original BKM-15R and a BVM-A20F1E.

When the remote starts up in peer-to-peer mode, it first ARP’s “192.168.0.1”. The remotes standard IP address is 192.168.0.100. By default the monitors’ IP is 192.168.0.1. The monitor broadcasts some information about it, like model number and such from time to time.

When the remote gets a positive ARP response, it opens up a TCP socket to the monitor on port 53484.

Packet format

Each packet from remote to monitor starts with a header like this, top part is the “decoded”, bottom raw bytes:

0x03 0x0B 'SONY' 0x00 0x00 0x00 0xB0 0x00 0x00 <PAYLOAD_LENGTH> <PAYLOAD> 
0x03 0x0B 0x53 0x4F 0x4E 0x59 0x00 0x00 0x00 0xB0 0x00 0x00 <PAYLOAD_LENGTH> <PAYLOAD>

The monitor responds with a packet whose header is:

0x03 0x0B 'SONY' 0x00 0x00 0x01 0xB0 0x00 0x00 <PAYLOAD_LENGTH> <PAYLOAD>
0x03 0x0B 0x53 0x4F 0x4E 0x59 0x00 0x00 0x01 0xB0 0x00 0x00 <PAYLOAD_LENGTH> <PAYLOAD>

<PAYLOAD_LENGTH> is the length in bytes of the payload. Note that <PAYLOAD_LENGTH> can be zero, and thus there is no data returned.

Payloads

The payloads are ASCII based, meaning everything, even numbers, are encoded in ASCII. Note that the “commands” in the payload are generally separated by 0x20, meaning ASCII space.

Status probing

When the remote has connected, it starts continuously asking for the status of the monitor (roughly every 500ms), and the monitor replies with a 5 word status packet, like this:

Remote:

<HEADER> 'STATget' 0x20 'CURRENT' 0x20 0x35 0x00
0x03 0x0B 0x53 0x4F 0x4E 0x59 0x00 0x00 0x00 0xB0 0x00 0x00 0x12 0x53 0x54 0x41 0x54 0x67 0x65 0x74 0x20 0x43 0x55 0x52 0x52 0x45 0x4E 0x54 0x20 0x35 0x00

Red is the header part, 0x12 last in the header is the payload length (0x12 = 18 bytes after the header).

Monitor will then return something like this:

<HEADER> 'STATret' 0x20 'CURRENT' 0x20 8000 0x20 0000 0x20 0000 0x20 0000 0x20 0000
0x03 0x0B 0x53 0x4F 0x4E 0x59 0x00 0x00 0x01 0xB0 0x00 0x00 0x28 0x53 0x54 0x41 0x54 0x72 0x65 0x74 0x20 0x43 0x55 0x52 0x52 0x45 0x4E 0x54 0x20 0x38 0x30 0x30 0x30 0x20 0x30 0x30 0x30 0x30 0x20 0x30 0x30 0x30 0x30 0x20 0x30 0x30 0x30 0x30 0x20 0x30 0x30 0x30 0x30

Red is the header part, 0x28 is the payload length (0x12 = 40 bytes after the header). Bold parts are the status words. From this it can be seen that the monitors status words are 0x8000 0x0000 0x0000 0x0000 0x0000.

By pressing the buttons, it can be seen how the status words change depending on what is activated, like if the Aspect Ratio (16:9) button is pressed, the monitor returns:

0x03 0x0B 0x53 0x4F 0x4E 0x59 0x00 0x00 0x01 0xB0 0x00 0x00 0x28 0x53 0x54 0x41 0x54 0x72 0x65 0x74 0x20 0x43 0x55 0x52 0x52 0x45 0x4E 0x54 0x20 0x38 0x30 0x30 0x32 0x20 0x30 0x30 0x30 0x30 0x20 0x30 0x30 0x30 0x30 0x20 0x30 0x30 0x30 0x30 0x20 0x30 0x30 0x30 0x30

So the status words are now 0x8002 0x0000 0x0000 0x0000 0x0000. Thus it can be seen that, just like on the BKM-10R, the status bits are OR’ed together. By going through all the buttons I have deduced the following status bits:

Status word 0:
0x8000 - Power On (CRT On)
0x0400 - Scanmode (Underscan)
0x0200 - Horizontal Delay
0x0100 - Vertical Delay
0x0080 - Monochrome
0x0040 - Char mute (Char Off)
0x0020 - Marker Mode
0x0010 - External Sync
0x0008 - Aperture On
0x0004 - Chroma Up
0x0002 - Aspect Ratio (16:9)

Status word 1:
Unknown / unused

Status word 2:
0x0100 + 0x0080 - Menu is showing (both seem to be on) 
0x0040 - Color Temp
0x0020 - Comb
0x0010 - Blue Only
0x0004 - R Cutoff
0x0002 - G Cutoff
0x0001 - B Cutoff

Status word 3:
0x0080 - Phase Manual
0x0040 - Chroma Manual
0x0020 - Brightness Manual
0x0010 - Contrast Manual

Status word 4:
Unknown / unused

Status changing

When a button is pressed, the monitor issues a status change ‘STATset’ packet plus the button name plus the word ‘TOGGLE’ (except for degauss) with spaces between each “part”. So for instance aspect ratio toggle sends:

<HEADER> 'STATset' 0x20 'ASPECT' 0x20 'TOGGLE'  
0x03 0x0B 0x53 0x4F 0x4E 0x59 0x00 0x00 0x00 0xB0 0x00 0x00 0x15 0x53 0x54 0x41 0x54 0x73 0x65 0x74 0x20 0x41 0x53 0x50 0x45 0x43 0x54 0x20 0x54 0x4F 0x47 0x47 0x4C 0x45

The monitor replies with the standard header, with no payload, so <PAYLOAD_LENGTH> is zero:

0x03 0x0B 0x53 0x4F 0x4E 0x59 0x00 0x00 0x01 0xB0 0x00 0x00 0x00

So by pressing all the buttons, the list of possible status button names are (most quite self explanatory):

"POWER" - Power
"DEGAUSS" - Degauss
"SCANMODE" - Scanmode (Underscan)
"HDELAY" - Horizontal delay
"VDELAY" - Vertical delay
"MONOCHR" - Monochrome
"APERTURE" - Aperture
"COMB" - Comb
"CHARMUTE" - Char Off
"COLADJ" - Color Temp.
"ASPECT" - Aspect ratio (16:9)
"EXTSYNC" - External sync
"BLUEONLY" - Blue only
"RCUTOFF" - R Cutoff
"GCUTOFF" - G Cutoff
"BCUTOFF" - B Cutoff
"MARKER" - Marker mode
"CHROMAUP" - Chroma up

"MANPHASE" - Manual phase
"MANCHR" - Manual chroma
"MANBRT" - Manual brightness
"MANCONT" - Manual contrast 

The navigation buttons and input keypad are split into another “group” of buttons, the INFO group. Presses are announced <HEADER> 'INFObutton' 0x20 <BUTTON_NAME> 0x20. So for instance button ‘1’ is announced like this:

0x03 0x0B 0x53 0x4F 0x4E 0x59 0x00 0x00 0x00 0xB0 0x00 0x00 0x0D 0x49 0x4E 0x46 0x4F 0x62 0x75 0x74 0x74 0x6F 0x6E 0x20 0x31 0x20

Note the second last byte, 0x31, which is ASCII for ‘1’. Button 2 is then the same packet, except for the second last 0x31 byte instead being 0x32, so (difference marked in blue):

0x03 0x0B 0x53 0x4F 0x4E 0x59 0x00 0x00 0x00 0xB0 0x00 0x00 0x0D 0x49 0x4E 0x46 0x4F 0x62 0x75 0x74 0x74 0x6F 0x6E 0x20 0x32 0x20

Button 3 is 0x33, and so on for 0-9. The remaining button names are:

"ENTER" - Ent at numeric keys
"DELETE" - Del at numeric keys
"MENU" - Menu
"MENUENT" - Menu Enter
"MENUUP" - Up
"MENUDOWN" - Down

So for example 'INFObutton' 0x20 'ENTER' 0x20 when pressing “Ent” at the numeric keys.

Knobs

The knobs are also part of the INFO group. When a knob is turning, the remote sends a packet about every 150 ms with the string “INFOknob”, name of the knob, and a designation of how far it has turned. An example turning the Chroma knob slowly clockwise:

<HEADER> 'INFOknob' 0x20 'R' 0x20 'CHROMA' 0x20 '96/1/25'
0x03 0x0B 0x53 0x4F 0x4E 0x59 0x00 0x00 0x00 0xB0 0x00 0x00 0x19 0x49 0x4E 0x46 0x4F 0x6B 0x6E 0x6F 0x62 0x20 0x52 0x20 0x43 0x48 0x52 0x4F 0x4d 0x41 0x20 0x39 0x36 0x2F 0x31 0x2F 0x32 0x35

I am still somewhat unsure of how to interpret the output, except that the 96 is static, the next part (1) can be small or “large” (like 7), usually depending on how fast one turns the knob, positive values are clockwise, negative values counterclockwise. The last part (25) seems to be saying something about how far it has rotated, but I have seen both values be negative, which makes not so much sense.

Here is an example of the brightness knob turning counterclockwise at about the same pace as above:

<HEADER> 'INFOknob' 0x20 'R' 0x20 'BRIGHTNESS' 0x20 '96/-2/21'
0x03 0x0B 0x53 0x4F 0x4E 0x59 0x00 0x00 0x00 0xB0 0x00 0x00 0x1E 0x49 0x4E 0x46 0x4F 0x6B 0x6E 0x6F 0x62 0x20 0x52 0x20 0x42 0x52 0x49 0x47 0x48 0x54 0x4E 0x45 0x53 0x53 0x20 0x39 0x36 0x2F 0x2D 0x32 0x2F 0x32 0x31

The knobs’ names are:

'R PHASE' - Phase
'R CHROMA' - Chroma
'R BRIGHTNESS' - Brightness
'R CONTRAST' - Contrast

Memory card

Haven’t gotten to that yet…

Wrap-up

All in all its a pretty simple protocol that can be reproduced in almost any “normal” programming language and on any platform that supports raw TCP sockets on a LAN connection.

Any questions, comments or whatever, send me an email at martin@hejnfelt.com

Region modding a PSone TFT screen (SCPH-130)

December 20th, 2021

A guy on Facebook asked if it was possible to region mod a NTSC PSone with the original PSone screen attached, to be able to play PAL games also, to which I replied that as far as I knew, the screen is “region” locked, so PAL signals will give a rolling image, exactly like if your VHOLD on a CRT is “out of sync”. I remembered I had a banged up SCPH-130 monitor, which as far as I know is the first Japanese revision (unsure tho), and is thus NTSC, so I tried it out on my modchipped and DFO’ed EU/PAL PSone (SCPH-101), and true enough, when playing PAL games the picture would begin rolling.

Chipped PSone playing a PAL game (Die Hard Trilogy) on a NTSC SCPH-130 TFT screen.

Since the screen was already pretty banged up (not my doing) I decided to investigate and see if I could determine *what* was actually causing this.

It should be noted that there are several revisions out of this monitor, and I have no idea how other revisions work, so your mileage may vary.

Anyways, to disassemble the monitor, you need to remove the speaker grille, at least in the outer corners, as there are two screws in each side here. Note that I would assume it quite easy to destroy the grille (and end up with a result like mine), so take care.

When all screws are removed you should be able to pry the back cover off, and see the main PCB of the monitor:

The SCPH-130 mainboard.

So there are three large IC’s which is of interest here. IC12 in the middle which is an AN2526NFH signal processor (“jungle” IC), IC1 in the bottom which is a MN5814 “TFT AV Panel Controller IC” and IC3 which is a Wiseview LTS5QTB. The latter I couldn’t find any information about, but since it isn’t important, who cares.

So looking at IC12 it has a ~3.57MHz crystal, the color carrier frequency for NTSC composite signals, and thus is wired for NTSC. Looking at the datasheet, the unit does support PAL (with a 4.43MHz crystal instead, and another color trap filter), but since the PSone is connected via RGB, PAL/NTSC shouldn’t matter.

The MN5814 is apparently a controller for TFT panels, so I looked at the datasheet to see what its pins did. Here I noticed the NP1 (pin 46) which is labeled “NTSC/PAL” in the pin overview. This pin has a built in 100K pull-down resistor, and whaddaya know, there’s a test pad labeled NP1 on the SCPH-130 mainboard!

The MN5814 TFT controller. Notice the NP1 pad in the top left of the IC.

The datasheet I had didn’t mention much about what level did what, but measuring it out, it is pulled low by the internal resistor. I thought that I’d try to pull this high, so I did that, and voila, now NTSC was rolling and PAL video was steady! So to “fix” the issue, this pin needs to be toggled depending on the video type.

I tried looking around for a suitable signal, but couldn’t find any, but I found a bunch of VSYNC pads, including one called VOUT at the AN2526NFH.

AN2526NFH. Notice the pads to the right top of the chip.

There are 4 pads, bottom is labeled FIELD, next is VOUT, then HOUT and then BURST (VOUT, HOUT and BURST labels are further to the right just after the caps).

I took a scope and measured VOUT, and could read that it was the discrete VSYNC output. To prove that my hypothesis worked, I took an Arduino Nano, wrote a very small program that measured the VSYNC duration, and from that decides if the VSYNC is 50Hz or 60Hz. I do this by connecting VOUT to INT0/D2 (interrupt 0) so the MCU runs a piece of code when the signal changes state, and then measure the number of milliseconds from LOW to LOW (using millis() function). Above 18ms I decide it to be 50Hz (which is ideally 20ms) and below is 60Hz (essentially ~16,7ms). Below 12ms is considered “noise”. Applying the decision to an output and then connect that output to the NP1, and *pling* everything now works! You can find the code here: https://github.com/skumlos/frqdetector

The same setup as previously, now with the Arduino attached.

Now this is very crude, I don’t even know where to put the Arduino, but maybe using a Mini Pro or any smaller footprint Arduino compatible board, or writing a version that works for the PIC12F508 which is really common for modchips, I suppose that would be much better (or any ATTiny or whatever).

The Arduino is powered by the 7.5V point in the bottom of the PCB near the hinge and whatever GND point.

The NP1 pad connection.
The VOUT pad connection.
The Arduino Nano (notice the 7.5V pad in the top right)

Fun stuff! Any ideas or something for this? Let me know! martin@hejnfelt.com or skumlos/skum on various boards.

Making a new BKM-11R cable

April 6th, 2021

So I bought a Sony BVM-D9H1E to be able to test my BKM-129X boards at 31kHz (480p) and above. Being a *1 unit and not a *5 (like D9H5E) it has no integrated controls, and thus need an external controller. This can be either a *5 unit, a BKM-10R, or in this case the BKM-11R which connects to the front mini-DIN 8-pin option plug.

BKM-11R remote controller
The 8-pin mini DIN “OPTION” plug of the D9H1E.

Now the controller has a DE-9 port (and another mini-DIN 8-pin plug to daisy chain further to units like the BKM-14R probe) which therefore requires an adapter cable to the mini-DIN.

The DE-9 port to the right that connects to the monitor, and the 8-pin mini-DIN to the left.

The cable didn’t come with my remote, so I needed to make a new one. Searching the internet brought me to the shmups forum where this picture exists:

The top picture is the DE-9 port of the BKM-10R and the bottom is the mini-DIN. Unfortunately the BKM-11R does *not* use the same pinout as the BKM-10R seemingly, because making a cable from the above doesn’t work. I opened up the remote and could see that ground and 5V were definitely not the same, so I started searching for answers. Although 5V and GND are easy to measure out, the TX/RX pairs are not so easy as they’re seemingly buffered by the remote for the mini-DIN port.

I was unable to find a service manual documenting the the remote, so I was kinda lost. The next step was of course to disassemble the remote further to see if I could decipher further, but then awesome guy Steve Nutter of Retro Tech (he does a lot of informative CRT related videos on YouTube, check it out if you don’t know him https://www.youtube.com/channel/UCwOTvOtoAjiqQx1PCrfmTKw) measured a working cable out, which then made it easy to make a replacement. The pinout is as follows (pin numbers are as a normal DE-9 plug):
1 – NC
2 – RXD
3 – TXD
4 – NC
5 – GND
6 – 5V
7 – ~TXD
8 – ~RXD
9 – NC

Thus to make a cable, using this pinout, along with the information fromt the shmups post by xga, the connection needs to be like this:

Signalmini-DIN (monitor)DE-9 (controller)
TXD83
5V76
RXD62
~TXD57
GND45
~RXD38
Mini-DIN female pinout (taken from Schurter 4850.2810 datasheet)

For cable, I simply used an normal unshielded Cat5E ethernet cable, and then use a twisted pair for TXD/~TXD and other pair for RXD/~RXD.

BAM! Cheap BKM-11R cable! Thanks again, Steve!

Improving 720p performance of my BKM-129X compatible designs

April 4th, 2021

So I’ve found out I’ve made a huge mistake on my BKM-129X designs (all of them): The low pass filter of the THS7374 is *not* disabled as was intended. As I myself have no hardware to test 480p and above, so I never noticed the issue, but Bob from RetroRGB.com noticed when running 720p sources through it, there is a softness to the picture which shouldn’t be there. This is because I erroneously connected the BYPASS pin of the THS7374 to GND instead of VCC. This means that instead of disabling it, it is enforced. Nobody likes this filter, and it should never have been enabled! For 240p/480i/480p the filter makes no difference so if this is what you’re using the board for, don’t bother, but for 720p/1080i it makes a huge difference. Luckily the fix is rather easy, and can be carried out on already assembled boards.

Disconnecting BYPASS from GND

First thing to do is get the BYPASS pin (pin 9 of U2) disconnected from the GND plane.

The THS7374 (U2) chip. BYPASS/Pin 9 is the bottom second pin from the left, right above the via (the black dot).
Picture courtesy of RetroRGB.com

Pin 9 is connected to the GND plane through a small trace right in front of it. We need to cut this trace (or lift the pin). I prefer cutting the trace.

This small trace marked with red should be cut/removed.

Use a scalpel or boxcutter blade to gently cut it. Its not very thick but its important to ensure the connection is completely gone, and at the same time ensure the via is not destroyed. The pin to the farthest left is not connected to anything, the other is VCC. Measure that connection to GND is gone by using a multimeter set to measure resistance.

Connecting BYPASS to VCC

After severing the pin, we need to connect it to VCC to force the low pass filter off. Luckily the pin right next to it, to the right, is VCC. The easiest is simply adding a small blob of solder to the two pins.

The BYPASS pin bridged to VCC pin (pin 10).

That’s it. Enjoy 720p, and sorry for the inconvenience.

De-mystifying the BKM-68X

March 11th, 2021

– or “How my love for logic analyzers is re-affirmed”

This is both a documentation project as much as a reference for myself. I will update this as I figure stuff out, so this article is in no way “final” (yet). I’ve decided to release it as openly as possible, mostly to maybe get feedback and ideas, but also to show the world that reverse engineering projects of the BKM-68X *is* being done. Please send me any ideas or thoughts about the project (martin@hejnfelt.com) but please no “When is it done?” and alike. It might come “soon”, it might come never.

In the current world of CRT enthusiasts working with professional monitors, there are few input cards that has a more elusive status than Sony’s BKM-68X, the analog RGB/YPbPr input card for Sony’s BVM-A series. This card is the only way to get analog component signals into the BVM-A series, at least without any converters. The BVM-A series came very late in the CRT era, and thus lot of production had already shifted to digital formats and utilized digital formats like (HD)SDI as the transport, instead of analog formats. This seemingly means that there are very few BKM-68X in existence (the talks are around 1000 units) in comparison to the number of available BVM-A’s, which then usually sports the (HD)SDI card BKM-62HS and maybe the BKM-61D (SDI and composite/S-Video). The scarcity of the BKM-68X means that they often sell for more than $2000 a piece, if they are even available. Given that the A-series are young monitors, they are desirable given that their tube-time is often quite low.

So, in the world of retro-gaming, digital formats, especially not SDI are usually not desired, while young tubes, high TVL and RGB is are. Shelling out $2000+ for a input card is however insane for many, which often leaves BVM-A monitors undesired (there are some other issues with the 68X/BVM-A, especially sync wise, but forget about that for now). In general increasing the availability and decreasing the costs of the BKM-68X, would be helpful for the retro-gaming community, which makes it a good candidate for reverse-engineering.

When I released the reverse engineered BKM-129X card, a lot of people asked me about the BKM-68X, to which I normally replied “Forget it”. This was because the design is *massive*. The card has so many components that it looks like the surface of the Death Star, and if some X-Wings appeared trying to find an exhaust pipe to bomb, nobody would really find it weird. At the component level, Sony has also blessed us with black-box units like a FPGA plus a CPLD at the same time, so yay!

The BKM-68X. Picture is courtesy of RetroRGB.com.
The Lattice CPLD and Xilinx Spartan FPGA. Picture is courtesy of RetroRGB.com,

One night however, my life was apparently going so slow, that I ended up looking at the service manual for the card (most likely to avoid watching Dora the Explorer, Bob the Builder or alike), which sports the full block diagram and schematics to the card. By looking at the signal names for what is seemingly thrown around on the IO connectors (of which there are two 100-pins) and by looking at the signal paths, it made me believe that the card is seriously over-engineered, at least in the context of retro gaming, and that many features can be simply omitted, if the goal is not a replica but a basic “alternative” which supplies RGB/YPbPr to the A-series, and that’s it. It’s my belief that this would be beneficial to 95% of the A-series owners, of which I suspect very few are professional video editors, and many retro-gaming enthusiasts.

What really made me interested was this part of the block diagram:

The block diagram for the interface between the BKM-68X’s CPLD and the monitor.

Plus the part of the video signals:

The video signal portions of the BKM-68X card.

Now the CPLD interface suggests a quite standard parallel interface of a shared address/data bus, plus the video signals seemingly are normal analog RGB/YPbPr along with separate horizontal and vertical sync signals.

All around the board are signals which are generally in the same “vicinity” to the ones of the BKM-129X, namely: VIDEO_OE_X (video output enable, active low), INT/EXT_X (internal/external sync, active low), HD/SD_X (high definition/standard, active low) and so on. There is some circuitry that seemingly seems able to do some “magic” to the input, like an aperture generator (I have no idea what that should be doing) but in the end, I this seems to be non-vital. In general though, looking at the signal paths for the RGB signal paths, they basically do through some filtering and buffering, with some different paths (filtering orders, delay lines) selectable by the HD/SD_X signal. It all ends up in the IC486, which is a AD8075ARUZ which is a 500MHz 6dB video buffer, that is still easily obtainable (and should most likely also be replacable by something like the THS7374).

As the address/data bus should be able to be “eavesdropped” on, exactly like the SPI-like bus for the BKM-129X, it is my firm belief, that a viable alternative can be created.

Of course reading the contents of the EEPROM for at least the FPGA could be possible, but using the data would be against the law in many countries, and in the end also still require the same FPGA unless the bitstream was then reverse-engineered.

Accessing the hardware

To be able to actually do such a project, the original hardware is strictly necessary. Fortunately I was gifted a BVM-A20F1M with a BKM-15R for the project. The BVM didn’t have the BKM-68X, but it did have the BKM-62HS, so it was a start, even because the 62HS’s digital signals still end up as analog video coming into the monitor. Technically, this would mean that even just ending up being able to impersonate a 62HS might give a “workaround” card. However seemingly the card supplies YPbPr to the monitor, plus there are some horizontal scan rate “issues”. Jumping a bit in time, I was lucky to get an original BKM-68X donated to the project, which opened up the possibilities to make a board much closer to “what we want”.

To eavesdrop on the communication, I developed a small “breakout” board that could be inserted between the monitor and the card, which breaks out each 100-pin connector:

BVM-A card breakout.

The connector is a JAE TX24-100R-LT-H1E (mating with TX25-100P-LT-H1E). This card allows me to easily attach the probes of my logic analyzer and listen in on how the card and monitor speaks to each other, exactly how I also dealt with the BKM-129X card. I still need to solder a couple of wires onto miscellaneous spots of the board, as to find the command sequences for the different “options” (internal/external sync, RGB/YPbPr etc.). This is kinda fear inducing when you start to solder onto a $2000+ card. Luckily though, there are some resistors at all points of interest that can be used:

VIDEO_OE_X @ R522
INT/EXT_X @ R885
HD/SD_X @ R838
RGB/COMP_X @ R840

Also to be sure how the data exchange works I needed to figure out when the data bus is driven by the CPLD and when it is driven by the monitor. As can be seen in the service manual, the CPLD has 8 address/data (A/D) lines in and 8 A/D lines out, both “buffered”, but the A/D out lines, has an “output enable” signal (AD_OE_X) attached to it. The same goes for the IRQ_X signal which is tied to the IRQ_X/SLOT_X signal, through a buffer, which is enabled by IRQ_OE_X signal. These signals can be observed at:

IRQ_X @ R914
IRQ_OE_X @ R915
AD_OE_X @ R916

The address/data bus and relevant control signals.

The signals of interest are thus:

AD[0:7] (8 bits/1 byte) – The address or data byte being transmitted
A_X/D – A/D byte is address when this is low, or data when it is high
R/W_X – Data is read when this is high or written when low
CLK_RW – Data is latched on the this, seemingly on positive edge
AD_OE_X – Address/data bus is controlled by CPLD when this is low
IRQ_X/SLOT_X – Seemingly when this is low, the monitor interrupts the card *OR* when IRQ_OE_X and IRQ_X is low, the card interrupts the monitor
RESET_X – Reset the card on low

So 15 signals, plus the 4 internal-card signals (VIDEO_OE_X etc.) meaning 19 signals. To do all this at once, I had to upgrade my logic analyzer to more channels, so I bought one of DreamSourceLabs DSLogic U3Pro32 and srsly, it’s awesome. If you need a cross platform logic analyzer, I can recommend this (I use Ubuntu/Linux as my primary OS).

As with the BVM-D9 and D14, the monitor starts speaking with the card already at standby-power (when the mains-switch on the back of the monitor is flicked), so I set the logic analyzer to sample at 20 MHz for 50 seconds from when I flicked the switch, and ho-lee-phuck, these monitors speak, jesus christ.

Analyzing the data

So here’s a screenshot of DSView of the data:

Initialization, settings are RGB with External sync (no signal input)

So first, again, there’s a lot of data, and at first, I noticed that CLK_RW pulses were 50 ns (20 MHz) in width, which means when I sample at 20 MHz, I’m not upholding Nyquists Sampling Theorem, which says that to properly sample something at frequency X, you need to sample at 2*X. This means that I needed to redo my measurements at 50 MHz (next available sample speed on my analyzer) at least if I wanted accuracy in the timing between edges. Doing this showed that clock pulse widths are 40 ns so 25 MHz(!).
At first I was unsure of what to make of all this data, because according to the service manual of the monitor, each slot has a separate SLOT_X_INT_X signal *and* CLK_RW signal. This would indicate that it would only clock data to the card (with CLK_RW signal) when it was speaking to the specific card. Seemingly all these data points are clocked in, which would, at first glance, mean that all this data was bound for the card. Luckily after “working” through the data, I got an “epiphany”: The card “changes” which commands it reacts to depending on which slot it is in, and this is actually somewhat of a kicker, because it narrows the amount of data to react to considerably down. The idea can be seen here:

One of the first command sequences, here card is in OPT1, so first slot.
Same sequence, but a “block” later, when card is in OPT2, so second slot.

So first the monitor issues a 02h command, followed by 01h which asserts IRQ_OE_X (interrupt output enable, active low). Then comes FFh (which denotes a start and end of command sequences, as will be seen later). Then it issues a of 10h 03h XX (XX = 01h-07h), FFh then 02h 00h which de-asserts IRQ_OE_X again (thus seemingly allowing interrupts from the card again). Now in the first picture, it can be seen that SLOT_X_INT_X goes low in the 10h 03h 02h (where the card is in OPT1 slot), but in the second picture, it goes low during 10h 03h 03h (where the card is placed in OPT2, so the second slot). As the first slot is actually the network card (or whatever we call it), where the remote and such is placed, it makes sense that OPT1 is actually slot 2 (in the monitors sense), OPT2 is slot 3, OPT3 is then slot 4, then comes whatever the other “cards” are in 5 and so forth.

EDIT: Using my eyes, the monitor even says so 😀

The monitors label explaining the slots.

When the card is in OPT1 as the only one, it reacts to commands in the 2Xh, while in OPT2, it reacts to 3Xh. The monitor does try to read 3Xh and 4Xh addresses, from time to time, but when there is no card there, nothing responds.

Blank read from 30h 00h when card is in OPT1.
Blank read from 20h 00h when card is in OPT2.
Succesful read from 20h 00h (returning 88h) when card is in OPT1.

So all in all, we can now assume that only commands starting with 2Xh is actually intended for the card, when sifting through the data recorded when the card was in OPT1. I will generally show 2Xh commands as I’ve got most data from the card in the first slot, so in all those cases, practically the first byte could be 3Xh or 4Xh if the card was in OPT2 or OPT3 respectively.

Command format

From this it can be deduced that the general command format is like this:

First the monitor issues a FFh with AX_D low and R_WX high.
Then it writes a “command/register byte”, then an “offset” byte, and then the actual value (either read from, or written to, denoted by R_WX), and then a FFh more.

So an example as above is FFh 20h 00h 88h FFh, where R_WX is high on the last byte, meaning the monitor is reading byte 00h from register 20h, in the above example, this returns 88h.

For the two “special” commands, 02h (IRQ) and 10h (setup/init) it starts with the command, and ends with FFh.

Here is an example of a write:

A write sequence

So here it writes A0h to 32h 25h, so FFh 32h 25h A0h FFh.

Card identification

So first part is figuring out where the card is actually being identified. Analyzing the data from the BKM-68X, BKM-61D and the BKM-62HS shows that the first difference lies in the response of the read from 20h 00h, which responds 88h on BKM-68X, 81h on the BKM-61D and 82h on BKM-62HS. This also the first command issued to the card, before all the serial stuff and whatnot begins.

Serial number readout

Command 23h seems to read the serial. The serial must be read out from some PROM by the card internally somewhere, as I suspect the Xilinx FPGA actually contains a softcore CPU or something, because there are some “wait-states” where the monitor issues some command, then repeatedly reads some register until it turns to some value (00h), then reads another register, seemingly verifying its contents, and then repeats the process if not “accepted”. One of those registers is the serial, first byte, 23h 00h. Initially the monitor issues some commands, reads a register until it turns 00h, then reads 23h 00h, which returns 88h (like the identification register). If this is the case, the monitor re-issues the command sequence, reads the register, repeats until this register returns at least 32h, which is the first byte of the serial (just as with the BKM-129X, this is ASCII coded digits, so 32h is 2). The complete serial is then located in registers 23h 01h, 23h 02h etc. until 23h 06h as there are 7 bytes in the serial (eg. “2 0 0 1 3 3 7” would be represented by 32h 30h 30h 31h 33h 33h 37h), so:
23h 00h 32h
23h 01h 30h
23h 02h 30h
23h 03h 31h
23h 04h 33h
23h 05h 33h
23h 06h 37h

Video format settings

Command 21h seems to work on the video settings.

Register 10h (21h 10h) works on (at least) VIDEO_OE_X (video output enable, active low), INT_EXT_X (internal/external sync, low = external) and APA_ON (Aperture). The bits of the data byte I’ve identified are:
Bit 3 is VIDEO_OE_X (0 = VIDEO_OE_X -> HIGH, 1 = VIDEO_OE_X -> LOW).
Bit 2 is aperture (1 = APA_ON -> HIGH, 0 = APA_OFF -> LOW)
Bit 0 is INT_EXT_X (1 = INT_EXT_X -> HIGH, 0 = INT_EXT_X -> LOW).

Register 31h (21h 31h) is the detected video format. This detection is seemingly done by the card itself. When the card detects video, it de-asserts the SLOT_X_INT_X signal, thus interrupting the monitor, which then issues some commands, including reading this register. I’ve identified the following data byte meanings (21h 31h XXh):

00h – No video input
01h – 576i / 50 Hz (verified with EU Wii/EU PS3)
02h – 480i / 60 Hz (verified with US SNES)
03h – 576p / 50Hz (verified with EU PS3)
04h – 480p / 60 Hz (verified with EU Wii)
0Ch – 1080i / 60Hz (verified with EU PS3)
12h – 720p / 50 Hz (verified with EU Xbox 360)
13h – 720p / 60 Hz (verified with EU PS3)

I need further units to know the values for other supported, however my guess is that some of the others are:
0Bh 1080i / 50 Hz

1035i/60 and 1080i/48 I have no idea about…

Given the supported formats here:

Supported formats from the BVM-A20F1M manual

So when the card detects an input (change) it de-asserts INT_X (which de-asserts SLOT_X_INT_X), and waits for the monitor to issue a sequence of commands.

Impersonating the BKM-68X

So to make a “clone”, there are some hardware requirements we need to meet, which does go out-of-spec compared to normal microcontroller options, namely the 25MHz I/O capability. Given that the 68X *and* the A-series are pumped full of FPGA and CPLD technology, the obvious choice is to use a FPGA.

Fast forward some time, I’ll fill in the blanks at some point (everything will be open sourced), it works!

The BKM-68X “clone” showing 50Hz/PAL SMS (60Hz/NTSC works also)
Here showing PC engine (white original, with my external RGB add-on)

… To be continued

Sony PVM-20L1 RGB mod

January 12th, 2021

This should also work for PVM-9L1, PVM-14L1, SSM-20L1 (confirmed) and in the end also other monitors using TDA9394H jungle, at least if you can get to the service menu. Note that capacitor numbers will be different for others. Check the service manuals and verify according to the jungle pins.

So a guy approached me because he had a PVM-20L1 without RGB, and asked if it was possible to mod it for RGB SCART. The unit uses a TDA9394H jungle which is very sparsely documented on the web. There are some mentions of it on the RGB mod thread on shmups and some different documents at least showing the pinout. These pinouts show that it has a separate RGB/YUV input on pin 50 (INSSW2/blanking), 51 (R2/Y), 52 (G2/U), 53 (B2/V), so that is our point of interest. Now the jungle is one of Philips’ which are known to have bits to disable/enable these kinds of inputs through I2C, usually the bit is called IE1 or IE2 (for Interrupt Enable), and this is no exception.

Enabling RGB

Looking in the service manual for the PVM-20L1 there is a walkthrough of the service menu, where the IE2 bit is shown/mentioned, and that relates to this external input. Thus we need to enable this bit.

TLDR; Open the service menu (Menu then Menu+Enter for 5 sec), SYS->Maintenance ID=111, DES->CONTROL0->IE2=1, ENG->Comb filter=Forced, save by Menu+Enter for 5 sec.

The service menu is accessed by pressing Menu once (so the “normal” menu comes up) and then pressing Menu and Enter simultaneously for 5 seconds.

The service menu.

Using + and – keys and “Enter” (“Menu” goes back), go to SYS and change Maintenance ID to “111”, press “Enter” to accept.

“System” menu
Setting “Maintenance ID” to “111” unlocks the settings we want to change.

Then go to DES (Design) and into CONTROL0 (on “page 2”). Here the IE2 bit is found so set it to 1. Press “Enter” to accept the setting.

Enable IE2 bit.

Right now, if we had everything hooked up, we’d see a nice RGB signal, BUT IT WOULD BE IN BLACK AND WHITE!!!1

So after going nuts about this, I talked to MarkOZLAD whom many might know from shmups and the RGB mod scene in general, and he hinted that he’d heard that the TDA93xx units needed CVBS for sync to have colors… Aha, so I tried this, and whaddyaknow, it worked.

Ok, but I have CSYNC cables for many consoles, so the suggestion was to create a board that encoded RGBS to CVBS and use that as sync source, to be able to support all RGBS inputs. This can be done “pretty” by using an RGB amplifier like THS7374 along with an AD724/725 RGB to CVBS encoder, and then feed the CVBS to sync, and RGBS to the TDA9394H. I actually begun a board for that, until I stumbled upon some people discussing the same jungle on the CRT Discord. Here a guy, “nut” had apparently fixed this (on a similar PVM I think) by changing “Comb filter” setting from “Auto” to “Forced”. Now this setting I had not seen, and I had been through them all (I thought) so I went back to the service manual, and goddamn, it was there, but NOT under the DES, but instead under ENG (and still needs “Maintenance ID” set to “111”). Flipping that, and glorious color was back! Thanks Mr. “nut”!

The “Comb filter” option

Now the “Comb filter” option is apparently since software 1.2, I don’t know what it looks like on earlier.

Hooking up RGB

So now to actually hooking up the wires. I will be using the SCART board which I’ve used several times, and I will be injecting each 0.7Vpp RGB signal directly through a 0.1uF capacitor, after 75 Ohm terminating them. The SCART board can be found here: https://oshpark.com/shared_projects/JpxJNG1c

The TDA9394H is situated underneath the main board, so in order to get to it, you need to more or less remove it from the monitor. Pull off the metal case and plastic back, by removing the screws. Pull it backwards a bit, then up. There are a lot of wires to disconnect, find what works for you best. Tip: The small grounding “clip” which there are two of on the tubes lower left back, has a small “release” that needs to be pushed backwards to remove it.

Remember to discharge everything (there seems to be bleeder resistors, so if you have left it unplugged after some time, seemingly there is no charge, but warning warning)!

The TDA9394H jungle.

So locate the jungle that looks like above, and locate the 4 capacitors, C065, C066, C067 and C068.

The capacitors of interest

We need to remove them all, at least C065/66/67, but I took them all. For future reference changing C068 to a 1K resistor here might make sense, but I placed it elsewhere (and didn’t wanna take it all apart again).

Caps removed

Now injecting can be done on either the cap pad or on the small test/solder pads on the traces. I chose the test pads. Now C065 was blue/B2, C066 was green/G2, C067 was red/R2 and C068 was blanking/INSSW2.

The pads I used. Purple is blanking.
Wired up. You also need GND of course. White is blanking.

Now bend the cables onto the other side, from the front, as the sides are not accessible due to the “rails” that the board slides into.

Cables, cables, cables!

Attach the RGB to a circuit like this:

As stated, I have this on the SCART breakout board, but if you use other connectors like BNC, do your thing. Note that when IE2 bit is on, INSSW2 is weakly pulled to 5V. Thus if you need to switch to composite, you should add a 1K resistor between blanking and GND, and switch that, or add the resistor and blank with 5V (I use RGB blanking/pin 16 from SCART).

Add sync directly to the BNC inputs (on the PCB) and the same for mono-mixed audio to RCA input. I used input B.

Yellow is sync, white is audio.

So with that, turn on, select input B, and RGB input on 20L1 and friends is now possible! Then comes the fun of physically putting the SCART or BNCs or whatever somewhere…

Attaching SCART

So the back plastic part is attached to the metal casing by some plastic rivets. Now these can be removed by simply putting a flathead screwdriver in and they pop out. It is however not a very durable solution in my experience, and I have no idea how easy it is getting spares, so I spent some time contemplating on how to attach the SCART to the back while still being able to remove the whole thing and not tear everything apart. The solution I came up with was soldering connectors to the SCART PCB thus making the wires detachable from the PCB.

The SCART PCB with connectors on. You can see the small 1K resistor between blanking and GND if you look closely. The split connector is simply because I didn’t have any 10 pin angled ones.

Now as can be seen, I needed to make an opening in the side of my normal SCART bracket and then I designed a small detachable cover for it. To get the wires out, I clipped a little of the grille out. The designs including FreeCAD source can be found here: https://www.thingiverse.com/thing:4726360

The small “lid” for the bracket
The grille clipped out

The finished system then looks like this:

The disconnected wires.
…and attached

And of course some pictures

Sonic 4, best SNES game ever.
The “That guy with the brown jacket” game!

RGB modding the JVC TM-H140PN

October 18th, 2020

This is somewhat of a “short” post as I haven’t done the mod myself, but it has been verified that my software also works on this monitor. It’s mainly to index that it is possible to RGB mod it, using the I2C interception method as I’ve described for several other JVC monitors. Pictures and schematics should be credited to Victor Clausson.

The mod is somewhat closer to the DT-V100CG mod than the TM-A140PN one, as the jungle of the TM-H140PN is a TA1276AN and not a TB1226EN as the TM-A140PN uses. This means the software for the Arduino to be used, should be this: https://github.com/skumlos/ta1276an-i2c-bridge

Update regarding the Arduino software

Some time ago, an user told me he had a problem with the RGB dropping out every 4-5 seconds, being worse with some consoles, and others showed rare dropouts. We never solved it at the time. Another user contacted me with the same problem recently and we seemingly successfully fixed the issue, and version 1.2 of the software was made. If you’ve already done this mod with 1.1 of the software, and experience this issue, try upgrading to version 1.2.

Now just as with the other mods, the jungle needs to be isolated, and the Arduino put in-between the monitors microcontroller and the jungle (refer to some of the other articles if you want to know more). To isolate the jungle we need to remove resistor R540 (SCL) and R541 (SDA). The pads connecting to the jungle should then be connected to the Arduino on pin D5 (SCL) from R540 and D4 (SDA) from R541. The pads going to the MCU (so the other “side” of the footprint) should then be connected to the Arduino A5 pin (SCL) from R540, and A4 (SDA) from R541. On all 4 lines, a 100 Ohm in-line resistors should be added. On the jungle from pin 27 (SDA) and pin 28 (SCL) a pull-up resistor to 5V should be added to each. Usually I’d use 2.7K for these. Power to the Arduino can be taken from CN002 pin 8 and 9, which should be fed to VIN/RAW of the Arduino. Using a 5V rail connected to the 5V pin on the Arduino, can result in a monitor that won’t start. My guess is this depends on how fast the 5V rail comes up.

An example of the hookup. Power supply to the Arduino is still missing. Credit Victor Clausson.

To add RGB, we need to connect to TA1276AN pin 35 (R), 34 (G), 33 (B). Capacitor C519, C518, C517 needs to be removed, and each color line should include the “standard” 0.1 uF in-line capacitor and a voltage divider, like this:

RGB input line attenuation and DC bias circuit.

Now the above picture is actually not the best circuit, as it attenuates the signals more than necessary, and the resulting termination resistance for the lines is 105 Ohm (30+75) instead of the correct value of 75 Ohm. The thing is that the TA1276AN takes 0.5Vpp signals instead of the normal 0.7Vpp, so the voltage divider accounts for this. Many suggest (including myself on earlier posts) using R1 = 24 Ohm and R2 = 51 Ohm, as that creates ~0.5Vpp from 0.7Vpp and 75 Ohm termination, however I’ve begun using R1 = 10 Ohm and R2 = 64.9 Ohm, as this gives ~0.6Vpp which is stated as the maximum input for TA1276AN (I’ve tried direct 0.7Vpp also which works fine, but better go by the specs). This results in a brighter picture, but I’d rather turn down brightness on the monitor, than not being able to turn it up. Your milage and wishes may vary.

Sync should be connected directly to the input pin on either input A or B (on the PCB side of the BNC plug). Which you select will then decide which input should be selected for RGB.

To be able to blank (switch to RGB) you need to lift pin 32 of the jungle, and add a pull-down resistor from this to GND. I usually use 10K here, but the schematics from Victor says 51K. Use whatever 10K+ resistor you got. Now on pin 32, when ~0.75V or higher is applied it switches to RGB. You can use a switch for this, or use pin 16 from SCART, whatever you fancy.

That’s basically it. Check the full schematics from Victor here:

Credit to Victor Clausson

JVC IF-C01COMG Clone Full-size THT Edition

August 27th, 2020

So since I released the IF-C01COMG designs some years ago, several people asked me to make a full-sized card, I decided to finally do so. The result is this:

The IF-C01COMG THT clone

So this is basically my original THT base board thrown onto a full size PCB. The original was divided into two parts, mainly because of PCB pricing, which has now been reduced significantly by manufacturers like JLCPCB and so on. To use the available space, the complete BOM is written directly on the silkscreen along with various assembly instructions.

The board supports having either SCART or BNC connectors mounted, and has different audio options: When using SCART, the switch between the two RCAs select either passing the stereo from SCART to the RCAs or send it through a mono-mixing circuit to the internal speaker of the monitor, selected through SW1 switch. When using BNCs, the RCAs can be used as an input instead, running through the same mixing circuit. In that case SW1 should not be mounted, and needs bridging of a couple of pads (explained on the board).

I chose to use THT as I know some people find using SMD components a daunting task, so this should be possible to solder for most people.

You can find the complete Kicad project here, including the gerber files (found in the plot directory). The if-c01comg.zip fil can be directly uploaded to PCB manufacturers that don’t support direct kicad_pcb files (like JLCPCB).

BKM-129X compatible, full size version

June 17th, 2020

So, latest addition to the family of BKM-129X compatibles, is a full size BNC version.

BKM-129X compatible, full size version

This is functionally quite close to the original, as it now has BNC outputs also. Since I couldn’t find BNC plugs featuring auto-termination (at a reasonable cost at least), I decided to add a switch to each signal which will either terminate to 75 Ohm, or enable passthrough of the signal to the OUT port.

Top view

It is then possible to set the switches to OFF (down) and use external terminators, or simply skip mounting the switches altogether, and add a jumper/0 Ohm resistor to the lower left two pins of each switch footprint, and then use external terminators.

The board requires the same Arduino with the BKM-129X-MCU code, directly programmed to the Arduino with a programmer (read some of my previous posts regarding this).

The MCU code is here: https://github.com/skumlos/bkm-129x-mcu

The Kicad project for the board here: https://github.com/skumlos/bkm-129x-simple-full

The 3D printable bracket here: https://www.thingiverse.com/thing:4462796

Card inserted to PVM-9L2
Two 9L2s connected, bottom is the full size card (before bracket)
SNES with passthrough

Please copy, share, produce and become rich, of this 🙂