Trois Îles, Luxembourg

SPD AG stimmt für Fusion mit der CDU AG

Bonn ― In einer Sondersitzung der SPD AG haben die Aktionäre heute den Weg für eine Fusion mit dem ehemaligen Konkurrenten, der CDU AG, frei gemacht.

„Das war nur konsequent“, sprach der CEO der SPD AG, Martin Schulz, vor der Presse. „Die Angebotspalette beider Konzerne zeigte schon lange große Überschneidungspunkte und hat sich seitdem immer weiter angenähert. Wir produzieren außerdem schon seit mehreren Jahren in gemeinsamen Fabriken.“

Auch der CEO der CDU AG, Angela Merkel, zeigte sich erfreut. „Hier wächst zusammen, was zusammen gehört. Schon seit Jahren nutzen wir das Know-How der CDU AG, die Marketingfähigkeiten unserer Tochter CSU GmbH und die Arbeitskraft der SPD AG. Und das zu meiner vollen Zufriedenheit! Dieser Schritt war überfällig.“

Der neue Konzern soll den Namen CDU-PD AG tragen. Schulz: „Das ‚S‘ wurde entfernt, weil wir die Produkte, für die dieser Buchstabe einst stand, schon lange nicht mehr in unserem Angebot haben.“

Frau Merkel wird dem neuen Konzern vorstehen. Herr Schulz wird sich ganz um den Abbau der Arbeitsplätze kümmern, die durch Synergieeffekte wegfallen werden. Allerdings seien keine Entlassungen geplant. Schulz ist zuversichtlich, dass die natürliche Fluktuation in dem neuen Konzern völlig ausreichen werde.

Mit einem Einspruch der Kartellbehörden rechnen Schulz und Merkel nicht.

Die Fusion ist innerhalb der SPD AG nicht unumstritten. Insbesondere viele so genannte Zwergaktionäre stemmten sich bis zuletzt dagegen. Sie befürchten, dass nach einer Fusion die traditionellen Produkte der SPD AG endgültig verschwinden werden. „Dabei sehen wir immer noch eine hohe Nachfrage unserer traditioneller Produkte, die von der Konkurrenz nicht bedient wird“, so ein Aktionär auf der Sondersitzung. „Hier wird ein lukrativer Markt einfach liegen gelassen.“

Auch die Börse sieht der Fusion nicht so positiv entgegen. Die Aktienkurse beider Konzerne lagen bereits in der Vergangenheit im deutlichen Minus. Viele Experten glauben, dass die Fusion nicht den erhofften Trendwechsel bringen wird. ◼

Errors in GitLab SonarQube plugin

The sonar-gitlab-plugin is an useful plugin to connect SonarQube with GitLab. After pushing, the branch is inspected by SonarQube, and code smells are immediately commented in the commit.

Unfortunately, the error messages of that plugin are a little difficult to understand. It took me a while to collect this little cookbook of error fixes.

  • Unable found project for null project name
    In SonarQube, as Administrator, select the project. Then in Administration ⭢ General Settings ⭢ GitLab, enter the project ID of your project and save it. The project ID is the group name in GitLab, followed by a slash and the name of the project, e.g. shred/timemachine.

  • Unable found project for mygroup/myproject
    In SonarQube, check that the project ID is correct and there are no spelling mistakes. In GitLab, make sure that SonarQube's GitLab user is actually a member of the project, and that the user has Developer rights. I hit a strange bug in GitLab here. The SonarQube user was a member of the project, but still this error occured. When logging in as the SonarQube user, the project was not on the roll of projects. Removing and adding Developer rights to the user didn't help. The only thing that finally worked was to add the SonarQube user to a different project, even if just for a moment. It seems to be a caching problem in GitLab.

  • Multiple found projects for mygroup/myproject
    You should never see this error, but if you do, be more specific with the projectID.

Recovering old ZX Spectrum tapes, Part 2

The ZX Spectrum was a comparable cheap home computer, and thus the tape loading and saving mechanisms have not been very sophisticated. The tape recording is just a stream of short waves (0 bit) and long waves (1 bit). The stream starts with a leader signal (a series of even longer waves) and a single sync pulse. So, in the theory, reading a tape recording means measuring single wave lengths, by taking the time between two zero-crossings, and converting them into a sequence of bytes.

But then again, we are dealing with 1980's analog technique. In practice, we will find signals like this. A click produced an additional zero-crossing that is to be ignored. Also, the amplitudes and DC offsets change all the time.

And pooof... There went another week of nightly hacking Python code, having very close looks at audio waves, and searching for clues about why tzxwav won't behave like I expect it to behave. But I think the result is worth looking at now! tzxwav now reads almost all of my tape samples without those dreaded CRC errors. If there are CRC errors, the sample was usually so damaged that it would need manual restauration.

And as a bonus, it is now almost twice as slow as before. 🤭 But speed was never a goal anyway, as people are likely to convert their old tapes only once.

Recovering old ZX Spectrum tapes

The author, on Christmas Eve 1985 Since I am in the mood of heavy ZX Spectrum retro action, I dug out all my old computer tapes in an attept to digitize and convert them. It turned out to be more difficult than I thought...

The first trouble was to find a tape player. I had disposed my last tape recorder a couple of years ago. The new ones I found at Amazon looked nice on the first sight. They could be connected to the USB port or even digitize the tapes straight to an USB stick. The customer reviews were scaring off: cheap plastic, poor sound, digitizing to USB stick was only possible on battery power... I had more luck on eBay, where I found a genuine 1990’s Aiwa Walkman (it’s even a recorder, with auto reverse and Dolby NR) in good condition for about the same price.

I connected the Walkman to my computer’s microphone input using a cable with 3.5mm stereo jacks, selected the correct tape type (Normal or CrO2), and turned off Dolby NR. Then I digitized right away, using Audacity for recording and post processing. I used 16 bits per channel, and a 44100 Hz sampling rate. The ZX Spectrum provided a mono signal, so I chose the left or right channel (depending on the quality) and discarded the other one. Mixing down the stereo signal turned out to be problematic, as well as using a lossy file format like ogg.

The WAV files can be loaded into the Fuse Emulator, but it’s better to convert them to TZX files, as they are much smaller. There are a few tools for that, for example audio2tape that comes with Fuse. I wasn’t satisified with the result though, as the generated TZX files contained many CRC errors. MakeTZX is also worth a try, as it supports digital filters, but I was unable to make it run on Linux. Some other converter tools are for Windows only, and thus not very interesting. 😉

So I found myself writing a set of TZX tools in Python. It contains tzxwav, that’s yet another tool for converting WAV files to TZX files, but is robust against poor audio quality. It took me three weeks of work, and about 30 hours of tape material, until it was able to successfully read almost all of my tape recordings.

An advantage of TZX files is that they contain the raw ZX Spectrum binaries, so they are very easy to extract. tzxcat allows to retrieve single binaries from TZX files, which can then be converted into PNG files, BASIC sources or whatever, provided there are converters for it.

What I have now are TZX files of all my old ZX Spectrum tapes. It was very interesting to rediscover my old files, screens, programs and source codes. In 1987 and 1988, I wrote a lot of more or less useful tools, designed several fonts and completed two demos.

Optimizations

On a slow processor like the Z80, it is essential to think about execution time. Often a clean approach is too slow, and you need to optimize the code to make it a lot faster.

The ZX Spectrum screen bitmap is not linear. The 192 pixel rows are divided into three sections of 64 pixel rows. In each of these sections, all the 8 first pixel rows come first, followed by the second pixel rows, and so on. The advantage is that when writing characters to the bitmap, you only need to increment the H register to reach the next bitmap row. The disadvantage is that a pixel precise address calculation is hell.

This is how the coordinates of a pixel are mapped to the address:

HL
1514131211109876543210
010Y7Y6Y2Y1Y0Y5Y4Y3X7X6X5X4X3

X2, X1 and X0 represent the bit number at the address. It can be used as a counter for right shift operations.

My first attempt was a straightforward code that shifted, masked and moved the bit groups into the correct places. It took 117 cycles. This is nice, but we can do better.

We need a lot of rotation operations to shift the bits to the right position. Rotation is a rather expensive operation on a Z80, because there are no instructions that rotate by more than one bit at a time. My idea was to divide the X coordinate by 8 (by rotating it three times to the right) and simultaneously shift Y3 to Y5 into the L register. With a similar trick, I could set bit 14 while rotating, which saved me another or operation with a constant.

This is the final optimized code. It takes the X coordinate in the C register, and the Y coordinate in the B register. The screen address is returned in the HL register pair. BC and DE are unchanged, so there is no need for expensive push and pop operations.

pixelAddress:   ld      a, b
                and     %00000111
                ld      h, a    ; h contains Y2-Y0
                ld      a, b
                rra
                scf             ; set bit 14
                rra
                rra
                ld      l, a    ; l contains Y5-Y3
                and     %01011000
                or      h
                ld      h, a    ; h is complete now
                ld      a, c    ; divide X by 8
                rr      l       ; and rotate Y5-Y3 in
                rra
                rr      l
                rra
                rr      l
                rra
                ld      l, a    ; l is complete now
                ret

It only takes 108 cycles, ret inclusive. Optimizing saved me 9 cycles (or about 8%). This doesn’t sound like much, but if the code is invoked in a loop, those 9 cycles are multiplied by the number of loop iterations.

I claim this is the fastest solution without resorting to a lookup table. Try to beat me! 😁