The TopSpeed Modula-2 compiler for DOS and OS/2 by Jensen and Partners International had been rather popular in late 1980s. There are millions of lines of TS Modula-2 code still used in various environments. Unfortunately, the world is moving to Win32, and TopSpeed users may only watch this process as their compiler is no longer available as a standalone product, and is not actively developed, marketed, and supported by its new owners. So there is a need for them to simultaneously change the compiler and the target operating system.
XDS Modula-2 compilers, available for Win32, OS/2, and Linux, are likely to be considered as a replacement for TopSpeed. But moving from TopSpeed to XDS can be difficult: the JPI implementation of the language is based on PIM, whereas XDS follows the ISO 10514 standard, which is not a superset of PIM. In addition, both compilers provide different sets of language extensions.
That's why the TopSpeed Compatibility Pak (TSCP) has been developed by XDS. The XDS compiler which is part of TSCP has one additional option --- TOPSPEED. Setting it ON enables a set of language extensions that makes the compiler more compatible with TopSpeed. These extensions are described in the following sections.
TSCP also contains a set of TopSpeed-like library modules.
The existing users of your DOS and/or OS/2 software products are not likely to move to Win32 at once as you do it. So it is desirable to be able to compile the same sources with both TopSpeed and XDS. To facilitate this, XDS supports TopSpeed conditional compilation syntax.
Note: In TopSpeed, an identifier used in a conditional compilation clause must be a boolean constant, either predefined, defined in the project file, or in the source text. In XDS, it has to be an option name.
The __XDS__ option is always set to TRUE in XDS Modula-2, so we recommend you to define the __XDS__ constant in your TopSpeed project file:
#pragma define(__XDS__=>off)
and then use the above scheme to isolate TS- and XDS-specific portions of code. See example in section Pragma syntax.
TopSpeed alias declaration (CONST id1 ::= id2;) is available in XDS if the option TOPSPEED is turned ON.
Types LONGCARD, LONGINT, SHORTCARD, and SHORTINT are not part of ISO Modula-2, but they are available in XDS Modula-2 if the M2ADDTYPES compiler option is switched on. Usage of types SYSTEM.INT8, SYSTEM.INT16, etc. may also be considered.
TopSpeed Modula-2 types TEMPREAL and FIXREAL have no analogs in XDS.
XDS compilers generate 32-bit code, so the base Modula-2 types, such as CARDINAL, INTEGER, and BITSET, are 32-bit by default, whereas in TopSpeed Modula-2 these types are 16-bit. To achieve better performance, it is recommended to use 32-bit types anywhere except in declarations that represent externally defined data structures, such as network packet layouts or database records. Unfortunately, this requires a lot of analysis that can only be done manually and requires deep understaning of the program being migrated, and therefore is error prone. This problem resembles the famous Year 2000 problem --- no one can tell where in a million of source lines your program relies on base types being exactly 16 bits wide.
You may consider usage of the following techniques that do not require this analysis:
Take into account that the XDS runtime library has been compiled with the M2BASE16 option turned OFF.
(*%T __XDS__ *) TYPE INTEGER = SYSTEM.INT16; CARDINAL = SYSTEM.CARD16; BITSET = SYSTEM.SET16; (*%E *)
There are no based and virtual pointers in XDS.
In TopSpeed Modula-2, types BYTE, WORD, and LONGWORD are assignment compatible with any type of corresponding size (1, 2, or 4 bytes), and allow testing for equality or inequality. Due to a far more complex definition of the BYTE and WORD types in ISO Modula-2, the only operation directly defined for them is an assignment and there are special rules affecting parameter compatibility (see XDS User Guide, System parameter compatibility for further details). The LONGWORD type does not exist in XDS.
In TopSpeed, a variable can be placed at a fixed physical address which is specified in segment:offset form:
VAR Screen [0B800H:0] : ARRAY [1..25][1..80] OF Cell;
In XDS, the SYSTEM.MAKEADR function has to be used:
VAR MyCardIO [SYSTEM.MAKEADR(0FFFF0000H)] : BITSET;
In ISO Modula-2, constructors of array type and record type value use curly braces ({}) rather than parenthesis (()).
TYPE
Struct = RECORD
date :CARDINAL;
time :CARDINAL;
END;
CONST
(* s = Struct ( 10, 12 ); TS manner *)
s = Struct { 10, 12 }; -- XDS (ISO) manner
If the option TOPSPEED or the option M2EXTENSIONS is set ON, XDS recognizes function procedure calls in designators, including predefined, SYSTEM, and type transfer procedures:
CurPos(point[i,j])^.x GetCurDate().year REC_PTR(adr)^.field
The '<<' and '>>' infix operators of logical left/right shift, defined for cardinal types in TS, are available in XDS if the option TOPSPEED or the option M2EXTENSIONS is set ON.
TopSpeed Modula-2 supports comparison of structured values (arrays and records) as a language extension. In general, alignment and packing rules of the target environment may cause existence of gaps between elements of an array or fields of a record. So comparison of structured values in raw mode (e.g. using Lib.Compare) is likely to produce incorrect results. In ISO Modula-2, you have to compare stuctured values element by element or field by field,
In TopSpeed Modula-2, absence of the ELSE clause in a CASE statement is equivalent to presense of an empty ELSE clause. In XDS Modula-2, according to ISO 10514, if the value of the case selector is not contained in any case label list and the ELSE clause is not present, an exception shall be raised. Therefore, the CASE statement in the following code fragment:
x := 2; CASE x OF |0: p0; |1: p1; END;
does nothing if compiled by TopSpeed, and raises an exception if compiled by XDS.
The recommended solution is to add empty ELSE clauses to all your CASE statements that lack it, as this would make no difference in TopSpeed. To quickly find all such statements, turn on warning 319:
C:\WORK>xc t.mod -woff319-' XDS Modula-2 v2.40 [x86, v1.08] - build 20.10.1998 Compiling "t.mod" * [t.mod 12.05 W319] * CASE statement without ELSE
ISO Modula-2 requires that a FOR loop control variable must be declared in the scope to which the loop belongs. TopSpeed does not make this restriction, so you may need to redesign some of your FOR loops.
There are no GOTO and LABEL clauses in ISO Modula-2. XDS provides them as an extension if the option TOPSPEED is turned ON.
XDS compilers allow only foreign procedures to be declared as external.
A declaration of an external procedure consists of a procedure header only, with the procedure name preceded by the symbol "/":
PROCEDURE ["C"] / putchar(ch: SYSTEM.CARD8): SYSTEM.INT32;
In TopSpeed Modula-2, an expression of type T can be substituted as a parameter whose formal type is ARRAY OF T. XDS supports this if on of the M2EXTENSIONS or TOPSPEED is turned ON.
In ISO Modula-2, a procedure belongs to a particular procedure type if the types of each formal parameter, taken in turn, are exactly the same. In TopSpeed Modula-2, there is one exception: if one of the two types is SYSTEM.ADDRESS, another type can be any pointer type, so the following piece of code is legal:
TYPE SetNameMethod = PROCEDURE(SYSTEM.ADDRESS,ARRAY OF CHAR); NodePtr = POINTER TO Node; Node = RECORD name : ARRAY [0..63] OF CHAR; setname: SetNameMethod; END; PROCEDURE SetNodeName(p: NodePtr; name: ARRAY OF CHAR); BEGIN Str.Copy(p^.name, name); END SetNodeName; . . . NEW(node); node^.setname := SetNodeName;
In TopSpeed Modula-2, a procedure type definition may reference an incomplete type containing that definition, provided that the incomplete type has already been referenced:
TYPE P = POINTER TO R; R = RECORD data: CARDINAL; proc: PROCEDURE(R); END;
XDS does not allow this, so you have to define the procedure type separately:
TYPE P = POINTER TO R; R_proc_type = PROCEDURE(R); R = RECORD data: CARDINAL; proc: R_proc_type; END;
The NULLPROC value is absent in XDS; NIL should be used instead. You may consider declaring NULLPROC under conditional compilation in modules that use it:
(*%T __XDS__ *) CONST NULLPROC = NIL; (*%E *)
Predefined procedures FarADR, NearADR, Ofs, and Seg are specific to the 80x86 16-bit real and protected modes and therefore are not available in XDS.
The FieldOfs and VSIZE procedures are predefined if the option TOPSPEED is set ON and are available from the module SYSTEM otherwise.
In TopSpeed Modula-2, the predefined function procedure VAL(type, expr) performs type conversion if both type and expr are numeric or ordinal, and performs type cast otherwise. In ISO Modula-2, VAL may only be used in the former case; SYSTEM.CAST has to be used for type casting.
According to PIM, a type transfer function type(expr) shall not affect the bit pattern representaion of the result of expr. Unfortunately, TopSpeed does not follow this specification and treats type(expr) exactly as VAL(type,expr) (see above). Due to introduction of such ambiguities by pre-ISO compiler vendors, this feature was not included in the International Standard. XDS provided it as a language extension (option M2EXTENSIONS ON), but treated it as SYSTEM.CAST(type,expr). Now, if the TOPSPEED option is turned ON, XDS will treat this construction exaclty as TopSpeed. This ambiguity may lead to porting problems that are hard to detect, so the compiler issues a warning upon encountering a type transfer function. It is recommended to use the built-in VAL and SYSTEM.CAST function procedures for type conversion and type cast respectively.
In XDS, there is no way to point out an inline procedure explicitly (yet), however native-code compilers can make inline substitutions of some procedures automatically. It is also not possible to define inline procedures in a definition module. You have to move such procedures into implementation modules.
As for TopSpeed binary inline procedures, they also have no equivalent in XDS. Since they contain 16-bit mode instructions, they would have to be rewritten anyway. XDS inline assembler may be used to implement machine code routines.
In TopSpeed, any entity imported into a definition module is "automatically" imported into the corresponding implementation module. In ISO Modula-2, all entities required by an implementation module should be explicitly imported into it.
TopSpeed Modula-2 supports qualification of entities defined in a definition module in the respecive implementation module. In XDS, this is not possible.
In TopSpeed, if T is a type defined in an implementation module, it is still possible to declare a type POINTER TO T in a definition module:
DEFINITION MODULE T; TYPE p1 = POINTER TO t1; r1 = RECORD p2: POINTER TO t2; END; END T.
XDS does not allow this, so you have to either
In TS. the keyword QUALIFIED (in local modules) has no effect and a name of an object exported from a local module can be used with or without qualification. In ISO Modula-2, there is a distinction between qualified an unqualified export. You will have to introduce uniformity here: if you mostly use objects exported from local modules with qualification, ensure presence of the QUALIFIED keyword, otherwise remove it. Compilation errors will then indicate places where you have to add or remove qualification.
In ISO Modula-2, all objects from the module SYSTEM, such as types ADDRESS, WORD, BYTE, and the procedure ADR have to be explicitly imported. In TopSpeed Modula-2, they are predefined.
Although the XDS module SYSTEM contains procedures NEWPROCESS, TRANSFER, and IOTRANSFER, it is recommended to replace them with more powerful mechanisms provided by the ISO system module COROUTINES. See also Multithreading.
The set of platform-specific facilities is almost completely different in TopSpeed and XDS variants of the module SYSTEM.
TopSpeed Modula-2 supports Intel x86 16-bit real and protected modes, whereas XDS produces code for the 32-bit flat memory model. Therefore, code that deals with segment:offset addresses has to be rewritten for XDS.
In XDS, there is no analog for TopSpeed pointer constructors of the form [seg:ofs type]. These constructors are commonly used to directly interface to hardware and to access DOS and BIOS data areas, so routines that utilize this feature have to be redesigned anyway,
The following TopSpeed Modula-2 types and procedures, specific to Intel x86 16-bit real and protected modes, are absent in XDS:
FarADDRESS FarNIL FarADR
NearADDRESS NearNIL NearADR
Ofs Seg
There are so called based pointers in TopSpeed, i.e. pointers belonging to a particular segment. This feature is useless in 32-bit flat mode, so it is not available in XDS.
There are no object-oriented extensions in XDS Modula-2. Although XDS features Oberon-2 as well, its OO facilities are dissimilar in many aspects from provided in TopSpeed Modula-2. So, unfortunately, the required porting efforts for a program that makes heavy use of TS OO extensions may be unaffordable.
XDS pragma syntax is completely different from TS. Most TopSpeed pragmae have no analogs in XDS, and many of them are pretty useless in the 32-bit flat memory model. Conditional compilation has to be used to isolate TopSpeed and XDS pragmae:
(*%T __XDS__ *) <* PUSH *> <* COVERFLOW - *> (*%E *) (*%F __XDS__ *) (*# save, check(overflow=>off) *) (*%E *) hash := hash*alpha+ORD(ch); (*%T __XDS__ *) <* POP *> (*%E *) (*%F __XDS__ *) (*# restore *) (*%E *)
When an array is passed as a value parameter in TS, it can be either copied onto the stack or passed by reference, depending on the call(o_a_copy) pragma setting. In XDS, an array is always copied onto the stack unless the formal parameter is marked as read-only, for instance:
PROCEDURE PrintStr (s : ARRAY OF CHAR); -- pass by value
PROCEDURE PrintStr (s- : ARRAY OF CHAR); -- pass by reference
Note: the M2EXTENSIONS option should be turned on.
When a procedure parameter has type ARRAY OF CHAR, TopSpeed allows an actual parameter of type CHAR to be passed. This is not allowed by the ISO standard, and XDS will not accept it.
In TopSpeed Modula-2, the pragma option(bit_expr=>on} enables bitwise operations AND, OR, and NOT to be applied to cardinals. In XDS, this feature is enabled if the M2EXTENSIONS option is set ON.
More information on TS-to-XDS pragmae mapping will be provided in future releases.
The following modules have been implemented in the XDS TS-like library:
BiosIO FIO Graph IO Lib
MATHLIB MsMouse ShtHeap Str Window
In order to preserve the overall level of the XDS libraries portability, some restrictions and modifications were made to the original interface and functionality of the modules. There are also some useful extensions. See the following sections for details.
Note: The library is compiled with the M2BASE16 compiler option turned OFF, so types INTEGER, CARDINAL, and BITSET are 32-bit.
MODIFICATIONS
TYPE SHAREMODE = (ShareCompat, ShareDenyRW, ShareDenyRD, ShareDenyWR, ShareDenyNone);
Supported target operating systems do not distingiush between subsequent opens of a file done by the same or different process, so ShareCompat works as ShareDenyWR in OpenRead and as ShareDenyRW in other file open procedures.
PROCEDURE AppendHandle(fh : LONGCARD ;ReadOnly : BOOLEAN ) : File;
RESTRICTIONS
PLATFORMS
OS/2, Windows 95, Windows NT
MODIFICATIONS
The Init procedure has been changed to
PROCEDURE Init(xLeft, yTop, xd, yd : LONGCARD) : BOOLEAN;
The position of the left upper corner of the Graph window is passed in the xLeft and yTop parameters, horizontal and vertical sizes of the window are passed in the xd and yd parameters. The Graph window is closed automatically upon program termination. The Exit procedure can be used to close the Graph window before the program execution is finished:
PROCEDURE Exit;
The Init procedure returns success indicator (TRUE means succesful module initialization) and it is recommended to test the value in the program.
PROCEDURE SetGraphWindowTitle (text :ARRAY OF CHAR);
| Name | Explanation | VideoConfig field |
| Width | width of the Graph window work area | numxpixels |
| Depth | depth of the Graph window work area | numypixels |
| NumPColors | number of colors in use | numcolors |
| Columns | number of text columns available | numtextcols |
| Rows | number of text rows available | numtextrows |
Note: By "number of text columns" ("text rows") a maximum number of monospaced font characters which can be placed into the Graph window in one column (row) is meant.
PROCEDURE Polygon(n : CARDINAL; px, py : ARRAY OF CARDINAL; FillColor : CARDINAL; Fill : BOOLEAN);
The variable FillState, influencing on Polygon's fill mode, has been excluded.
PROCEDURE Pie_a (x1, y1, x2, y2 : LONGCARD; startAngle, sweepAngle : LONGREAL; Color :CARDINAL; Fill : BOOLEAN);
A pie is defined as a part of an ellipse enclosed in the rectangle ( x1, y1, x2, y2 ), with a start angle passed in the startAngle parameter and a sweep angle passed in the sweepAngle. Angles are measured in degrees. The pie is drawn counterclockwise.
PROCEDURE RawOutText (x, y : LONGCARD; Color : LONGCARD; Text :ARRAY OF CHAR);
The following scheme shows the standard order of procedure calls within the program which uses bitmaps:
PROCEDURE GetImage(x1, y1, x2, y2 : LONGCARD; VAR bmpHandle : HBITMAP );
The handle of the created bitmap will be returned in the bmpHandle parameter (the HBITMAP type is exported from the module).
PROCEDURE PutImage(x, y : LONGCARD; bmpHandle : HBITMAP; Action : LONGCARD);
PROCEDURE DelImage(hbmHandle :HBITMAP);
LNSTYLE_DOT LNSTYLE_SHORTDASH LNSTYLE_DASHDOT LNSTYLE_DOUBLEDOT LNSTYLE_DASHDOUBLEDOT LNSTYLE_SOLID
PATSYM_DENSE1 PATSYM_DENSE2 PATSYM_DENSE3 ...
PROCEDURE SetBkMix (isOpaque : BOOLEAN);
The _TRANSPARENT and _OPAQUE constants can be passed to the isOpague parameter. The procedure sets background color mixture mode. Namely, if _OPAQUE passed, bits of a fill mask, set in zero, are painted in background color when filling with a non-solid mask is used. If _TRANSPARENT passed, screen pixels corresponding to zero bits of a fill mask, are not changed.
RESTRICTIONS
PLATFORMS
OS/2, Windows 95, Windows NT
RESTRICTIONS
PLATFORMS
OS/2, Windows 95, Windows NT
MODIFICATIONS
PROCEDURE Environment (N :CARDINAL;
VAR result :ARRAY OF CHAR);
PROCEDURE WordFill(Dest: ADDRESS;
WordCount: CARDINAL;
Value: SYSTEM.CARD16);
RESTRICTIONS
AddressOK CpuId GetInProgramFlag Execute NoSound ProtectedMode SelectorLimit SetReturnCode SetInProgramFlag Sound SysErrNo UserBreak WrDosError
PLATFORMS
OS/2, Windows 95, Windows NT
RESTRICTIONS
PLATFORMS
Any platform where XDS is available
MODIFICATIONS
PLATFORMS
Any platform where XDS is available
MODIFICATIONS
As a result, after a call FixRealToStr(V,5,S,Ok) variables S and Ok have the following vaues in TS and XDS respectively:
V
SIZE(S)
S
OK
0.99E17
30
99000000000000000.0
TRUE
99000000000000000.00000
TRUE
0.99E18
30
990000000000000000
TRUE
990000000000000000.00000
TRUE
0.99E18
18
990000000000000000
TRUE
?
FALSE
PLATFORMS
Any platform where XDS is available
EXTENSIONS
RESTRICTIONS
TYPE p2WDescriptor = POINTER TO WinDescriptor; PROCEDURE GetWDescriptor ( W :WinType ) :p2WDescriptor;
PLATFORMS
OS/2, Windows 95, Windows NT
The TopSpeed module Process implements its own scheduler which does not make use of the underlying operating system multithreading capabilities, in order to enable the development of multithread programs portable between DOS and OS/2. In case of XDS, the ISO Processes module should be used. Under OS/2, Windows NT, Windows 95, its functionality is mapped onto the corresponding thread control API.
Note: The MULTITHREAD option should be set on to enable the functionality described above. Otherwise, the module Processes will work in coroutines mode.
An example of the Processes module usage is the WinDemoM sample which was ported from TopSpeed Modula-2.
The Processes module does not provide direct way to start or stop the process scheduler, which is possible in TopSpeed Process module, but this problem can be easily solved by writing some extra code (see WinDemoM.mod).
The ISO standard module Semaphores can be used to utilize the underlying operating system (OS/2, Windows NT, or Windows 95) semaphore API.