repair in spite of the asshole manufacturer

Disclaimer: I am not a trained service technician, nor do I have any significant background in electronics. I am discussing my bad customer experience with Samsung here, but while this is obviously a manufacturer that I will avoid in the future, it seems that there are many other companies that engage in the same or similarly rotten business practices. I hope this text might be useful to other people that want to repair their broken devices but I am sure there is room for improvements to what I did.

It has been around 13 years since I had made the mistake to buy a washing machine from Samsung: A 7kg EcoBubble machine with a direct drive motor. (Younger people may think that it is normal that washing machines must be replaced every 10 years.. take the word of an “older” person: no it isn’t!)

I had already been forewarned of the poor product quality of this washing machine when some years ago I had had to replace the broken aluminum drum mount. (Service technicians may tell you that these things used to last forever before some manufacturers started to replace the stainless steel with less durable aluminum. Now you are lucky if the “modern” garbage lasts 10 years..)

Some fine morning, the machine then to longer worked at all: No beeps nor display – just dead. Since I had just had a similar experience with my old Siemens dishwasher some month earlier, I thought that I’d just grab the broken electronics PCB and send it to a technician to have it repaired. (In the case of the dishwasher that had worked fine: at a modest cost of around 70€ the technician had replaced the few broken components on my old PCB.)

But before sending this washing machine PCB anywhere I wanted to be sure that the problem actually originated from that thing – and not from the separate PCB used for the control panel, or from some other part of the machine. Most PCBs have test points where you can use a multi-meter to easily check if main power rails function correctly (etc). So I thought that I’d just grab a service manual and my multi-meter to do some testing.. how hard can it be?

Very. Very hard indeed – if you made the mistake to buy from an asshole manufacturer: Samsung provides no schematics for respective devices and you are lucky if you can get your hands on a service manual at all (which doesn’t contain any meaningful technical information anyway). With regard to defective PCBs respective Samsung “service manuals” actually tell the technician to always replace the complete board. They basically pretend that a component level repair is not possible.

When I tried to find respective replacement PCBs online, these where few and far between, and sold at prices around 200€ (without shipping). The problem of actually finding respective replacement boards will not diminish in the coming years (whereas the individual defective electronics components used on the boards are so much easier to find).

In the case of my Siemens dishwasher that garbage company had shown a similar attitude with regard to board schematics (i.e. unavailable to customers), but still there had been technicians capable (and willing) to perform component level board repairs. However a respective search in the “Samsung washing machine” context was a total failure.

Looking at the actual PCB it quickly became apparent why nobody wants to touch those things. (This isn’t 100% correct and in places like India there are actually technicians willing to deal with this shit.) Samsung decided to drown the complete PCB in a thick layer of silicone-paste (about 5mm on both sides – gluing the PCB into its plastic enclosure – see photo above). One could be tempted to think that Samsung specifically uses this approach to hinder any repair attempts.

Officially Samsung uses the pretext that the silicone goo protects the PCB from moisture and technically this is probably not incorrect. However my impression is that the silicone layer also has the effect of a thermal insulator causing covered electronic components to run unnecessarily hot. In my experience excessive heat is usually detrimental to the longevity of respective components. Interestingly the conventional PCB in my dishwasher had lasted longer that the “humidity-protected” PCB in the Samsung washing machine, the benefits of a respective “humidity-protection” therefore seem very questionable to me (particularly in light of the massive negative trade-offs with regard to cooling, extra production cost and repairability).

For lack of technical documentation my means of diagnosing the problem of my machine where obviously quite limited. But after verifying that the 220V mains power actually got to the PCB and that some “weird” voltage got sent on to the control panel PCB, I guessed that something was indeed wrong with the main PCB. And since nobody was going to repair that thing anyway, there wasn’t really anything that I could possibly make worse.. so why not give it a try myself?

First I had to get that PCB out of the plastic enclosure. (People sometimes just drill out big holes from the back so they can access the back side of the PCB – while leaving the PCB in what remains of the damaged enclosure. Given the thickness of silicone layer that should usually work without damaging the PCB traces.) However I did not want to take the risk of potentially cutting too deep, and decided to get the PCB completely out of the box:

I first removed the silicone around the edges (the safest approach is to use some sort of plastic pick to dig out the silicone without damaging PCB traces, etc):

To then loosen the PCB from the backside of the box, I made a small incision at the side with a Dremel tool. I then first pried through that hole with a flat spatule and then used an air-gun to make the silicone detach from the box. (The small hole could later be easily closed with some hot glue – keeping the original box intact.)

Without any meaningful documentation and my limited technical background my means to find “the problem” were unimpressive. I just assumed that something to do with the DC power supply was probably broken. I closely inspected the board for signs of “damaged” components, e.g. if anything had leaked into the silicone layer close to some component. Unfortunately there were no obvious signs but finally I saw at least a minor smudge between the legs of a “STR-Y6473” (switching power supply) – which also seemed to be the “right” kind of component for the problem I was after. Given that this IC was one of the few with a passive cooler, I decided to just extend my replacement spree to anything with a passive cooler (assuming that these might be the fasted aging components). I therefore also removed the “D15XB 60” (single phase bridge) as well as a “MBRF 10200” (Schottky diode). A test with the multi-meter revealed that the latter one was actually burned out. (When measuring nearby components a “68K 2KV” capacitor that had been next to the “STR-Y6473” gave a weird reading – which might just have been a glitch of my cheep multi-meter but I decided to also replace it for good measure.)

During my searches on the Internet I had come across various reports that more recent Samsung machines often seem to suffer from failing motor controller chips. Since I had the PCB already out, I decided that it might be a good idea to also refresh the respective “STK621-728S” chip as a precaution. But in this case I should have stuck to “if it ain’t broken don’t fix it”.. The respective chip is rather annoying to unsolder, and ultimately the new chip from AliExpress was actually broken and I ended up reverting to my original chip which was still OK. ARRRGGH..

So the parts that I actually replaced were:
– 1x STR-Y6473 1.72€
– 1x 68K-3KV 0.15€
– 1x D15XB60 1.96€
– 1x MBRF10200 0.32€
i.e. a grand total of 4.15€ for spare parts:

I wish I could say that I was confident in the outcome of my repair attempt.. I wasn’t. But when I turned on the machine, the lights came on. When I loaded the machine and started a washing cycle it ran without a glitch. This was so much more satisfying than giving more money to an asshole corporation like Samsung (aka LG, aka..).

Those morons want to tell me what I can or cannot do with my property? FUCK YOU ASSHOLES!

Obviously I got lucky that I managed to replace the component that was actually defective – and I probably replaced more than was necessary. But if Samsung wasn’t such an asshole company, then they would not try to prevent people from repairing these devices in the first place: They would make available the information necessary for component level board repair and not burry the components under a thick layer of goo. Random electronics technicians would then be able to properly fix that stuff and I would gladly pay them for their effort.



Addendum: If the above did not already convince you to avoid asshole companies that try to rip you off with deliberately bad products, you may want to have a look at the below video. It seems that more recent “washing machines” are only getting much worse and the list of asshole companies keeps getting longer… https://www.youtube.com/watch?v=crzZEvFf_L4 Without consumer pushback these assholes will just keep going. Please push back hard to avoid the dystopian future that they have planned for us!

you used to be able to repair what you own..

I recently came across some of my old VHS video cassettes. The few cassettes that I had kept had sentimental value to me since those where the kind of personalized recordings that you might have brought back as a vacation souvenir back in the days.

I also found my old VCR tucked away in some cardboard box. It must have been some early ninety’s model back from an era when model release dates where not published on the Internet yet.

I had strong doubts if those video cassettes might even still work after all those years. But I thought that this was probably my last chance to still recover what was left of them. So I went and bought one of those cheap “VHS to DVD” USB dongles that allow to record your old VHS treasures on PC hardware.

However fate didn’t seem to be on my side and shortly after turning on the VCR, thick white smoke started billowing out of the device that had been sitting in a box for the past 20 years.


Given my ignorance of electronics this wasn’t what I had hoped for. But this was a somewhat rare device that played PAL as well as NTSC standard cassettes and it wouldn’t be easy to find a replacement. So I curiously opened the device to have a look if some obvious defect might be visible. And indeed it was: Some TPW3300AD component (whatever that was) obviously had a hole that didn’t belong there originally.

Luckily this was a ninety’s device and you find service manuals for that thing! (see
https://archive.org/details/manual_V854G_SM_TOSHIBA_EN/page/n93/mode/2up). And better still, you actually still find the respective spare part for 12€ in 2024!

So I ordered that part and fired up my soldering station and a few moments later the thing had been replaced (thanks to the manual I also found the type of the toasty diode that was installed underneath and which I also replaced just in case). I also found that some rubber belt at the bottom of the device had completely decayed over the years and so I replaced that as well.

Full of anticipation I then again started my VCR and the joy lasted about 30 seconds – before white smoke once again came pouring out of the device. Since I had left the device open, the source was immediately identified this time: Two “security capacitors” had blown up. (I suspect that whatever isolating oil is usually used in respective paper/metal caps might just have disappeared in my old device.. and I therefore thought it might not be a total waste of time to just try and replace them). Since all the smoky problems of the device had originated from the same high voltage / power supply area of the circuit board, my wishful thinking led me to believe that if there was actually a different root cause for the problems, it might also be somewhere in the power supply circuitry. But I didn’t see anything suspicious and decided to also replace a big 47nF electrolyte capacitor just for good measure (since those are said to dry out over the years).




Bingo! The next time I powered up the device there wasn’t any smoke! But unfortunately it also didn’t play for more than 2 seconds.. and instead almost immediately switched back to “standby” mode. This was getting annoying and maybe the burned up parts had actually caused damage to other parts of the electronics.. but who would give up now?!

Youtube to the rescue! On a channel called 12voltvids I found what might be useful for my patient, i.e. how to clean the “mode switch” and other mechanical parts. And with the help of the above service manual I then found how to properly realign the respective mechanical gears to properly put everything back together.


Hurray! It worked! The cassettes that I was interested in are playing flawlessly and are on their way into the digital age 🙂

(damn, it’s really been some time since that “rescue diver” course..)


Lessons learnt:

  1. never give up
  2. being able to repair your stuff is useful (don’t let greedy companies take that right away from you)
  3. electronic devices decay automatically (some faster than others)

I wonder how many of the garbage throw-away products that you buy today you’ll be able to repair in 30 years (or even next month)….

Note: In case you ever try to record NTSC videos using a PAL video recorder (like the above): The signal generated by respective devices is typically neither correct PAL nor NTSC but a mixture of NTSC’s 60 frame refresh rate with PAL’s color signal encoding. The resulting mode is typically called PAL60. If you are trying to use a STK1160 chipset based USB dongle (cheaply sold on Amazon for around 10€) you’ll find that the supplied Windows device drivers do NOT know that mode and you’ll typically end up with black&white videos. The drivers bundled with recent Linux kernels do not have this limitation and you can there use tools like VLC to get the job done (see menu “Media/Convert Save/Capture Device/”; use “Capture mode: Video camera”, “Video device name: /dev/video0”, “Audio device name: hw:1,0”, “Video standard: PAL60”).

why you should not use AudioWorklet

Note: The below text is rather technical and meant for Web developers/architects that have an interest in respective WebAudio related infrastructure.

I have been successfully using the old (meanwhile “deprecated”) ScriptProcessorNode infrastructure since around 2012. Since 2018 there is a “modern” replacement API which supposedly solves all the “problems” that Google’s browser developers (and their friends in the Mozilla subsidiary) had identified in the old API. The “modern” API has now been around for 5 years and one would think that any teething troubles of that “new” API should meanwhile be resolved. Therefore everybody should replace his old ScriptProcessorNode garbage with the cool “modern” stuff immediately, right?

One of the main flaws of the old ScriptProcessorNode infrastructure is that it “lives” in cohabitation with the browser’s main UI thread, i.e. it competes for the same CPU core that is used for “all” the remaining UI code. This means that if the CPU is “too slow”, then the UI code may interfere with the music playback and vice versa the music logic may negatively impact the responsiveness of the UI. How relevant that concern is will depend on the actual hardware of the end user (and the amount of work that the web page has to perform). I am the proud owner of a 10 year old Intel i7-4790K desktop processor (which I used for all the measurements in the later parts of this post) but users of modern smartphones may obviously be less fortunate.

For context here some measurements with my respective webSID player: The computational needs of that player are somewhat proportional to the number of SID chips (see “MOS 8580″) that have to be emulated. Below a visualization of the impact an increase from 1 to 2 and then to 3 SID chips has on the browser’s UI thread utilization (see quota needed for”Scripting”). In the webSID example the player requires about +6% (on my machine) of the available processing capacity for each additional SID chip that is emulated.



On my machine and for the above example (which doesn’t use much UI logic) there still seems to be quite a hefty reserve of “idle” CPU time. But that picture can change if the UI also starts to use the CPU. Below the same 3-SID emulation from above but this time the UI uses the streamed data of the 12 SID channels (4 per SID) to draw some graphs on HTML Canvas. It is obvious that as a function of rising UI CPU consumption there is some point at which it would be nice to offload the music related CPU use to somewhere else so that the UI has the complete capacity of the CPU core available to itself.



Let’s try out the AudioWorklet infrastructure and see what happens! For the sake of the experiment I am using my old ScriptProcessorNode based “V2” music player (the old version can the tested here: https://www.wothke.ch/webV2M/). You can try out yourself how well or how badly it fares on the device of your choice. (Let me know your experience/results in the comments!)

I’ve created a new AudioWorklet based version of that player that you can test here: https://www.wothke.ch/webV2M_worklet/



The page actually delivers on the “move CPU load elsewhere” goal: Below on the left the old ScriptProcessorNode based implementation and on the right the new AudioWorklet based implementation (these are measurements from the two pages liked above, i.e. the UI needs some CPU to draw the 16 voice streams). On the hardware that I am using, the new implementation frees up 15.6% “Scripting” of the available CPU capacity to whatever else the UI might want to do with it (at the same time there seems to be a +0.7% increase of “System” CPU use – something that one might want to keep an eye on in specific use case scenarios).



So it’s “mission accomplished”, right? Let’s go for it! Or why not? Unfortunately there are some severe trade-offs.. and once again history seems to be repeating.

You remember that incident when the Google “hotshots” (and their vassals) set out to protect us users from pages to play unwanted audio some years ago? Where they then came up with that brilliant “solution” that forced developers to rewrite their existing code to add “user clicking” facilities to be used before any audio could ever be played (uselessly wasting endless amounts of the developer’s time all over the world just to make sure that the user now has to first click the “claim your free i-Phone” button – before the page screams “welcome to porn-emporium” from your office cubicle)? Well done geniuses…

Working with the AudioWorklet infrastructure I often got the impression that once again it was the clueless intern that was tasked to design the new API.. 😦 Below some additional observations I made in the brief period that I prototyped my respective V2 player.

1) The new API adds multiple levels of asynchronous behavior during the construction phase of the AudioWorkerNode. In scenarios where the new code (AudioWorkerNode) is supposed to replace existing old code (ScriptProcessorNode) in the context of existing surrounding code this may be really annoying.

In the old ScriptProcessorNode model there already was some async handing needed if you were using WebAssembly, i.e. the ScriptProcessorNode needed to use some kind of callback to report when the WebAssembly part was actually ready to be used. (If your existing code doesn’t already use that approach.. then life will be even more interesting for you.)

In the new model a “processor” first has to be loaded asynchronously and this is the 1st callback (Promise) that you’ll need to handle before you can even create the AudioWorkletNode. By default that load will report back before the WebAssembly code that it may be using is actually ready to be used. And the only way to get information about what the state on the “processor” side is, is via asynchronous messaging (both ways). Or alternatively you might use the SharedArrayBuffer construct – which comes with severe limitations of its own. And all of this adds more levels of asynchronous callbacks to your code. In my use-case the “processor” obviously isn’t ready to play anything before it has the actual music file that should be played, so that has to again be initialized via asynchronous messaging (two ways before your UI side player code actually knows what the current state is). Other players may need to load additional chains of additional files (e.g. instruments) before they are ready to play – adding more respective roundtrips.

The added complexity obviously affects the whole software development process (from the design of the more complex solution to the debugging of potential problems).

2) To make the above “more interesting”, the designers of the API have added some “random” limitations into the mix. For example there actually are “Atomics” APIs that are designed for “concurrency” scenarious, and which allow to let one thread wait for some shared memory to be updated from some other thread – which makes sense since it potentially avoids wasteful “busy waits”. But the designers blocked use of these APIs from the UI thread – since it “would potentially be bad for user experience” (e.g. if a baboon doesn’t know what he is doing while using it).. so now it is “busy waiting” for everybody instead. Or you can completely rewrite your existing code from the ground up to embrace the new model – which you probably had intended to do from the start, right? “welcome to porn-emporium”!

3) SharedArrayBuffer seems to be one of the nicer and more useful features in the above context. But after the turmoil surrounding the Spectre exploit a few years ago, commonly used browsers had already temporarily blocked that feature completely in panic. So if you invest your time to write code based on the “modern” infrastructure be aware that this rug might be pulled from under your feet at the blink of an eye. Certain APIs apparently are deemed to be “dangerously powerful” and I am amazed to see how the geniuses at Google address these threats. In the case of SharedArrayBuffer you now have to make specific webserver configurations just to get Chrome into the mode which should actually be the default. “welcome to porn-emporium”!

4) I found tool support for the AudioWorklet infrastructure (e.g. Chrome’s DevTools, etc) ridiculously lacking – considering that it is a “feature” that has been officially released 5 years ago. I find it quite strange that respective flaws have apparently gone largely unnoticed – and one explanation that comes to mind is that the feature might not actually be used to the extent that its proponents want to make us believe. Examples: Chrome (v117) does not even show in the “Network” tab what “processor” files it has actually loaded and worse it keeps loading old cached versions of “processor” files instead of the updated versions of those files (not always) – even after shift-reload and browser restart (eventhough Chrome does load the correct version of the exact same file if it is directly opened via its URL). Imagine the fun that you’ll have when trying to figure out why some of your users see a different versions of your code than what you have actually deployed! Even diagnosing simple syntax errors in the “processor” file becomes unnecessarily challenging when Chrome doesn’t give a proper error message and basically only tells you that it could not use the respective code due to “some problem in your ‘index’ file” (that behavior actually seems to depend on the specific type of syntax error and a stray ‘.’ will break the debug info where a stray ‘a’ might not.. LOL). “welcome to porn-emporium”!

5) Google uses AudioWorklet to push its https agenda – eventhough there is absolutely no rational explanation why people should not be allowed to load music playing pages by whatever means THEY chose to use. But Google always knows better what is best and will protect you from yourself if necessary! Therefore pages that use the “modern” AudioWorklet cannot play music when loaded via http. “welcome to porn-emporium”!

6) It is an obvious precondition for realtime music players that the logic producing the music must not take longer than it takes to play the produced audio data. Violations of this precondition will potentially interrupt the continuous playback and lead to clicking or stuttering noises. In the case of the old ScriptProcessorNode it may typically be a 2048 (it may range from 256 to 16k) sized output that is produced, corresponding to 0.046 seconds of audio (at the typical sample rate of 44100Hz). For AudioWorklet that output is always 128 sized, i.e. the interval is typically 16x shorter (and may be up to 128x shorter as compared to the ScriptProcessorNode implementation). This means that in the ScriptProcessorNode case, variations in the actual processing needs can have temporary peaks (e.g. GC kicking in or waveforms that are more expensive to calculate than others) without hurting the playback – as long as the average processing time fits into the available longer time window. Whereas AudioWorklet is much more sensitive to variations – which must average out within each of the much shorter 128 -samples windows. If respective processing time variations are an issue in your existing audio generation logic then you might need yet additional workarounds (e.g. separate Worker to produce larger buffers in the background) when migrating the code to AudioWorklet.

7) I don’t have a smartphone to use my pages on, so I did the “next best” thing to at least get an indication for what to expect by running Chrome-DevTool’s “Lighthouse” analyzer. (Based on past experience respective Lighthouse reports have to be taken with a large pile of salt.) But I was interested to see how the two version of my V2M player supposedly score for Desktop as compared to Mobile devices in the categories “Performance” and “Best practices”. The 3 year old Chrome version that I used first reported 100/100 for both versions and all scenarios except for the old version (ScriptProcessorNode) on Mobile that scored “only” 98/100.
I then repeated the same test on the recent version of Chrome (v117) . Here the old version scored 99/100 on Desktop and 76/100 on Mobile. While the new (AudioWorklet) version scored 97/100 on Desktop and 76/100 on Mobile (i.e. performance supposedly worse on Desktop than the old version..). So these test results suggest to me that for this page the end user experience will be pretty identical for both versions of the page.
I did reach a point where the new version actually performed better than the old version when I simulated a “6x CPU slowdown” via Chrome’s DevTools. However the prospect of having code still work on older/slower devices may be deceiving: Users of respective old devices may often chose to not update their OS and/or browser to the latest versions and other features that you might want to use on your pages (e.g. ES6 classes, WEBGL2, etc) would prevent them from even using your page anyway.

8) From an audio visualization perspective, AudioWorklet’s 128-samples output is an advantage. You no longer need the hacks needed in the ScriptProcessorNode scenario in order to show the currently played data with a precision that exceeds the playback length of the used audio buffer.

9) Be careful to not prematurely jump at any conclusion with regard to some audio playback issue that you might be having. At some point I found that one of my pages produced ugly clicks in Chrome during its audio playback for no good reason: When analyzing the CPU load in the DevTools, respective overall load was well below 50%. But it got really interesting when I then tried to “Record” a “Performance” trace using the DevTools. As soon as the DevTools were in “Record” mode the same song that glitched in “normal” mode suddenly played flawlessly (and it restarted to glitch as soon as I stopped to “Record”). When the browser implemention is garbage you might end up getting the short end of the stick regardless of which particular feature you are using. “welcome to porn-emporium”!


So to return to the clickbait title.. should you use AudioWorklet (as a replacement for existing legacy ScriptProcessorNode implementations or in general)?

I’d say only if you verified that you actually need its potential benefits. The extra price you pay for using the “modern” API is quite high (in terms of extra software development, testing, deployment and maintenance cost, unnessessary lack of http support, extra technological risks, cross-browser issues) and often the “old” API is totally good enough and much easier to use. If AudioWorklet solves a problem that you actually have (only) then you should go for it.

“Google’s” approach of “deprecating” the ScriptProcessorNode and pushing the use of AudioWorklet resembles the move of an imaginary “handyman’s guild” to “deprecate” the use of nails and pushing the use of screws.. “welcome to porn-emporium”!

1990 video card nostalgia

The purpose of this post is to preserve the little information that I still have about my first “serious” programming project (for my personal entertainment).

Back in 1990 – while most of my friends from secondary school had to do their mandatory Swiss military service – I had used that time to work on software for the brand new “Chili” video card. The “Chili” was an extension card developed by a small company (“Marvin AG”) from Zürich, that plugged into the VME-bus of an Atari TT or Mega STE home computer. The hardware side had been designed by two electronics engineers from ETH.

A friend of mine and I were brought in mainly to create the “paint box” software (“Chilivision”) that was bundled with the card: Functionality-wise the program resembled other programs of the time (e.g. DeluxePaint on the Amiga, Photoshop, etc) and the main difference was that the user had to toggle between the tool palette permanently shown on the Atari’s standard black&while display (settings, etc) and the output of the “Chili” card shown on a separate color display (color picking, interacting with the image).

We had split the work: My buddy hacked away at the AtariST specific B&W UI in some programming environment called “GFA Basic” and I primarily implemented all the timing critical graphics algorithms in 68k assembly (remember: the Motorola 68k CPU used in the Atari was quite slow) – e.g. drawing lines and other shapes and filling them (etc).

It was a fun time: we would usually start our programming sessions some time in the evening and then program until the sun came up. Later there were the trips to CeBIT, creating flyers, decorating and manning the booth – trying to promote the product.

Press coverage of the time can be found in the “TOS” magazine, e.g. “Das kann die neue Videoeffekt-Karte Chili” page 24 of the 06-1990 issue or “Monitor an, Chili läuft” page 16 of the 09-1990 issue (please let me know in case you find additional articles).

Unfortunately my respective Atari MegaST equipment seems to have been stolen/lost during one of my relocations and the screenshot used in one of the above “TOS” reviews is probably the last thing I’ll ever see of our respective “Chilivision” program. (Why couldn’t they take the Apple II instead..?!)

The two electronics engineers that created the ‘Chili’ hardware had initially also implemented a number of real-time video effects (mosaic, sphere, etc) and I remember later also programming respective DSP machine code for additional effects.

With my old hardware gone, the only thing that I still found were the below low quality screen recordings that I had made (probably 10 years after the product was long dead) using a cheap PC graphics card. The bad quality is due to my recording equipment (the “Chili” card’s original output had something like S-VHS quality).

effects performed in real-time on continuously grabbed video images
overlay of Chili’s image buffer output over video input signal
using the card without video input

Sadly the ‘Chili’ product was no success. From the get go the product was quite expensive (around 3’000 CHF) – much more than the Atari computer that it was designed for had ever cost. And the number of people that were willing to spend than amount of money was rather small. Then it did not help that there were simular products that came out at the same time – like the “Video Toaster” for the Commodore Amiga.

But ultimately the hardware seems to have had one fatal flaw: The production costs had originally been expected to come down with larger production volumes. But apparently the fragile tolerances used in the hardware design meant that pre-assembled PCBs often had to be analyzed and extensively hand-tuned by the two electronics engineers in order to actually make them work correctly. No margin to scale up production or bring down cost.

For me it had become clear that I wanted to continue to write software and to improve my skills, the university of Zürich would be my next stop to learn a new thing or two.

playing with shadertoy

Shadertoy is a nice little project that lets users program simple WEBGL fragment shaders that are then directly published as pages on their site. The subset of WEBGL features available for use on respective pages severely limits what can and what cannot be programmed on shadertoy.com. But as the name implies the site is meant as a playground that allows to quickly play with fragment shader code – without having to deal with the necessary glue code (vertex shaders, etc).

When publishing their fragment shaders, users also publish their source code, something that other users can then tinker with (credits for derived work can usually be found in the comments, or code may directly be marked as “forked” from somewhere else).

I sometimes play on the site myself and below a selection of respective shaders that I have done these last few years (if your device offers WEBGL2 support, you might want to directly look at the respective pages on shadertoy.com):

2019

twisted thingyhttps://www.shadertoy.com/view/3dXSDB

isolines & capsuleshttps://www.shadertoy.com/view/wsSXzh

isolines https://www.shadertoy.com/view/3sSSRR

2020

kleinian skullshttps://www.shadertoy.com/view/wtsyRB

RGB glitchhttps://www.shadertoy.com/view/WlXcDr

deep-sea mandelboxhttps://www.shadertoy.com/view/wllczj

fractional part patternshttps://www.shadertoy.com/view/wtXczf

colored moirehttps://www.shadertoy.com/view/wtXczl

2021

warrior lissajoushttps://www.shadertoy.com/view/NllGDn


2022

Fork of speakershttps://www.shadertoy.com/view/DtlGW8

2023

neon lights tunnelhttps://www.shadertoy.com/view/cddSRM

Tequilla Rainbowhttps://www.shadertoy.com/view/ddy3DD

audio reactive Discoteqhttps://www.shadertoy.com/view/mlfBDX

metaballs v0.2https://www.shadertoy.com/view/cldBzN

an old player revisited

Ten years after my first sc68 web port I thought it might be time to update the code base to the latest sc68 dev version. This new version supposedly has improved SNDH playback capabilities and I used the occasion to play with WEBGL and also add a visualization of the emulation’s internal voice streams. A live demo can be found here: http://www.wothke.ch/webSC68


PS: I updated PlayMOD to now also use this version.

m68k reminiscences

I had done the original Web port of UADE back in 2014 without bothering about its existing implementation. Though I had owned an Amiga back in the nineties, I had never actually written much software on that machine. And though I had done some m68k assembly programming on the AtariST I cannot say that I still remembered much about the respective Motorola 680×0 CPUs.

But it bothered me that UADE’s emulation still doesn’t support certain Eagleplayers – after all these years (i.e. it is incapable to play certain Amiga music files). So I decided that it might be a fun exercise to fix at least some of those flaws myself.. “If you want something done right, do it yourself”… right? 😉

The linked page shows my resulting webuade+ version: As compared to the original, this version has an added “audio.device” implementation, added multi-tasking support and added support for shared library loading (in addition to various small fixes). It supports (at least) these additional players: Andrew Parton, Ashley Hogg, PlayAY (except ZXAYEMUL), Digital Sound Creations, FaceTheMusic, G&T Game Systems, Janne Salmijarvi Optimizer, Kim Christensen, Mosh Packer, Music-X Driver, Nick Pelling Packer, TimeTracker Titanics Packer, UFO and some Custom modules.

While this little “project” was a fun “code reviewing” exercise (trying to make sense of UADE’s original m68k ASM and C code based emulator implementation), with some reverse engineering (disassembling portions the Amiga’s Kickstart OS) thrown in, it was also a stark reminder of what it meant to program back in the days..

software archaeology/puzzle..

This was the first time that I’ve tried my luck at reverse engineering a Windows *.exe with the goal of porting the respective functionality to the Web. Matter-of-factly it was a rather pointless (who needs another old music player in 2022?) but fun undertaking, that gave me a pretext to play with some new tools and refresh my “code reviewing” skills. The hobby project had one clear win condition: The result would either work perfectly or the project would end as a humiliating defeat..

To come to the point: the project was a success that can be tried out here: www.wothke.ch/webIXS

But lets take a step back shall we? “IXSPlayer Version 1.20” was originally created by the no longer existing “Shortcut Software Development BV” about 20 years ago.

The music player belongs into the “Impulse Tracker” family but what sets it apart is the way by which it generates the used audio sample data. At the time it must have looked like a promising idea to save the limited Internet bandwidth by using the smallest music files possible.. and via compression and audio synthesis this player uses ridiculously small song files that are only a few thousand bytes long (see status information in the player widget). For comparison: mp3 files are typically several million bytes (megabytes) long.

As we now know, Internet bandwidth was about to evolve quite dramatically and only a few year later, people could not care less how many megabytes some silly TikTok or Youtube video might be wasting. So ultimately the idea with the micro music-files finally did not get much traction.

Music files for this player can be found on the Internet (see modland.com). In the modland.com collection respective files are listed under “Ixalance”. I am therefore also using that name here.
The only *.ixs format player that Shortcut Software ever seem to have released to the public is a small Win32 demo-executable:

This demo player obviously only works on Windows. It only plays one song at a time (in an endless loop). And there is a flaw in its “generated cache-file naming” which may cause songs to load the wrong files and then not play correctly.

I had gotten in touch with the original developers to check if they might provide me with the source code of their player (so that I could adapt it for use on the Web – like I had already done for various other music formats in my playMOD hobby project). But unfortunately those program sources seem to have been lost over the years. The above Windows *.exe was indeed the only thing left. So that is what I used as a base for my reverse engineering.

Greetings go to the Rogier, Maarten, Jurjen and Patrick who had created the original player at “Shortcut Software Development BV”. Thank you for letting me use this reverse engineered version of your code.

Non-software engineers can safely stop reading here 🙂 All others might find useful information for their future reverse engineering projects below.

Stage 1

The original developers had told me that the player had been an “ImpulseTracker” based design and that it had been written mostly in C++. This sounded promising since C/C++ can be cross compiled quite easily to JavaScript/WebAssembly using Emscripten. I therefore set out to find some decompiler that might allow me to transform the x86 machine code from the *.exe back to its “original” C++ form. To make it short: If you want to do this kind of thing professionally you should probably buy IDAPro – or as a hobbyist like me you can try your luck with Ghidra and the free demo of IDAPro as a supplement (other tools like boomerang, cutter, exe2c, retdec, etc seem to be a waste of time).

What to expect?

A tool like Ghirda has a sound understanding of different calling conventions used by typical C/C++ compilers (__fastcall, __stdcall, __thiscall). Based on the stack manipulations performed in the machine code this allows it to correctly identify the signatures of the used C/C++ functions 95% of the time (Ghidra struggles when FPU related functions are involved but that can be fixed by manual intervention, i.e. overriding the automatically detected function signatures). Obviously respective tools also know most of the instruction set of the used CPU/FPU which in this case allowed Ghidra to translate most of the x86 gibberish back into a more human readable C form:

With no knowledge about the used data structures that code is still quite far from the C code that this will eventually turn out to be:

The decompiled code will often be a low-level representation of what the machine code does – rather than what the original C abstraction might have been, e.g. though technically correct the below code:

in the original C program would probably rather have read:

Similarly the “1:1” mapping of the optimized machine code:

must still be manually transformed to its “original” form:

Most importantly in order to get meaningful decompiler output it is indispensable to find out what the used data structures are.

But before diving into that jigsaw puzzle it makes sense to narrow down the workspace: In my case I knew that I was looking for IXS music player logic – while most of the executable was probably made up of uninteresting/standard VisualStudio/MFC code (some of which Ghidra was able to automatically identify). String constants compiled into the code then allowed me to identify/sort out additional 3rd party APIs. (The relative position of stuff within an excutable is useful to get an idea of what belongs together.)

After a tedious sorting/tagging process, the result was a set of functions that *probably* belong to IXS library that I am looking for – and that I could now export as a (still incomplete) “C-program” at the click of a button (since Ghidra does not seem to be suitable for an iterative process there was no point to do that just yet).

Time to identify data structures

I had been tempted to presume that virtual function tables should be one aspect of C++ code that can be easily identified in the machine code – but it seems I was mistaken. Even the additional “ooanalizer” tool – that seemed to be promising at first – mostly discovered useless CMenu (etc) classes – but none of the stuff that I was looking for (its extremely slow running “expert system” approach seems to be incapable to reliably search the memory for arrays that point to existing functions.. something that I had to do manually as a fallback).

At this point IDAPro’s debugger also comes in handy: When dealing with virtual function calls a simple breakpoint quickly eliminates any doubt regarding where that call might be going (this is quite crucial when dealing with Ghidra’s flawed stack calculations whenever virtual function calls are involved ).

The fact that I knew that the code was probably “Impulse Tracker based” obviously helped: When seeing an “alloc” of 557 bytes the chance of it not being an “ITInstrument” is just very slim (luckily there is a specification of the respective “Impulse Tracker” file format). Memory allocation/initalization code per se is a good place to look for the data structures used in a program.

At this point you may not know what the variables mean, but you already know what types they are – and offsets used to access data start to make sense.

Once the modelling of the data structures and the “list of the interesting functions” is reasonably complete, it is time to switch gears and enter the next development stage (it will be inevitable to come back to Ghidra from time to time to clarify open issues and it helps if variable/function names perserve some kind of postfix that allows to match them to the original decompiler output during the later development stages). But not before I mention some Ghidra pitfalls:

Ghidra pitfalls

Most of the time Ghidra works pretty well and I would not have been able to do this project without it. But there are instances where the decompiler fails miserably (I am no Ghidra expert and there might be “power user” workarounds that I am just not aware of.):

In some instances you don’t get around looking at the filthy x86 opcodes one by one (something I had hoped to avoid) to figure out manually what some piece of code actually should be doing:

The above shows the original x86 code on the left and Ghidra’s decompiler output on the right. The code seems to use LOGE2 and LOG2E constants and “f2xml” and “fscale” operations – which are known to be used in “C math pow()” implementations. But what seems to be plausible code at first glance is just total garbage – since Ghidra completely ignores the “fyl2x” operation which is actually quite important here.

Another weak point are arrays which are used as a local variable in some function. Ghidra may turn what was originally a 100 bytes array into a local “int” variable and then happily use those 4 bytes as an “arraybuffer” to poke array accesses into random memory locations. (As a workaround it helps to manually override the function’s stackframe definition. Eventhough this has problems of its own, like Ghidra introducing additional shadow vars for some of the data that is already explicitly defined in the stackframe.)

In general, Ghidra’s calculations with regard to pointers into a function’s stack frame (i.e. its “local vars”) leave a lot to be desired (Let’s say function A (among other local variables) has an array and it wants to pass a pointer to that array to a function B that it is calling.). Here the array address calculated by Ghidra is often just wrong. (Again IDAPro’s debugger comes in as a life saver to figure out what those offsets really should be. It seems save to presume that IDAPro is the far superior tool in this regard. But I guess you get what you pay for..)

Ghirda seems to be out of its depth when stuff is “simultaneously” processed on the CPU and on the FPU – and the decompiled code may then perform operations out of order – which obviously leads to unusable results.

Similarly Ghidra seems to completly ignore the calling conventions declared in virtual function tables. Consequently all its stack position calculations may be completly incorrect after a virtual function call.

Finally Ghidra’s logic seems to go totally bananas when a function allocates aligned stack memory via __alloca_probe.


Stage 2

The now exported “C program” from stage 1 is now ready to become an actually functioning program. At this point I obviously want to make as little changes as possible to the respective code: In order to not add additional bugs to the problems that undoubtedly already are present in the exported code. Also there isn’t any point to start cleaning up yet since there is a high risk that additional “program exports” may still be needed which then would require time consuming code merging.

So the first goal is to get the original multi-threaded MM-driver based player to work – like in the original player, just without the UI. That thing has been originally built using Microsaft’s C compiler and libs? Then that’s exactly what I’ll be using. And this tactic actually worked well: still slightly flawed at first but I got the exported code to actually produce audio for the first time.

Since I am aiming for a single-threaded Web environment, the multi-threading and Microsaft specific APIs have to go next: Standard POSIX APIs are available on Linux and they will be available in the final Emscripten environment as well. Therefore a simple single-threaded Linux command line player that just writes the audio to a file is the next logical step.

The code now works fine on Windows as well as in the Linux version. I am confident that the exported code has sufficiently stabilized and it is time for a cleanup (until now everything was still in one big *.h and one big *.c file – as exported by Ghidra). This is the moment where you want to have an IDE with decent refactoring support. Since I am an old fan of IntelliJ, I decided to try the trial version of CLion for the occasion. And though I found the “smart” auto-completion features of their editor rather annoying, the refactoring worked well (and the UI crap can be turned off somewhere in the settings).

Stage 3

But will it also work on the Web? Obviously it won’t! Intel processors are very lenient with regard to their memory alignment requirements, i.e. an Intel processor does not care what memory address it reads a 4-byte “int” from. And this is the platform that the exported program had been originally designed for. The processors of many other manufacturers are more restrictive and require a respective address to be “aligned”, i.e. the address for a 4-byte “int” then must be an integer divisible by 4. The Emscripten environment that I am targeting here shares this requirement. All relevant memory access operations must consequently be cleaned up accordingly – once that cleanup is done the code actually runs correctly in a Web browser.

Feeding the IXS player output to something that actually plays audio in a browser (see WebAudio) requires additional JavaScript glue code: I already have my respective infrastructure from earlier hobby projects and this part is therefore largely copy/paste that I will not elaborate on here.

But one extra bit of work is still needed: The IXS player generates the data for its instruments whenever a song is first loaded and that operation is somethat slow/expensive and blocking a browser tab for several seconds just isn’t polite. But one solution that “modern” browsers propose is the Worker API that allows to do stuff asynchronously in a separate background thread. This means that the orginal program must be split into two parts that then run completely independently and only talk to each other via asynchronous messaging. Finally there is the browser’s “DB feature” that allows to persistently cache the results of the expensive calculation so that it doesn’t even need to be repeated the next time the same song is played. So that’s what I do: Worker asynchronously fills the DB with respective data if necessary and the Player pulls the data from the DB and triggers the Worker when needed. Bingo! (All that remains to be done now is for the Google Chrome clowns to fix their browser and make sure that it isn’t their DB that blocks the bloody browser.. it just isn’t polite!)

webSID-Pi

In previous posts I had documented some “Raspberry Pi 4B” related ramblings and this post here may clarify why I was interested in that device in the first place.

It’s been some years that I’d created a C64/SID music emulator for the Web called webSID (see https://bitbucket.org/wothke/websid). I still have somewhat fond childhood memories of my respective 80ies home computer which had started my career in software engineering (particularly memories of its signature music creation capabilities).

If you have ever written any sort of music hardware emulation you are certainly well aware that there is absolutely no shortage of “fanboys” that know exactly what some original hardware sounded like and who are happy to tell you that your pretentious emulation sounds egregiously wrong. In the case of the SID sound chip, the manufacturer MOS had produced various models (6581 vs 8580) and revisions (of those models) that had different hardware designs (and production tolerances related differences) affecting the chip’s actual audio output. Consequently the opinions of “fanboys” here tend to be somewhat useless, since they may well be based on one particular chip revision.

Obviously the only authoritative reference is the use of the actual SID sound chips. One way to go about testing respective chips would be to use an old C64. However this approach has some obvious drawbacks: By today’s standards the respective hardware is an extremely slow piece of garbage and it takes forever to even load/run the minuscule programs. Respective 40 year old hardware components (power supply, CPU, PLA and VIC II chips) are prone to die and are more and more difficult to find replacements for. Different songs may require an NTSC or a PAL version C64 (affecting the used clock rate and the behavior of the VIC II chip). Different SID models require different hardware environments, e.g. different supply voltages (9V vs 12V) was well as different filter capacitors.

Given that my webSID emulation already covers all the required runtime environments I decided to ditch the respective non-SID legacy hardware and connect the SID chips directly to a “Raspberry Pi 4B” and drive it via the available GPIO pins.

The below image shows my respective prototype board. The design is actually fairly simple: The SID chip is dropped into the prototyping socket in the center. A boostconverter (red PCB) is used to create the 9V or 12V supply for the SID chip based on the Pi’s 5V. The filter capacitors suitable for the used SID model are plugged in (see green caps between SID and the boostconverter). (I had originally also used a hardwired 1 MHz crystal oscillator but later replaced it with the Pi’s built-in clock signal generator.)

The main problem with running a SID chip from a C64 emulation on a Raspberry PI is that the available operating system is not meant for realtime applications: By default various processes used by the OS are preemptively scheduled to run on the available CPU cores.

Ideally you would want to let the emulation step through the ~1Mhz clock cycles of the emulated C64 hardware one by one in realtime, and you’d have to somehow synchronize that processing with the actual processing speed of the Raspberry (though the overhead for synchronizing each 1MHz cycle would be excessive). The processing cost of different cycles also might vary, and though fast enough on average, sporadic peaks might potentially exceed the available time window for some of the cycles.

Fortunately for practical SID playback purposes the timing constraints can be somewhat relaxed: All of the SID’s timing is directly driven via the Pi’s hardware clock signal and when SID registers are updated respective player routines usually expect to make updates with 50Hz to maybe 200Hz frequency. Even if an update is slightly out of sync, it will usually not be noticeable here. The most timing critical interaction comes from songs that use the C64 CPU to trigger SID changes for the purpose of playing digi-samples. Respective samples may then be played (worst case) with a frequency of about 30kHz (meaning that updates should then ideally occur every 33 micros, i.e. there is some margin as compared to the ~1 micro clock timing).

I decided to go for a two-threaded buffered design: One CPU core is reserved for the emulation thread that generates a batch of SID updates (with respective target timestamps) for the next time window. And a second CPU core is dedicated to a playback thread that consumes the output of the emulation thread and that polls for the current time to perform the SID updates when the time is right.

As a first attempt I configured the program to run exclusively in user space and it actually ran good enough for regular SID songs. Unfortunately the preemtive multi-tasking used by the Linux OS causes timing glitches that are quite noticable in songs that use “digi-samples” tricks.

I tried to limit undesirable interference effects from other programs by isolating use of the relevant CPUs (see isolcpus, rcu_nocbs, rcu_nocb_poll and nohz_full) but unfortunately the remaining 100Hz “arch_timer” IRQs still seem to prevent a clean playback.

As a workaround I then created a kernel module for the playback thread and that version indeed plays nicely without any glitches. But eventhough the respective version may sometimes run fine for multiple minutes, there still seems to be some part of the kernel that sporadically crashes the system – probably due to the “playback” thread completely blocking “its” dedicated CPU core. The test results suggest that this problem might be fixed if the component that triggers the crash can be identified and disabled. Unfortunately I am not familiar with the respective Linux kernel implementation and I currently don’t feel like looking for that needle in the haystack. (Please let me know in case you have an idea how I might hack the kernel to work around the issue.)

Here the results created by the respective programs:

PS: More information on this project can be found here: https://bitbucket.org/wothke/websid/src/master/raspi/

we’ve got fan spin..

It seems that the small cooling fan that came with my Raspberry Pi 4B is much louder than anything that I’ve ever had in my PC. Adding some lithium grease to the fan’s dry bearing had somewhat improved the situation (especially for the longevity of the fan) but it was still way too loud – at times it seemed to be louder than my SID chip music contraption – which had been the reason for me getting a RPi4 to begin with (see https://www.youtube.com/watch?v=bE6nSTT_038 ),

I therefore decided to add some PWM fan control to reduce the noise. Respective instructions can be found here: https://www.instructables.com/PWM-Regulated-Fan-Based-on-CPU-Temperature-for-Ras/ and the wiring was quickly done:

But where to connect the respective control wire? Already there are not that many pins available on the Raspberry’s GPIO connector (many of the pins are redundant GND or 5V pins or reserved for some Raspberry internal purpose) such that less than half of the 58 GPIOs can actually be used. Still worse, all of the available pins are from the low-range (i.e. 0-31) GPIOs and are controlled via the same shared control registers. But since I am already using that low-range for the timing critical logic in my SID chip music contraption, the last thing I need is some CPU fan also messing with those same registers. I therefore started to look for *other* GPIOs that could be used instead.

Luckily there are lots of built-in Raspberry features that I don’t need (e.g. Bluetooth, audio output jack, camera, DSI display control, etc) so many of the “internally used” GPIOs are not actually used on my machine and they might potentially be salvaged for something else. Unfortunately few of the potentially unused lines are exposed such that they could be patched into easily and the miniaturization used on the Raspberry’s PCB is obviously not very soldering friendly. Also many of the unused components seem to be using some I2C bus, for the kind of which the RPi4 has 8 built-in controllers. Unfortunately there seems to be little publicly accessible documentation regarding which devices are actually sharing the same bus controller, so respective I2C pins of some irrelevant device may well share that same bus with some crucial device like the PMIC. Salvaging pins safely here may be risky..

Fortunately I found a low hanging fruit elsewhere: In addition to the main block of GPIOs the RPi4 also seems to have a “GPIO expansion” that hosts 8 additional GPIOs used by the firmware (the expansion also seems to be accessed via I2C). One of these GPIOs is CAM_GPIO, i.e. a pin used to turn a potentially connected camera on/off. I’ll never connect a camera to my RPi4 so this is the GPIO that I’ve been looking for. Even more conveniently the signal can be patched into easily since the camera connector has nice big (for a RPi4) pads to solder to. Works perfectly 🙂 (PS: I blacklisted the camera driver just in case..)

In hindsight: With the temperature and fan speed control in place the result is actually somewhat sobering. It seems that a barely turning fan is totally sufficient to keep the SOC temperature of my RPi4 below 50°C. And I still have to find some computationally expensive “killer application” that will force the fan to actually rev up. It seems that the unregulated fan had been turning much too fast for no good reason. Worse it may well be that a decent passive cooler (instead of the silly RPi badge – see photo above) would cover the actual cooling needs just as well without even using a fan.

Be that as it may.. it was a fun exercise and in case some additional GPIO is needed I now know where to find it 🙂


PS: The C program that I have written to control the fan speed can be found here: https://bitbucket.org/wothke/websid/src/master/raspi/cpufan/

Design a site like this with WordPress.com
Get started