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 firstname.lastname@example.org") 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, email@example.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 ) = 'L' THEN ch := Option  ELSE ch := Option  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 ??? and