M I D l e t P a s c a l
First MIDletPascal program

After reading the first chapter you should know how to work with MIDletPascal environment. This chapter will show you how to write simple applications that draw on the device display, read keyboard input and use units to separate the program source code into multiple files.


Hello world

If you have already programmed in Pascal, you would write Hello world program like this:

program HelloWorld; 

begin
  Writeln('Hello world'); // this will not work in MIDletPascal !!!
end.



However, the procedure Writeln writes the text to standard output – that is the MS-DOS console window. Mobile phones don't have MS-DOS nor they have console window. Writing text on the mobile device display is more like 'drawing' that 'outputting'. To draw the text on device display, use procedure DrawText which takes 3 arguments: the string to write, and screen coordinates of the upper-left corner of the text bounding rectangle. Since all drawing operations are done on the memory buffer, you must call Repaint when you want to update the device display:

program HelloWorld;

begin
  DrawText('Hello world', 0, 0);
  Repaint;
end.



If you compile this program and try to run it, you will get the impression that the program does not get executed. In fact, the program starts, draws the text and terminates almost instantly. To see the result of the drawing operation, call procedure Delay which suspends the execution of the application for the given number of milliseconds:

program HelloWorld;

begin
  DrawText('Hello world', 0, 0);
  Repaint;
  Delay(2000);
end.



Procedure Repaint slow compared to drawing procedures such as DrawText. That is why Repaint should be called only when needed. The following example illustrates bad use of Repaint:

begin
  DrawText('Hello world', 0, 0);
  Repaint;
  DrawText('Other text', 0, 20);
  Repaint;
end.



The same program can be rewritten to improve performance:

begin
  DrawText('Hello world', 0, 0);
  DrawText('Other text', 0, 20);
  Repaint;
end.





Reading from the keypad


MIDletPascal provides two functions for reading from the device keypad: GetKeyPressed and GetKeyClicked. GetKeyPressed returns the key code of the key that is currently pressed. GetKeyClicked returns the key code of the last clicked key.

MIDletPascal has following key code constants predefined: KE_NONE, KE_KEY0, KE_KEY1, KE_KEY2, KE_KEY3, KE_KEY4, KE_KEY5, KE_KEY6, KE_KEY7, KE_KEY8, KE_KEY9, KE_STAR and KE_POUND. These keys have the same standard key codes on all mobile phones. Unfortunately, arrow keys and other special keys have key codes that differ from one mobile phone model to the other. To get around this problem, MIDletPascal offers a function which translates the key code into an action code. The function is KeyToAction; it takes a key code as the only parameter and returns one of the following predefined constant values: GA_NONE, GA_UP, GA_DOWN, GA_LEFT, GA_RIGHT, GA_FIRE, GA_GAMEA, GA_GAMEB, GA_GAMEC, GA_GAMED.

The following example allows the user to move the text on the screen using the arrow keys. The program terminates when the user presses the fire or zero key.

program MoveText;

var x, y: integer;
    keyCode: integer;

begin
  repeat
    keyCode := GetKeyPressed; // retrieve the currently pressed key

    if  KeyToAction(keyCode) = GA_UP    then y := y - 1;    
    if  KeyToAction(keyCode) = GA_DOWN  then y := y + 1;    
    if  KeyToAction(keyCode) = GA_LEFT  then x := x - 1;    
    if  KeyToAction(keyCode) = GA_RIGHT then x := x + 1;    

    SetColor(255, 255, 255); // set the drawing color to white
    FillRect(0, 0, GetWidth, GetHeight);  // erase the screen by drawing a
                                          // rectangle as big as display
 
    SetColor(0, 0, 0); // set the drawing color to black
    DrawText('Hello world', x, y); // draw the text to the given position
    
    Repaint;
    Delay(100);

  until (keyCode = KE_KEY0) or (KeyToAction(keyCode) = GA_FIRE);
  
end.



Sometimes you might want the user to input some text. Using GetKeyPressed and GetKeyClicked for this purpose is almost impossible. User input is handled by forms, which are described in the next chapter.


Drawing images


In the next example we will implement a simple screen-saver. The screen saver will bounce the MIDlet icon:


program ScreenSaver;

const up    = 1; // these constants define the direction
      down  = 0; // in which the icon moves
      left  = 1;
      right = 0;

var x, y      : integer; // the current icon's left upper corner position
    img       : image;   // handle of the image object
    direction_x,
    direction_y  : integer; // the movement direction
     
begin
  img := loadImage('/icon.png');
  
  repeat
    // move the icon according to the current direction
    if  direction_y = up    then y := y - 1;
    if  direction_y = down  then y := y + 1;
    if  direction_x = left  then x := x - 1;
    if  direction_x = right then x := x + 1;

    // check if the icon hit the display border
    if  x = 0 then direction_x := right;
    if  x = (GetWidth – GetImageWidth(img)) then direction_x := left;
    if  y = 0 then direction_y := down;
    if  y = (GetHeight – GetImageHeight(img)) then direction_y := up;
  
    SetColor(255, 255, 255);
    FillRect(0, 0, GetWidth, GetHeight);
    DrawImage(img, x, y);
    Repaint;
    Delay(50);
  until GetKeyPressed <> KE_NONE;
end.





Using units

When developing large programs, having only one source file is a major drawback. You cannot get around in hundreds of functions and procedures, and all the functions, procedures and global variables that you use are not grouped by their functionality. To solve this problem, you can use units just like in any standard Pascal.

There are few minor differences between MIDletPascal units and regular Pascal units. In regular Pascal, you can have as many units in a source file as you want. In MIDletPascal, you can have only one unit per file, and the file name must correspond to the unit name. MIDletPascal does not support finalization block that regular Pascal units can have. And finally, in MIDletPascal, units are tied to the project – if you want to use a unit from one project in another, you must create the unit in the other project, and copy the source code.

To illustrate how the units can be used in MIDletPascal, we will write a simple application that uses units.

First, create a new MIDletPascal project; let's name it TestUnits. Then select Project -> New source file…menu, and enter Complex as unit's name.

Enter the following code in the file Complex.mpsrc:

unit Complex;

interface

  type complex = record
    real_part, imag_part: real;
  end;

  function Add(a, b: complex): complex;
  function Sub(a, b: complex): complex;
  function MakeString(a: complex): string;

implementation

  function Add(a, b: complex): complex;
  begin
    add.real_part := a.real_part + b.real_part;
    add.imag_part := a.imag_part + b.imag_part;
  end;

  function Sub(a, b: complex): complex;
  begin
    sub.real_part := a.real_part - b.real_part;
    sub.imag_part := a.imag_part - b.imag_part;
  end;

  function MakeString(a: complex): string;
  begin
    MakeString := '' + a.real_part + ' + ' + a.imag_part + 'j';
  end;

end.



Enter the following code in file TestUnits.mpsrc:

program TestUnits;

uses Complex;

var x, y: Complex.complex;

begin
  x.real_part := 2; x.imag_part := 3; // x := 2 + 3j
  y.real_part := 0; y.imag_part := 1; // y := j

  x := Complex.Add(x, y);  

  DrawText(MakeString(x), 0, 0);
  // if more units define identifier MakeString, then write:
  // DrawText(Complex.MakeString(x), 0, 0);

  Repaint;
  Delay(2000);
end.




Units may use one another – if you want to use one unit from within the other, write
uses UnitName;  
right after the implementation keyword. However, units may not be used circularly, that is, unit A cannot use unit B if unit B already uses unit A.
The units may contain initialization code. This is the code that is guaranteed to execute before any function, procedure or variable from the unit is accessed. The initialization code should be written after the initialization keyword, like in the following example:


unit Unit_X;

interface
  var x:integer;

implementation
 // nothing to be implemented here

initialization
  x := 17; 

end.