Modula-2: the structured form of Pascal
Modula-2 was developed by Niklaus Wirth the inventor of Pascal. Modula-2 was designed for constructing
operating systems. The first OS for which it was used, was Lilith. Probably this was the last one too, since
most of the "middle of the road programmers" didn't or couldn't switch to the new language. "C is for Chaos".
And not only as an acronym: C is a write-only language.
Modula-2 is superstructured. It is case sensitive. There are only few internal commands. The rest is imported
from libraries, which are simple to make yourself with the language itself.
Modula-2 is based on code-hiding: as a programmer you produce a ".DEF" file in which you declare how each
procedure works, plus a precompiled ".M2O" file which can be linked in by the user to complete the total code.
You can keep your sources to yourself.
The biggest drawback of Modula-2 is it's lack of a GOTO statement! There you go, as a seasoned C++ progammer.
No GOTO! How on earth are you now expected to leave tricky bits of code?
Modula-2 is equipped with a series of statements which give enough power such that you will never, ever, need a GoTo statement. Below, I have put a small piece of code. It is part of the soap hardware (for more information: see the mainpage), an 11 channel 12 bit AD converter project. Originally written in assembly language, and now ported to Modula-2.
A sample program: SOAP, a mouse driven program.
This source produces an executable which is completely mouse-driven. The keyboard is superfluous as long as
this program is running! Start the program SOAP02 (it is included with the full source in the file SOAP02.ZIP)
and try to return to the operating system. It can be done but you must use your imagination.
Below is part of the source. I removed all the irrelevant pieces of code. The full source is part of the ZIP
file. Just download it and get access to everything needed to compile it.
SOAP: the source of beauty.
MODULE Soap02;
FROM Display IMPORT SetCursorPosition;
FROM InOut IMPORT Write, WriteCard, WriteLn, WriteLine, WriteHex;
FROM Mouse IMPORT ConfineMouse, GetMouseStatus, HideMouse, InitMouse,
MouseRecord, SetMouseCursor, ShowMouse;
FROM SYSTEM IMPORT ADDRESS, ASSEMBLER;
FROM System IMPORT GetArg, Terminate;
FROM Timer IMPORT LongWait;
FROM VgaLib3 IMPORT WinData, COLOUR, SetVGA, ChrWid, Center, SetColour, MakeBox,
EraseBox, FillBox, PlotChar, WriteNumber, DrawH, PlotText;
TYPE LogicState = (Low, High);
CONST AdcResolution = 12; (* nr of bits of TLC 2543 ADC *)
AdcChannels = 11; (* nr of channels in this ADC *)
Tab = 11C;
VAR MainScreen, PlotWin, BotLin, TopWin, MousWin : WinData;
Inimini : MouseRecord;
Number, StopCondition, OldMode,
LptPort, NextChannel,
Value, i, j : CARDINAL;
BipolarFormat : BOOLEAN;
PROCEDURE InitWins;
BEGIN
(* MainScreen is a dummy for the all around framework. *)
MainScreen.TopX := 0; MainScreen.TopY := 0;
MainScreen.Width := 640; MainScreen.Height := 480;
MainScreen.CurX := 0; MainScreen.CurY := 0;
MainScreen.DeltaX := 0; MainScreen.DeltaY := 0;
MainScreen.Indent := 5;
MainScreen.TexCol := GREEN; MainScreen.BoxCol := YELLOW;
MainScreen.BckCol := black; MainScreen.MnuCol := red;
(* TopWin describes the upper status bar for action keys. *)
TopWin.TopX := 10; TopWin.TopY := 10;
TopWin.Width := 550; TopWin.Height := 60;
TopWin.CurX := 0; TopWin.CurY := 0;
TopWin.DeltaX := 0; TopWin.DeltaY := 0;
TopWin.Indent := 4;
TopWin.TexCol := RED; TopWin.BoxCol := green;
TopWin.BckCol := pink; TopWin.MnuCol := RED;
(* MousWin describes the window for the mouse coordinates *)
MousWin.TopX := 580; MousWin.TopY := 10;
MousWin.Width := 50; MousWin.Height := 60;
MousWin.CurX := 0; MousWin.CurY := 0;
MousWin.DeltaX := 0; MousWin.DeltaY := 0;
MousWin.Indent := 4;
MousWin.TexCol := white; MousWin.BoxCol := white;
MousWin.BckCol := blue; MousWin.MnuCol := YELLOW;
(* PlotWin describes the uppermost window for the parameters. *)
PlotWin.TopX := 10; PlotWin.TopY := 155;
PlotWin.Width := 620; PlotWin.Height := 300;
PlotWin.CurX := 0; PlotWin.CurY := 0;
PlotWin.DeltaX := 0; PlotWin.DeltaY := 0;
PlotWin.Indent := 4;
PlotWin.TexCol := YELLOW; PlotWin.BoxCol := cyan;
PlotWin.BckCol := black; PlotWin.MnuCol := red;
(* BotLin describes the Copyright notice. *)
BotLin.TopX := 5; BotLin.TopY := 460;
BotLin.Width := 620; BotLin.Height := 20;
BotLin.CurX := 0; BotLin.CurY := 0;
BotLin.DeltaX := 0; BotLin.DeltaY := 0;
BotLin.Indent := 4;
BotLin.TexCol := brown; BotLin.BoxCol := green;
BotLin.BckCol := black; BotLin.MnuCol := green;
END InitWins;
PROCEDURE SetUpScreen;
BEGIN
OldMode := SetVGA (12H);
InitMouse (Inimini);
InitWins;
MakeBox (MainScreen);
MakeBox (PlotWin);
MakeBox (MousWin);
TopWin.CurX := 10;
TopWin.CurY := 0;
Center (TopWin, "SOAP_version_1.0 Load Save Run Cls Quit");
BotLin.CurX := 10;
BotLin.CurY := 0;
Center (BotLin, "Copyleft_2001: Jan_Verhoeven jverhoeven@bigfoot.com")
END SetUpScreen;
PROCEDURE SetUpMouse;
BEGIN
ShowMouse;
IF Inimini.Buttons = 2 THEN
StopCondition := 3
ELSE
StopCondition := 7
END;
SetMouseCursor (30, 30)
END SetUpMouse;
PROCEDURE ShowMouseXY; (* Show current mouse data on screen. *)
BEGIN
SetColour (MousWin.TexCol, MousWin.BckCol);
MousWin.CurX := MousWin.Indent;
MousWin.CurY := 4;
WriteNumber (MousWin, Inimini.MouseX, 4);
MousWin.CurX := MousWin.Indent;
MousWin.CurY := 23;
WriteNumber (MousWin, Inimini.MouseY, 4);
MousWin.CurX := MousWin.Indent;
MousWin.CurY := 42;
WriteNumber (MousWin, Inimini.Status, 4)
END ShowMouseXY;
PROCEDURE UserMessage (n : CARDINAL);
BEGIN
WriteLn;
CASE n OF
0 : WriteString ("Thank you for using this software. ");
WriteLine ("Remember that this is GNU GPL free software");
WriteString ("which comes with NO GUARANTEE but full sources. ");
WriteLine ("You use this program at your");
WriteLine ("own risk. Please study the GNU GPL conditions.");
WriteLn;
WriteLine ("Copyleft 2000, jverhoeven@bigfoot.com");
|
1 : WriteLine ("The syntax for using this software is as follows:");
WriteLn; Write (Tab);
WriteLine ("SOAP LptPort SampleRate <ADC channel set>");
WriteLn;
WriteString ("in which : LptPort is the number of the LPT port ");
WriteLine ("(1, 2 or 3)");
Write (Tab); Write (Tab);
WriteLine ("SampleRate is the number of samples per second");
Write (Tab); Write (Tab);
WriteString ("<ADC channel set> is an optional list of channels ");
WriteLine ("that are measured");
WriteLn;
WriteLine ("If no ADC channel set is specified, all channels are sampled.")
END
END UserMessage;
In-line assembly, the easy way.
PROCEDURE ChipSelect (state : LogicState);
(* Control the TLC 2543 ChipSelect line *)
BEGIN
IF state = Low THEN
ASM
MOV DX, LptPort
IN AL, DX
OR AL, 1
OUT DX, AL
END
ELSE
ASM
MOV DX, LptPort
IN AL, DX
AND AL, 0FEH
OUT DX, AL
END
END
END ChipSelect;
See how easy it was to include a piece of inline assembly? If the ChipSelect line must be "1" or HIGH, another
piece of code is executed as when the line must be LOW or "0".
Back to the source....
PROCEDURE ShiftClock (state : LogicState);
(* Control the TLC 2543 Shift Clock input *)
BEGIN
IF state = Low THEN
ASM
MOV DX, LptPort
IN AL, DX
OR AL, 2
OUT DX, AL
END
ELSE
ASM
MOV DX, LptPort
IN AL, DX
AND AL, 0FDH
OUT DX, AL
END
END
END ShiftClock;
PROCEDURE SerialDataInput (state : LogicState);
(* Control the TLC 2543 Serial Data Input *)
BEGIN
IF state = Low THEN
ASM
MOV DX, LptPort
IN AL, DX
OR AL, 4 (* Set bit 2 of the LPT port *)
OUT DX, AL
END
ELSE
ASM
MOV DX, LptPort
IN AL, DX
AND AL, 0FBH (* Clear bit 2 of the LPT port *)
OUT DX, AL
END
END
END SerialDataInput;
PROCEDURE ReadEOC () : LogicState;
(* Determine state of the End Of Conversion output *)
VAR Result : CARDINAL;
BEGIN
ASM
MOV DX, LptPort
INC DX
IN AL, DX
AND AL, 040H
MOV Result, AL
END;
IF Result = 0 THEN
RETURN Low
ELSE
RETURN High
END
END ReadEOC;
The standard way of returning parameters from Inline assembly
PROCEDURE ReadDataOutput (VAR word : CARDINAL);
(* Read TLC 2543 Serial Data Output and store in word *)
VAR value : CARDINAL;
BEGIN
value := word;
ASM
MOV DX, LptPort
INC DX (* Point to inputchannel *)
IN AL, DX
SHL AL, 1 (* bit 7 -> CF *)
CMC (* invert it *)
MOV AX, value (* get current value *)
RCL AX, 1 (* shift in next bit *)
MOV value, AX (* and store in memory *)
END;
word := value
END ReadDataOutput;
PROCEDURE ReadWord (NextChannel : CARDINAL) : CARDINAL;
(* Read in result of previous conversion, while preparing the TLC 2543 ADC
for the next conversion. NextChannel contains the next ADC channel number.
*)
VAR LastValue, i, bit : CARDINAL;
BEGIN
NextChannel := NextChannel * 4096;
IF BipolarFormat THEN INC (NextChannel, 256) END;
LastValue := 0;
ChipSelect (Low);
FOR i := 1 TO AdcResolution DO
bit := 16 - i;
ReadDataOutput (LastValue);
IF bit IN BITSET (NextChannel) THEN
SerialDataInput (High)
ELSE
SerialDataInput (Low)
END;
ShiftClock (High);
ShiftClock (Low)
END;
ChipSelect (High);
RETURN LastValue
END ReadWord;
Modula 2 is almost self documenting.
As you can see here: all previously defined procedures and modules come together and fit the puzzle. The language is virtually "self documenting". You can pick (just about) any name and the case sensitivity of the language gives the programmer enormous flexibility in assigning names to functions, variables and procedures. All names can be made meaningful from the very first instant.
Comments remain useful, but often the name is already enough explanation for things to come.
Back to the source....
PROCEDURE OnScreen;
VAR i, j, value : CARDINAL;
BEGIN
value := ReadWord (0);
FOR i := 0 TO 10 DO
value := ReadWord (i+1);
SetCursorPosition (i+4, 10);
WriteCard (value, 8);
WriteHex (value, 12)
END
END OnScreen;
PROCEDURE Initialize; (* Set up defaults, read command line *)
VAR Option : ARRAY [0..7] OF CHAR;
count, i : CARDINAL;
ch : CHAR;
BEGIN
GetArg (Option, count);
IF count = 0 THEN (* No commandline arguments *)
UserMessage (1);
Terminate (1)
END;
IF CAP (Option [0]) = 'L' THEN
ch := Option [3]
ELSE
ch := Option [0]
END;
i := ORD (ch) - ORD ('1');
ASM
MOV AX, 0
MOV ES, AX
MOV BX, 0408H
MOV AX, i
SHL AX, 1
ADD BX, AX
MOV AX, ES:[BX]
MOV i, AX
END;
LptPort := i;
ChipSelect (High);
ShiftClock (Low);
SetUpScreen;
SetUpMouse;
BipolarFormat := FALSE
END Initialize;
BEGIN
Initialize;
REPEAT
GetMouseStatus (Inimini);
ShowMouseXY;
UNTIL Inimini.Status = StopCondition;
ChipSelect (High);
i := SetVGA (OldMode);
UserMessage (0)
END Soap02.
Page created long time ago,
Page equipped with FroogleBuster technology