Developing Retro-Softwares for OS/2 using Sibyl

In the last blog-post I discussed developing retro-apps for Windows that can be run under OS/2 using the WLO toolset from Microsoft. But what about native coding under OS/2 directly?

Since 2003 I'm coding software for lot of different target-systems, like microcontrollers (started with Atmel Studio 4 and the Atmel AT90S2313), personal computers (Borland Delphi 7 for Windows XP) and embedded systems (TI CodeComposerStudio and the TI C2000 Delphino F28335). For Windows I still like to code using Borland Delphi 7 as the compiled code is fast, the IDE userinterface clear and without any bells and whistles and the pascal-syntax is fluent as if you are writing a book. But when it comes to programming for retro-systems, I had a problem: modern compilers are not fine with 16-bit systems. In my last blog-post I addressed this issue by using Microsofts VisualC++ in version 1.52 together with the WLO-library that Microsoft initially released in 1987 for OS/2. While searching the internet for other solutions I stumbled accross a nice looking pascal-compiler for OS/2: Sibyl.

Sibyl, an Object Pascal compiler for OS/2

Sibyl denotes a family of Object Pascal IDEs for OS/2 and was initially released by the company SpeedSoft. Dated back to 2001, one of the available information on the internet are those: "SpeedSoft Sibyl is a high performance Visual Pascal Development Environment (VDE) for OS/2 Warp, Linux and Windows 95/NT. Sibyl allows you to create fast native code applications.". OK, that sounds promising. On several OS/2 sites the Demo-version of Sibyl 2.0 is still available for download. But this version does not allow unrestricted development of new applications for OS/2.

In the Free-Pascal-WIKI I found a promising information: "After being discontinued and open sourced by Speedsoft, development of Sibyl was resumed by Wolfgang Draxler.". Yeah, "OpenSource" and "Resuming" sounded great. But...

WDSibyl, a digital archaeological excavation

enter image description here

Looking at the website of the WDSibyl (https://www.wdsibyl.org) the last update was in October 2020 - and sorrowly the download-page is not working anymore. Man, the information about an up-to-date compiler for OS/2 was too good to be true. As WDSibyl has been published as open-source, I found the sources on GitHub: https://github.com/BAUSYS/WDSibyl, but I was not in the mood to compile the sources without knowing the length of the dependency tail. While searching for "BAUSYS" I discovered a swiss website offering some working downloads for WDSibyl v3.14 from 2010: https://www.bausys.ch/os2_download.html - not the most recent version of WDSibyl, but better than nothing:

enter image description here

So I copied the files to a CD, put it into one of my 486-retro computer with OS/2 Warp 3 and tried to install WDSibyl v3.14. The setup program also ran, but on starting the IDE some very strange error-messages shown up. I quickly realized, that WDSibyl was designed for OS/2 Warp 4 in mind - and that it uses long filenames. As my installation used FAT16 instead of HPFS as filesystem, I got stuck, both in 8.3 filenames - and with a non-working compiler - too bad.

Hacking Sibyl

While downloading the demo version of the original Sibyl from www.os2site.com I realized, that they had the files for the full-version of Sibyl uploaded in 2001. Downloaded and started, the setup of this version asked for a serial-number:

enter image description here

Well, my first though was: google a serial, but this did not offer any useful results as back in the days the internet was not so present yet. On second thought: as this was abandonware and SpeedSoft, the original and long gone company, had published the sourcecode, I thought about developing a key-generator. I downloaded the available Sibyl Sourcecode and searched for some code-parts, that are responsible for the serial-number. Within the file "REGCODE.PAS" I've found the following function:


s:=GetEnv(LoadNLSStr(SiSibylReg));
If length(s)=15 Then
Begin
  s1:=Copy(s,1,3);
  UpcaseStr(s1);
  //IF ((s1[1]<>'S')OR(s1[2]<>'2')OR(s1[3]<>'0')) THEN goto regerror;
  s1:=Copy(s,4,12);
  IF not Check(s1) THEN goto regerror;
End
Else
Begin
  regerror:
    LimitedVersion:=TRUE;
    PostMsg(SibylMainForm.Handle,CM_LIMITED,SiSibylRegError,0);
End;

So obviously, if the check()-function is returning a zero, a registration-error occurs which will result in a limited-version that is only able to create very limited applications (kind of demo-mode). So my plan was to create a reverse-function for the check()-function, to create a list of valid serial-numbers. So first, let's have a look at this check()-function:


FUNCTION Check(CONST s:STRING):BOOLEAN;
VAR i,t,t1,i1:LONGWORD;
  c:Integer;
  s1,s2:STRING;
  a:ARRAY[1..6] OF LONGINT;
BEGIN
  result:=FALSE;

  VAL(s,t,c);
  IF ((c<>0)OR(t=1)) THEN exit;

  //Quersumme der ersten 3 Ziffern muss durch 3 teilbar sein
  s1:=Copy(s,7,6);
  FOR c:=1 TO 6 DO a[c]:=ord(s1[c])-48;
  VAL(s1,t1,c);
  IF ((c<>0)OR(t1=1)) THEN exit;
  i:=(ord(s1[1])-48)+(ord(s1[2])-48)+(ord(s1[3])-48);
  IF (i MOD 3)<>0 THEN exit;

  //1. Zahl muss groesser sein als 3. Zahl
  IF (ord(s1[1])-48)<=(ord(s1[3])-48) THEN exit;

  //1. Zahl muss kleiner sein als 2. Zahl
  IF (ord(s1[1])-48)>=(ord(s1[2])-48) THEN exit;

  //letzte 3 Ziffern müssen eine Primzahl sein
  FOR i:=2 TO 66 DO
    IF (t1 MOD i)=0 THEN exit;

  //1.-3. Zahl ist QuerSumme der letzten 6 Zahlen auf 3 Stellen
  s1:=Copy(s,1,3);
  s2:=tostr(a[1]+a[2]+a[3]+a[4]+a[5]+a[6]+a[6]*25);
  WHILE length(s2)<3 DO s2:='0'+s2;
  s2[0]:=#3;
  IF s1<>s2 THEN exit;

  //3.-6. Zahl ist Querprodukt der letzten 6 Zahlen auf 3 Stellen
  s1:=Copy(s,4,3);
  i1:=1;
  FOR t:=1 TO 6 DO IF a[t]<>0 THEN i1:=i1*a[t];
  s2:=tostr(i1);
  WHILE length(s2)<3 DO s2:='0'+s2;
  s2[0]:=#3;
  IF s1<>s2 THEN exit;

  result:=TRUE;
END;

There are also some helpful comments. Looking at both the code and the comments, we are looking for a 15-character serial containing a 12-digit number with the following properties:

(the individual digits of the serial number are labeled with the letters a to l in the following list)

  1. the cross sum of the digits a, b and c must be divisible by 3
  2. digit a must be greater than digit c
  3. digit a must be smaller than digit b
  4. the number of digits d to f must be a three-digit prime number
  5. the number of digits a to c must follow the equation: (a+b+c+d+e+f) + (f*25)
  6. the number of digits d to f must follow the equation: abcde*f

My first idea was: try out some AI. So I started both ChatGPT and Googles Gemini, put in the code and requested a code that matches the aboves properties. For reversing some polynomial functions this worked fine previously, but after a while it was not able to find a solution. So I coded a reverse-function on my own. The following code might not be the most elegant way, but it works. First, in a valid serial the last three digits must be divisible by 2 to 66 without a remainder. So I checked numbers between 0 and 999999 that matches this specification:


// i is a number between 0 and 999999
skip := false;
for k := 2 to 66 do
begin
  if (i mod k) = 0 then
  begin
    skip := true;
    break;
  end;
end;
if not skip then
begin
  // do further calculation for this i
  s_tmp := inttostr2(i, 6); // convert to six-digit string
end;

OK, now that we have numbers matching the first requirement, we have to make sure, that the first digit is larger than the third one and the first digit is smaller than the second one:


// ord()-48 converts the ASCII-char to a number
if ((ord(s_tmp[1])-48)<=(ord(s_tmp[3])-48)) then
  skip := true;

if ((ord(s_tmp[1])-48)>=(ord(s_tmp[2])-48)) then
  skip := true;

Additionally the cross sum of the first three digits must be a divisor of 3:


quer_abc := (ord(s_tmp[1])-48) + (ord(s_tmp[2])-48) + (ord(s_tmp[3])-48);
if ((quer_abc MOD 3) <> 0) then
  skip := true
else
  s_prime := s_tmp; // specification met, so take this part of serial

Furthermore, there are two calculations building a sum and a product of different parts of the serial-number. If you have a look at the code above you will realize, that there is a bit more calculation, than the comments suggest. So I had to find a reverse-function to match the check-function:


quer := (ord(s_tmp[1])-48) + (ord(s_tmp[2])-48) + (ord(s_tmp[3])-48) + (ord(s_tmp[4])-48) + (ord(s_tmp[5])-48) + (ord(s_tmp[6])-48) + (ord(s_tmp[6])-48)*25;
s_quer := inttostr2(quer, 3); // convert to three-digit string

prod := 1;
for k := 1 to 6 do
begin
  if ((ord(s_tmp[k])-48)<>0) then
    prod := prod * (ord(s_tmp[k])-48);
end;
s_prod := inttostr2(prod, 3); // convert to three-digit string

Finally, a possible serial-numer can be generated with combining the cross-sum, the shown product and the prime-number:


// concatenate individual strings to final serial
s_serial := 'S20' + s_quer + s_prod + s_prime;

Brute-forcing the variable i between 0 and 999999 and cross-checking with the original check()-function gave me in total 1.760 valid serial-numbers - enougth to install the original Sibyl on my OS/2 Warp 3 (bruteforcing with the check() directly would be another option). Here is a selection of some of the 1.760 valid serials:

S20030002120011 S20186014120017 S20238018120019 S20033008120041 S20189056120047 S20241072120049 S20086030120053 S20242090120059 S20191084120067 S20088042120073 S20192098120077 S20244126120079 S20038018120091 S20194126120097 S20030002120101 S20082006120103 S20239018120119 S20032004120121

Creating my first native application for OS/2

After a few days of wandering around the internet in old software-repositories and programming a key-generator I finally had a working compiler. As I like to connect hard- and software, my first program should talk to the serial-port of my computer. As Sibyl does not know anything about the serial-port, I found some additional components for the IDE. The serial-port component need some revision to work with Sibyl 2.0 but in the end I was able to get working components for a serial-port, a colorwheel, parallel processing and some minor components. Because my attempts on installing the FixPack 4 for Sibyl failed, I stayed with the working Sibyl 2.0 FixPack 3 for OS/2 but I'm very excited, that my old OS/2 Warp 3 has a working Pascal IDE with GUI-Editor now:

enter image description here

The full code of my first application looks like this:


Unit mainfrm;

Interface

Uses
  Classes, Forms, Graphics, Dialogs, uComPort, Buttons;
  
Type
  TForm1 = Class (TForm)
    comport: TComPort;
    Button1: TButton;
    Button2: TButton;
    Button3: TButton;
    Procedure Button10nClick (Sender: TObject);
    Procedure Button20nClick (Sender: TObject);
    Procedure Button30nClick (Sender: TObject);
  Private
  Public
  End;
  
Var
  Form1: TForm1;
  
Implementation

Procedure TForm1.Button10nClick (Sender: TObject);
Begin
  comport.CommPort := cpCOM1;
  comport.BaudRate := br38400;
  comport.ParityType := pcNone;
  comport.StopBits := sbOne;
  comport.DataBits := db8;
  comport.FlowControl := fcNone;
  comport.DataType := dtString;
  
  comport.OpenPort;
End;

Procedure TForm1.Button20nClick (Sender: TObject);
Var
  s: string;
Begin
  s := 'Hello from the OS/2-World!';
  if comport.PortIsOpen then
  Begin
    comport.SendData(@s+1, length(s));
  End;
End;

Procedure TForm1.Button30nClick (Sender: TObject);
Begin
  comport.ClosePort;
End;

Initialization
  RegisterClasses ([TForm1, TComPort, TButton]);
End.

Lessons learned?

In a job advertisement published in 2024 the Deutsche Bahn (a german railway company known for its delays) searched for an Admin for Windows for Workgroups 3.11. I'm not sure if they found someone, but the press was quite entertained about this search. Maybe the german Lufthansa searches for an Admin for OS/2 with hands-on-experience on developing apps for OS/2 - then I am their man. Just kidding. It took way more time to get a running system here, but it showed me, that for every problem there is a solution if you are persistent enough.

Maybe these information are useful for other nostalgics like me who want to work on their retro-computers under OS/2. I am bound to OS/2 in version 3 for reasons of nostalgia as this was my first operating system besides Windows 3.11. Newer releases would be much easier to handle, but for historical preservation reasons alone, it was worth the trip.

Comments