A small Pascal for the phones of the 2000s, the ones with a keypad, a screen
176 pixels wide and Java ME. A program compiles to a MIDlet: a JAR that runs on such a phone,
in J2ME Loader, or in the emulator built into ANPASIDE.
This page describes the language as the mp3cc compiler accepts
it, routine by routine. Where the original manual says otherwise, the compiler is what is
described here.
program hello;
begin
SetColor(255, 0, 0);
DrawText('Hello world', 10, 10);
Repaint;
Delay(2000);
end.
A whole program: it writes a red line of text at 10, 10, shows it, and exits two
seconds later.
1From source to a MIDlet
One run of mp3cc compiles one .pas file and writes class files,
nothing else:
a program
M.class, a subclass of the MIDP Canvas
a unit
a class named after the unit in lowercase, and a .bsf file with its symbols
a record type
R_0.class, R_1.class and so on
Because the program itself is the canvas, drawing and key handling are part of the language
rather than a library. Everything else the compiler only names in its output: the units the
program uses, the library classes it calls, and the runtime classes its built-in routines need.
Compiling those units, collecting the classes and packing them into a JAR with a manifest is
the job of the IDE. In ANPASIDE that is the Run button.
The runtime is a handful of small classes. FW.class goes into every JAR; the
others only when a routine needs them, and the reference says which:
A string is not an array of characters. Its first character is at index 0, and
characters are read and changed with GetChar and
SetChar; s[i] does not compile.
A function's result has to be used, as in standard Pascal; Turbo Pascal, Free Pascal and
Delphi let a call stand on its own. FormAddString('Hi'); alone is error E432, so
assign it: id := FormAddString('Hi');.
Parameters are always passed by value, and routines cannot be nested.
There are no sets, enumerated types, files, pointers or with.
2Program structure
A program is a header, declarations, and a main block:
program name;
{ constants, types, variables }
{ procedures and functions }
begin
{ statements }
end.
The program line may be left out. Declarations come in any order and any
number of blocks. The program runs the main block and ends at its end., with
the period.
As in every Pascal, keywords and names are case-insensitive: Begin,
begin and BEGIN are one word.
Comments
{ braces }
(* parentheses and stars *)
// to the end of the line
/* C style */
Compiler directives such as {$R+} are read as ordinary comments: the kind of
real numbers and of canvas is set by the build, not by the source.
3Data types
Type
Holds
integer
a 32-bit signed number, −2147483648 to 2147483647
real
a number with a fraction, computed in software; see Real numbers
boolean
true or false
char
one character; Ord and Chr convert it to and from its code
string
text of any length, indexed from 0
image
a picture, from LoadImage or one of the ImageFrom functions
The last five are handles: a program passes them around but cannot look inside. From these,
a program builds records and arrays.
Records
type hero = record
x, y: integer;
health: integer;
end;
var player, saved: hero;
begin
player.x := player.x + 1;
saved := player; { copies every field }
end.
Assigning a record copies it, so changing saved later leaves
player alone. A record cannot hold an array (error E446).
Each record type becomes a class numbered in the order the compiler meets it:
R_0, R_1, and so on. The numbers start again with every run unless
the build passes the next free one with -r, and ANPASIDE does, so that the record
classes of two modules do not overwrite each other in the JAR. One case is still not covered:
two units that use each other can end up with the same numbers, so keep record types out of
such a pair. The compiler tells record types apart by their number alone, which is why a clash
is not caught at compile time and shows up only as a crash on the phone.
Arrays
type board = array[1..8, 1..8] of integer;
var cells: board;
scores: array[0..9] of integer;
begin
cells[1, 1] := 0;
scores[0] := 100;
end.
Each dimension has its own bounds, and there can be any number of dimensions. Arrays are not
assigned whole, only element by element (error E443).
4Declarations
const
minutesInHour = 60;
greeting = 'To be or not to be';
type
number = integer;
point = record x, y: integer; end;
var
index: integer;
field: array[1..15] of integer;
p: point;
begin
end.
const names a value, type names a type, and var
declares variables, of a named type or one written out in place. Variables declared outside any
routine are global: every procedure and function declared after them sees them.
5Statements
Where a statement has a body, one statement needs nothing around it and several go between
begin and end.
Assignment
x := x + 1;
if
if lives = 0 then
gameOver := true
else
begin
lives := lives - 1;
restart;
end;
for
for i := 1 to 10 do
sum := sum + i;
for i := 10 downto 1 do
DrawText(IntegerToString(i), 0, i * 10);
while and repeat
while GetKeyClicked = KE_NONE do
Delay(100);
repeat
Delay(100);
until GetKeyClicked <> KE_NONE;
repeat
tick;
forever;
forever in place of until makes a loop that only
break, exit or Halt ends.
break leaves the innermost for, while or
repeat, and is an error outside a loop.
case
case GetKeyClicked of
KE_KEY2: y := y - 1;
KE_KEY8: y := y + 1;
KE_KEY4, KE_KEY6: x := x + key - KE_KEY5; { several labels }
KE_KEY5: begin
DrawText('fire', 10, 10);
Repaint;
end;
KE_STAR: ; { does nothing }
0..31: Delay(50); { a range, both ends included }
else
x := 0;
y := 0;
end;
case name of
'left', 'l': dx := -1;
'right': dx := 1;
end;
The value tested is an integer, a char or a
string. Labels are literals or constants, never variables; integer and char
labels may also be ranges. The first matching arm runs and nothing falls through. The
else part takes any number of statements and is optional; without it, a value
that matches nothing does nothing.
String labels are compared exactly, upper and lower case included.
A label that repeats or overlaps another is error E465.
In an arm such as 1: if ok then go; else stop, the else
after the semicolon belongs to the case, not to the if.
break in an arm leaves the loop around the case.
case needs mp3cc 3.5.4, which ANPASIDE ships since 2.5.0. Older
compilers stop with E442.
6Procedures and functions
var total: integer;
procedure add(a: integer; b: string);
var len: integer; { local, made anew on every call }
begin
len := Length(b);
total := a + len;
end;
function multiply(a, b: integer): integer;
begin
multiply := a * b;
end;
function square(x: integer): integer;
begin
result := x * x;
end;
begin
add(multiply(2, 3), 'abc');
total := square(total);
end.
A procedure returns nothing. A function returns the value last assigned to its own name, or
to result. exit leaves a routine at
once, and in the main block ends the program like Halt.
Parameters are passed by value. var in a parameter list is ignored with
warning W436, so a routine cannot change its caller's variables, only globals. Routines cannot
be declared inside other routines (error E431), but they can call themselves:
function factorial(n: integer): integer;
begin
if n <= 1 then
factorial := 1
else
factorial := n * factorial(n - 1);
end;
Two routines that call each other need one of them declared forward first:
procedure b(y: integer); forward;
procedure a(x: integer);
begin
if x > 0 then b(x - 1);
end;
procedure b(y: integer);
begin
if y > 0 then a(y - 1);
end;
7Units and libraries
A program can be split into units written in Pascal, and it can call Java through library
classes. ANPASIDE also ships three libraries of its own.
Units
unit geometry;
interface
function square(x: integer): integer;
implementation
function square(x: integer): integer;
begin
square := x * x;
end;
initialization
{ runs when the program starts }
end.
program game;
uses geometry;
var area: integer;
begin
area := geometry.square(5); { or square(5), unless two units share the name }
end.
A unit name has at least three characters; a shorter one is error E454, whose message says
two. There is no finalization section.
A unit shares everything in its interface: constants, types, variables,
procedures and functions. Its types, unlike the rest, are only found by their full name. With
a unit shapes that declares type point = record x, y: integer; end,
another module writes var p: shapes.point; a bare point is error
E415, even for an alias such as type index = integer.
Libraries in Java
When a program needs something of MIDP that the language does not offer, it can be written
in Java as a library class:
// Lib_mylib.java, compiled for CLDC 1.0 and preverified
public class Lib_mylib {
public static int square(int value) {
return value * value;
}
}
program demo;
uses mylib;
var id: integer;
begin
ShowForm;
id := FormAddString('5 squared is ' + mylib.square(5));
Delay(5000);
end.
The class has no package, and its name is Lib_ followed by the unit name in
lowercase.
Only methods declared exactly public static are seen. One that is also
final or synchronized is skipped without a word.
Parameters and results may be int, String, Image,
Command, RecordStore or InputStream, and a method may
return void.
A library has routines only, no types or variables.
The class goes in the project's library folder, libs/ in ANPASIDE, or in the
compiler's global one. The project's folder is searched first.
For phone features such as vibration or the backlight, a library needs the
Display; the original manual's chapter on advanced features shows how to reach it.
A call that exists only in MIDP 2.0 belongs in a try/catch, or the
MIDlet crashes on a MIDP 1.0 phone.
Libraries in ANPASIDE
ANPASIDE ships three libraries that a program reaches with uses and nothing to
copy. A Lib_ class of the same name in the project's libs/ takes their
place.
the parts of the screen the camera and the corners cover
since 2.6.0
Touch
For on-screen buttons held with several fingers, to walk and jump at the same time:
program buttons;
uses Touch;
var w, h, x, y, vy, floor: integer;
begin
Touch.Init;
w := GetWidth;
h := GetHeight;
floor := h - 120;
x := w div 2;
y := floor;
vy := 0;
repeat
{ each button follows its own finger }
if touch_area(0, h - 80, 80, 80) > 0 then x := x - 4;
if touch_area(90, h - 80, 80, 80) > 0 then x := x + 4;
if (touch_area(w - 80, h - 80, 80, 80) > 0) and (y = floor) then vy := -15;
y := y + vy;
vy := vy + 1;
if y >= floor then
begin
y := floor;
vy := 0;
end;
SetColor(0, 0, 0);
FillRect(0, 0, w, h);
SetColor(255, 255, 255);
FillRect(x, y - 40, 20, 40);
DrawRect(0, h - 80, 80, 80);
DrawRect(90, h - 80, 80, 80);
DrawRect(w - 80, h - 80, 80, 80);
Repaint;
Delay(20);
until false;
end.
Fingers are numbered 0 to 9. The first finger down is 0, and each next one takes the lowest
free number. Touch also has every function of Lib_sensor, under the same names,
following finger 0. A program moves over by writing uses Touch and
Touch.Init where it had Sensor.
The touch_ functions see every finger and the pointer_ functions
only finger 0, so they do not mix. A program that checks a rectangle with
touch_area and then reads the position from pointer_pressed_x reads
the wrong finger as soon as two are down. Take the position from the finger that is inside:
for i := 0 to 9 do
if (touch_down(i) = 1) and (touch_x(i) >= x) and (touch_x(i) < x + w)
and (touch_y(i) >= y) and (touch_y(i) < y + h) then
slot := (touch_x(i) - x) div cell;
Following Nokia, J2ME Loader and ANPASIDE's runtime report more than one finger only to a
JAR whose manifest has Nokia-UI-Enhancement: EnableMultiPointTouchEvents, and
ANPASIDE adds it to every program that uses Touch. The fingers are told apart through Nokia's
com.nokia.pointer.number property, so the JAR works in J2ME Loader too. On a phone
without multi-touch every touch is finger 0, and Touch behaves like Lib_sensor.
The built-in OpenPlayer has a single player, so background
music stops for every effect. Sound has eight channels, 0 to 7. Each channel holds one sound
and only ever replaces its own: music on one, effects on the others. It needs no
initialization.
program game;
uses Sound;
var ok: integer;
begin
ok := sound_open(0, '/music.mp3', 'audio/mpeg');
ok := sound_open(1, '/shot.wav', 'audio/x-wav');
sound_loop(0, -1);
sound_volume(0, 60);
ok := sound_play(0);
repeat
{ the shot starts over at every press, the music plays on }
if KeyToAction(GetKeyPressed) = GA_FIRE then ok := sound_play(1);
Delay(20);
until false;
end.
The loop count and the volume belong to the channel, not to the sound, so a music channel
keeps its volume from one track to the next. OpenPlayer still works alongside, as
one more sound.
In ANPASIDE's emulator and in exported APKs, Android plays every sound and mixes them, two
MIDI files included. It takes MIDI, WAV, MP3, AAC and AMR. A type it does not know, such as
audio/ogg, makes sound_open return 0. Whether an old phone mixes
sounds at all is up to the phone; one that does not may return 0 from
sound_play while another sound plays.
ANPASIDE's emulator and the APKs it exports draw the canvas over the whole screen, under
the camera cutout and into the rounded corners. Screen tells how far each of them reaches into
the canvas, so that buttons and text stay clear while the game world still fills the
screen:
program hud;
uses Screen;
var w, h, l, t, r, b: integer;
begin
repeat
w := GetWidth;
h := GetHeight;
l := screen_inset_left;
t := screen_inset_top;
r := screen_inset_right;
b := screen_inset_bottom;
SetColor(80, 160, 255);
FillRect(0, 0, w, h); { the world, edge to edge }
SetColor(255, 255, 255);
DrawRect(w - r - 64, t, 64, 32); { a menu button in the top right corner }
DrawText('v 1.0', l, h - b - GetStringHeight('A'));
Repaint;
Delay(20);
until false;
end.
The cutout counts as a band along its whole edge, as Android reports it. A rounded corner
counts as the distance that brings the corner of a rectangle inside the curve, on both edges
it touches. A Pixel 9 held sideways answers 173 on the camera side and 39 on the other three.
The values change when the screen turns, so a game that rotates reads them again.
The runtime measures the screen and hands the values out as the system properties
anpaside.screen.inset.left, .top, .right and
.bottom. Only ANPASIDE answers them; in J2ME Loader and on an old phone every
function returns 0, which is also correct there.
comparison; strings compare with upper and lower case distinct
and or xor not
logic on booleans, bitwise on integers
shl shr ushr
shifts, also written << >> >>>; ushr fills with zeros, as Java's >>> does
+ on a string
joins it with a string, char, integer, real or boolean
not binds tightest and the comparisons loosest, as in other Pascals, so
(a > 0) and (b > 0) needs its parentheses.
Literals
255 $FF { integers, decimal and hex }
3.14 { a real }
'it''s' { a string; a doubled quote is a quote }
"it's" { a string in double quotes }
#65 #$41 { the char 'A' by its code }
9Real numbers
CLDC 1.0, the Java ME of the early phones, has no floating point types, so
real arithmetic is done in software. It is slow and makes the JAR bigger. The build, not the source, chooses one of two
implementations:
Fixed point
Floating point
Switch
-m1, the default
-m2
Classes
F.class
Real.class, Real$NumberFormat.class
Precision
low, a fixed number of fractional bits
much higher
Cost
fast and small
slower, several KB more
All math functions but Abs and Sqr work
on reals, as do StringToReal and the constant pi. In ANPASIDE the choice is the MathType line of the project's
.aproj file.
10Beyond standard Pascal
MIDletPascal accepts a few things that standard Pascal does not:
exit and result in routines, as Delphi
and Free Pascal have them;
forever to close a repeat loop;
ushr, also written >>>;
// and /* */ comments, and strings in double quotes;
raw JVM instructions in a bytecode … end block, with labels.
Most of them were added by Javier Santo Domingo for MIDletPascal 3. mp3cc is the
3.5 compiler ported to Linux and Android, and the port added one thing to the language:
case, which 3.5 rejected with E442.
A build that only checks the compiler's messages can miss a failure: if the compiler process
is killed, by a bad argument or a binary built for the wrong system, it writes no
M.class and no error. A JAR holding nothing but FW.class is the sign.
The exit code of mp3cc is its error count, so any other exit code means the process
died. ANPASIDE checks both.
Built-in routines
The 159 routines every program can call without a uses clause, by
area. Each signature is the one mp3cc accepts. The 4 routines marked
not in mp3cc are listed in the original manual, but MIDletPascal 3.5,
and so mp3cc, does not have them.
Nothing matches . Search looks at routine names and
descriptions.
Everything is drawn into a buffer the size of the canvas, in the color set by SetColor, and Repaint shows it. The origin is the top left corner; x grows to the right and y down.
procedure DrawArc(x, y, width, height, startAngle, arcAngle: integer);
Draws the outline of an arc inside the rectangle at x, y. The arc begins at startAngle and runs arcAngle degrees counterclockwise. 0° points to three o'clock and 90° to twelve.
Example
begin
DrawArc(0, 0, GetWidth, GetHeight, 0, 90);
Repaint;
Delay(1000);
end.
var logo: image;
begin
logo := LoadImage('/logo.png');
{ centered on the screen }
DrawImage(logo, (GetWidth - GetImageWidth(logo)) div 2,
(GetHeight - GetImageHeight(logo)) div 2);
Repaint;
Delay(1000);
end.
function ImageFromBuffer(buffer: string; length: integer): image;
Decodes an image held in a string, for example one read from a resource or received over HTTP. Every phone takes PNG, some also JPEG. length is the number of bytes to decode. The original manual does not mention it.
Copies the drawing buffer to the screen. Drawing routines only change the buffer, so nothing is visible until Repaint. A repaint is slow on a phone: draw the whole frame first and call it once per frame.
Example
begin
DrawText('Hello world', 0, 0);
Repaint;
Delay(1000);
end.
Limits drawing to a rectangle: anything drawn outside it is dropped. SetClip(0, 0, GetWidth, GetHeight) lifts the limit.
Example
begin
SetColor(0, 0, 0);
FillEllipse(0, 0, GetWidth, GetHeight);
Repaint;
Delay(2000);
SetClip(10, 10, 15, 25); { only this part of the next ellipse is drawn }
SetColor(255, 0, 0);
FillEllipse(0, 0, GetWidth, GetHeight);
Repaint;
Delay(2000);
end.
The code of the last key pressed since the previous call, or KE_NONE. Reading it clears it, so a key press is reported once. The number keys, * and # have constants (KE_KEY0 to KE_KEY9, KE_STAR, KE_POUND); for the arrows and fire, use KeyToAction.
Example
begin
repeat
Delay(100);
until GetKeyClicked = KE_STAR;
end.
Turns a key code into a game action, which is the same on every phone: GA_UP, GA_DOWN, GA_LEFT, GA_RIGHT, GA_FIRE, GA_GAMEA to GA_GAMED, or GA_NONE. The raw codes of the arrow keys differ from maker to maker; see key scan codes.
Example
begin
repeat
Delay(100);
until KeyToAction(GetKeyClicked) = GA_FIRE;
end.
The system clock in milliseconds, cut to a 32-bit integer: good for timing, not for telling the date. It can be negative, and about every 50 days it jumps from the largest integer to the smallest. The difference of two readings, now - last, stays right across that jump.
Example
{ move the block down once a second, sideways at every key press }
last := GetRelativeTimeMs;
repeat
key := KeyToAction(GetKeyClicked);
if key = GA_LEFT then moveLeft;
if key = GA_RIGHT then moveRight;
now := GetRelativeTimeMs;
if now - last > 1000 then
begin
last := now;
moveDown;
end;
Delay(20);
until gameOver;
function Copy(s: string; first, last: integer): string;
The part of s from index first up to, but not including, last. Indexes start at 0, so Copy('MIDletPascal', 2, 5) is 'Dle'. This is not the Copy(s, index, count) of other Pascals.
function SetChar(s: string; c: char; index: integer): string;
A copy of s with the character at index replaced by c. Past the end of the string it returns s unchanged. Strings cannot be changed in place, so assign the result: s := SetChar(s, 'x', 0).
The screen shows one thing at a time: the canvas, the form, a menu, a text box or an alert. A MIDlet starts on the canvas. The form is a list of items, such as text, fields, gauges and choices, that the FormAdd functions fill and that are referred to by the id they return. Commands sit on the soft keys of any of these screens.
Puts a command on the soft keys or in the phone's menu, on the canvas or on a form. Some phones, Motorola among them, only show it after the next Repaint.
function ChoiceAppendString(choice: integer; text: string): integer;
Adds an item to a choice group and returns the item's index within the group.
Example
var city, newYork, losAngeles: integer;
begin
ShowForm;
city := FormAddChoice('Where do you live?', CH_EXCLUSIVE);
newYork := ChoiceAppendString(city, 'New York');
losAngeles := ChoiceAppendString(city, 'Los Angeles');
Delay(5000);
end.
function CreateCommand(label: string; commandType, priority: integer): command;
Creates a command to put on the screen with AddCommand. Keep the label short. commandType tells the phone what the command means, so that it can put, say, a CM_BACK on the key where the phone keeps “back”: CM_SCREEN, CM_BACK, CM_CANCEL, CM_OK, CM_HELP, CM_STOP, CM_EXIT or CM_ITEM. A lower priority is more important.
Example
var exitCmd, pauseCmd: command;
begin
exitCmd := CreateCommand('Exit', CM_EXIT, 1);
pauseCmd := CreateCommand('Pause', CM_SCREEN, 1);
AddCommand(exitCmd);
AddCommand(pauseCmd);
repeat
Delay(100);
until GetClickedCommand = exitCmd;
end.
function FormAddChoice(label: string; choiceType: integer): integer;
Adds an empty choice group to the form and returns its id; fill it with ChoiceAppendString. With CH_EXCLUSIVE exactly one item is selected, with CH_MULTIPLE any number.
function FormAddTextField(prompt, text: string; maxSize, constraints: integer): integer;
Adds a text field with text already in it and returns its id. maxSize is the most characters it takes. constraints limits what can be typed: TF_ANY, TF_EMAIL, TF_NUMERIC, TF_PHONENUMBER or TF_URL. Adding TF_PASSWORD to any of them hides the text as it is typed.
Adds FS.class to the JAR.
Example
var name: integer;
begin
name := FormAddTextField('Enter your name', 'Mr. Smith', 20, TF_ANY);
ShowForm;
Delay(5000);
ShowAlert('Hello', FormGetText(name), LoadImage('/icon.png'), ALERT_INFO);
Delay(2000);
end.
Fills the screen with a message. Only commands can share the screen with an alert; ShowForm or ShowCanvas takes it away. alertType is ALERT_INFO, ALERT_WARNING, ALERT_ERROR, ALERT_ALARM or ALERT_CONFIRMATION.
Example
var read: command;
begin
ShowAlert('New message', 'Mr. Smith has written to you', LoadImage('/icon.png'), ALERT_INFO);
PlayAlertSound;
read := CreateCommand('Read', CM_OK, 1);
AddCommand(read);
repeat
Delay(100);
until GetClickedCommand = read;
ShowForm;
end.
Brings back the canvas, the screen the program draws on. A MIDlet starts on the canvas; ShowForm, ShowMenu, ShowTextBox and ShowAlert replace it until this is called.
Example
var greeting: integer;
begin
greeting := FormAddString('Hello from the form');
ShowForm;
Delay(2000);
ShowCanvas;
DrawText('Hello from the canvas', 0, 0);
Repaint;
Delay(2000);
end.
Shows the form: the screen of text, fields, choices and images that the FormAdd functions fill. There is one form; it keeps its items while the canvas is shown.
Example
var greeting, name: integer;
begin
greeting := FormAddString('Hello world');
name := FormAddTextField('Enter your name', 'Mr. Smith', 20, TF_ANY);
ShowForm;
Delay(2000);
end.
Fills the screen with an empty menu, to be filled with MenuAppendString. Only commands can share the screen with it. CH_IMPLICIT is an ordinary list to pick from; CH_EXCLUSIVE shows a radio button next to each entry and CH_MULTIPLE a check box.
Fills the screen with a text editor holding text. Only commands can share the screen with it. constraints takes the same TF_ constants as FormAddTextField.
A record store is a named list of strings that the phone keeps after the MIDlet exits: saved games, settings, high scores. Each record has an index starting at 1.
mp3cc does not accept it. The original manual lists the enumeration routines, but MIDletPascal 3.5, and so mp3cc, does not have them. Go through the records by index from 1 to GetRecordStoreNextId − 1 and skip the empty ones.
function EnumerateRecords(rs: recordStore): integer;
mp3cc does not accept it. The original manual lists the enumeration routines, but MIDletPascal 3.5, and so mp3cc, does not have them. Go through the records by index from 1 to GetRecordStoreNextId − 1 and skip the empty ones.
function GetRecordStoreNextId(rs: recordStore): integer;
The index the next AddRecordStoreEntry will give. Every index below it has been used, though some may have been deleted since.
Adds RS.class to the JAR.
Example
var rs: recordStore;
i: integer;
data: string;
begin
rs := OpenRecordStore('scores');
for i := 1 to GetRecordStoreNextId(rs) - 1 do
begin
data := ReadRecordStoreEntry(rs, i);
if data <> '' then Debug(data);
end;
CloseRecordStore(rs);
end.
mp3cc does not accept it. The original manual lists the enumeration routines, but MIDletPascal 3.5, and so mp3cc, does not have them. Go through the records by index from 1 to GetRecordStoreNextId − 1 and skip the empty ones.
function OpenRecordStore(name: string): recordStore;
Opens the record store called name, creating an empty one the first time. A record store is the phone's storage for a MIDlet: it keeps its records after the MIDlet exits, until it is deleted with DeleteRecordStore or the MIDlet is uninstalled.
mp3cc does not accept it. The original manual lists the enumeration routines, but MIDletPascal 3.5, and so mp3cc, does not have them. Go through the records by index from 1 to GetRecordStoreNextId − 1 and skip the empty ones.
HTTP
A request is built up on an http variable: open it, set the method and the headers, send it, read the answer, close it.
function OpenHttp(conn: http; url: string): boolean;
Opens a connection to url, written as http://host[:port][/path]. Returns false if it fails. A phone usually asks the user before a MIDlet goes online.
Adds H.class to the JAR.
Example
var conn: http;
page: string;
begin
if OpenHttp(conn, 'http://example.com/') then
begin
SetHttpMethod(conn, GET);
if SendHttpMessage(conn) = 200 then
page := GetHttpResponse(conn);
CloseHttp(conn);
end;
end.
Sends the request, waits for the answer and returns the HTTP status code, such as 200 for success or 404 for a missing page, or −1 if the request could not be made.
function SmsStartSend(destination, message: string): boolean;
Starts sending a text message and returns at once. destination is written sms://+number. Returns false if the message could not be handed over, for example because another one is still on its way. MIDletPascal sends through the Wireless Messaging API or the Siemens API; a phone with neither cannot send at all.
Adds SM.class to the JAR.
Example
begin
if not SmsStartSend('sms://+5550000', 'Hello!') then Halt;
while SmsIsSending do
Delay(100);
if not SmsWasSuccessfull then Halt;
end.
true if the last message was sent. false means the phone cannot send messages from a MIDlet, the number is wrong, or the network refused it. The name is spelled with two l's.
function OpenPlayer(name, mimeType: string): boolean;
Loads a sound from the JAR; StartPlayer plays it. Returns false if it fails. There is a single player, and opening a sound closes the one before it, so music stops for every effect. For several sounds at once, use the Sound library.
MIDI: audio/midi
WAV: audio/x-wav
MP3: audio/mpeg
AU: audio/basic
Phones rarely play all of them.
Adds P.class to the JAR.
Example
begin
if not OpenPlayer('/explosion.mid', 'audio/midi') then Halt;
if not SetPlayerCount(-1) then Halt;
if not StartPlayer then Halt;
Delay(5000);
end.
Plays one tone. note is a MIDI note number from 0 to 127, where 69 is the A at 440 Hz; duration is in milliseconds and volume 0 to 100. The original manual does not mention it.
Opens a file from the JAR for reading. As with LoadImage, the name starts with /; in ANPASIDE the files of a project's res/ folder go into the JAR. Check ResourceAvailable before reading.
Adds S.class to the JAR.
Example
var res: resource;
line: string;
shown: integer;
begin
res := OpenResource('/data.txt');
if ResourceAvailable(res) then
begin
line := ReadLine(res);
CloseResource(res);
end;
ShowForm;
shown := FormAddString('First line: ' + line);
Delay(2000);
end.
The next line of text. At the end of the file, or on an error, it returns an empty string. Empty lines are skipped, so an empty result always means the end.
1 if finger n has touched the screen since the previous call for that finger, otherwise 0. Unlike touch_down, it does not miss a tap shorter than a frame. Reading it clears it, so read each finger once per frame and keep the result.
Example
uses Touch;
var i, taps: integer;
begin
Touch.Init;
taps := 0;
repeat
for i := 0 to 9 do
if touch_pressed(i) = 1 then taps := taps + 1;
{ ... draw, Repaint, Delay ... }
until false;
end.
function touch_area(x, y, w, h: integer): integer;
The number of fingers inside the rectangle at x, y of size w × h. An on-screen button is held while this is above 0, whichever finger holds it, so several buttons can be held at once.
Example
{ a jump button in the lower right corner }
if touch_area(GetWidth - 80, GetHeight - 80, 80, 80) > 0 then
Jump;
function has_pointer_events: integer;
function has_pointer_motion_events: integer;
function pointer_state: integer;
function pointer_pressed_x: integer;
function pointer_pressed_y: integer;
function pointer_dragged_x: integer;
function pointer_dragged_y: integer;
function pointer_released_x: integer;
function pointer_released_y: integer;
The functions of Lib_sensor, under the same names and with the same meaning, for finger 0 only. pointer_state is 1 while finger 0 is down, and a second finger does not change any of them, so do not mix them with touch_area, which counts every finger.
Sound
Reached with uses Sound;. Channels are numbered 0 to 7. A call with any other number does nothing and reports failure: 0, or −1 from sound_duration.
function sound_open(ch: integer; name, mimeType: string): integer;
Loads the sound name from the JAR into channel ch, 0 to 7, and closes what the channel held before. Returns 1 on success, 0 if the channel, the file or the type is wrong. The types are audio/midi, audio/x-wav, audio/mpeg, audio/aac, audio/mp4 and audio/amr.
Example
ok := sound_open(0, '/music.mid', 'audio/midi');
ok := sound_open(1, '/coin.wav', 'audio/x-wav');
Plays channel ch from the start. If it is already playing it starts over, so an effect fired twice cuts its first run short. Returns 1 on success, otherwise 0.
How many times channel ch plays in a row, −1 for endlessly; 1 at first. It takes effect at the next sound_play and stays with the channel through sound_open.
The length of the sound on channel ch in milliseconds, or −1 if it is not known. ANPASIDE's emulator only knows it while the sound plays, and answers 0 before that.
function screen_inset_left: integer;
function screen_inset_top: integer;
function screen_inset_right: integer;
function screen_inset_bottom: integer;
How far the camera cutout or a rounded corner reaches into that edge of the canvas, in canvas pixels, or 0. A control kept at least that far from every edge is seen whole. The values follow the screen when it turns. Outside ANPASIDE's emulator and the APKs it exports they are always 0.
Example
{ a menu button the top right corner does not cut off }
DrawImage(menu, GetWidth - screen_inset_right - GetImageWidth(menu), screen_inset_top);
The codes GetKeyPressed and GetKeyClicked return. The number keys,
* and # are the same everywhere; the others depend on the maker, which
is why there is KeyToAction.