Ordinal types
Type | Range | Size in bytes |
---|---|---|
BYTE | 0 .. 255 | 1 |
SHORTINT | -128 .. 127 | 1 |
WORD | 0 .. 65535 | 2 |
SMALLINT | -32768 .. 32767 | 2 |
CARDINAL | 0 .. 4294967295 | 4 |
LONGWORD | 0 .. 4294967295 | 4 |
DWORD | 0 .. 4294967295 | 4 |
UINT32 | 0 .. 4294967295 | 4 |
INTEGER | -2147483648 .. 2147483647 | 4 |
LONGINT | -2147483648 .. 2147483647 | 4 |
Boolean types
Type | Ord(True) | Size in bytes |
---|---|---|
BOOLEAN | 1 | 1 |
Enumeration types
The enumeration type in Mad-Pascal has been implemented in its basic form, i.e.:
Type
Days = (monday,tuesday,wednesday,thursday,friday,
saturday,sunday);
Joy = (right_down = 5, right_up, right, left_down = 9, left_up, left, down = 13, up, none);
The enumeration type is stored only in the memory of the Mad-Pascal compiler, no information about the enumeration type fields will be stored in the result file. It is permissible to use the ORD
, SIZEOF
and casts on the enumeration type.
var
d: Days;
d:=friday;
writeln(ord(d));
writeln(ord(sunday));
writeln(sizeof(days));
writeln(sizeof(monday));
d:=days(20);
case d of
sunday: writeln('sunday');
end;
Currently, the Mad-Pascal compiler does not check the correctness of enumeration types for IF ELSE
operations.
Real types
Type | Range | Size in bytes |
---|---|---|
SHORTREAL (Q8.8) | -128..127 | 2 |
REAL (Q24.8) | -8388608..8388607 | 4 |
SINGLE (IEEE-754) | 1.5E-45 .. 3.4E38 | 4 |
FLOAT (IEEE-754) | 1.5E-45 .. 3.4E38 | 4 |
FLOAT16 (IEEE-754) | 65504 .. -65504 | 2 |
Conversion of FLOAT
SINGLE
to INTEGER
type is only available in the range INTEGER
. The INTEGER
type will not allow to present the maximum value 3.4E38
of FLOAT
SINGLE
type.
Char types
Type | Range | Size in bytes |
---|---|---|
CHAR | ATASCII (0 .. 255) | 1 |
STRING | 1 .. 255 | 256 |
PCHAR | 0 .. 65535 | 2 |
The STRING
is represented as an array with a possible maximum size [0..255]
. The first byte of such an array [0]
is the string length from the range 0..255
. The actual character string begins from the byte [1..]
.
A pointer to the CHAR
type represents the PCHAR
string. The terminator of the PCHAR
string is the #0
character.
It is allowed to use additional characters after the final apostrophe, such as *
, ~
.
The character *
means a string in the inverse; the tilde ~
means a string in ANTIC codes.
Another way to modify the output characters is to use the system variable TextAttr
, each character output to the screen is increased by the value TextAttr
(default = 0).
a: string = 'Atari'*; // a character string in the inverse
b: string = 'Spectrum'~; // a character string in ANTIC codes
c: char = 'X'~*; // a character in inverted ANTIC codes
Pointers
Type | Range | Size in bytes |
---|---|---|
POINTER | 0 .. 65535 | 2 |
Indicators in Mad-Pascal can be typed and without a specific type, e.g.:
a: ^word; // a typed pointer to a word
b: pointer; // an untyped pointer
An uninitialized pointer will most often have the address of $0000
, you should make sure that before you use it, you will have initialized it with the address of the appropriate variable, e.g.:
a := @tmp; // pointer A is assigned the address of the TMP variable
If you don't do this, if you run such a program on a PC, you may cause a memory protection fault Access Violation.
Increasing the pointer using INC
increases it by the size of the type it indicates. Decreasing the pointer using DEC
reduces it by the size of the type it indicates. If the type is unspecified, the default step for increase/decrease is 1
.
For pointers, relation operations =
, <>
, <
, <=
, >
, >=
, and arithmetic operations +
and -
are allowed.
Using a pointer, we can cast a variable to another type:
var
s: single;
d: cardinal;
begin
s := 3.14;
d:=PCardinal(@s)^; // d = $4048F5C3
end;
Static arrays
Tables in Mad-Pascal are only static, one-dimensional or two-dimensional with an initial index equal to 0
, e.g:
var tb: array [0..100] of word;
var tb2: array [0..15, 0..31] of Boolean;
For an initial index other than zero, an error Error Array lower bound is not zero is generated.
In the memory the array is represented by the pointer POINTER
, the pointer is the address of the array in memory (WORD). The quickest way to refer to the table from an ASM
block is to use the prefix ADR
, e.g.:
asm
{ lda adr.tb,y ; direct reference to the TB array
lda tb ; reference to the TB array pointer
};
The compiler generates code for the arrays depending on their declaration:
- when the number of bytes does not exceed 256 bytes
array [0..255] of byte
array [0..127] of word
array [0..63] of cardinal
When the number of bytes occupied by the array does not exceed 256 bytes, the fastest code referring directly to the address of the array (prefix ADR.
) is generated without the pointer. It is not possible to change the address for such an array.
ldy #118
lda adr.tb,y
- when the number of elements of an array is
1
array [0..0] of type
When the number of elements of an array is 1
it is treated specifically. The code generated refers to the array through the pointer. It is possible to set a new address for such a table.
lda TB
add I
tay
lda TB+1
adc #$00
sta bp+1
lda (bp),y
- when the number of bytes exceeds 256 bytes
array [0..255+1] of byte
array [0..127+1] of word
array [0..63+1] of cardinal
When the number of bytes occupied by the array exceeds 256 bytes, the generated code refers to the array via an pointer. When the number of bytes occupied by the array exceeds 256 bytes, the generated code refers to the array through a pointer.
lda TB
add I
tay
lda TB+1
adc #$00
sta bp+1
lda (bp),y
Record types
In the memory the record is represented by a pointer POINTER
.
type
TPoint = record x,y: byte end;
var px: TPoint;
By default, records in Mad-Pascal are of type PACKED
. The total size of the record fields is limited to 256 bytes.
If you want to maintain FPC compatibility, you should additionally precede the word record
with the word packed
.
Without this, the size of the memory that the record takes varies, it occupies less memory on MOS 6502 target, potentially several bytes more on Windows.
type
TPoint = packed record x,y: byte end;
var px: TPoint;
Access to record fields from the assembly:
mwa px bp2
ldy #px.x-DATAORIGIN
lda (bp2),y
Table of records
Mad-Pascal only supports arrays of record pointers.
type
TPoint = record x,y: byte end;
var
tab: array [0..3] of ^TPoint;
Such an array must be instantiated with the corresponding record addresses, by default all fields of such an array are zeroed at the beginning.
The first way to instantiate an array of record indicators:
var
a1,a2,a3,a4: TPoint;
begin
tab[0] := @a1;
tab[1] := @a2;
tab[2] := @a3;
tab[3] := @a4;
end.
Second way:
begin
GetMem(tab[0], sizeof(TPoint));
GetMem(tab[1], sizeof(TPoint));
GetMem(tab[2], sizeof(TPoint));
GetMem(tab[3], sizeof(TPoint));
end.
Access record fields from such an array:
writeln(tab[1].x);
writeln(tab[1].y);
Object types
Objects are records with additional methods. In the memory, the object is represented by a pointer POINTER
.
type
TRMT = Object
player: pointer;
modul: pointer;
procedure Init(a: byte); assembler;
procedure Play; assembler;
procedure Stop; assembler;
end;
It is possible to use the CONSTRUCTOR
and DESTRUCTOR
procedures in objects. Such procedures can only be called manually.
Procedural
In memory, procedural type variables are represented by the POINTER
type.
type
tprc = procedure (a: byte; c: word);
tfun = function (a:smallint; x: single): byte;
var
fn: function (a,b,c: byte): word;
For the procedural type, procedures/functions with arguments require the STDCALL
modifier, which will force the use of the program stack.
var
fn: function (a,b: word): word;
function test(a,b,c,d: word): word; stdcall;
begin
end;
begin
fn := @test;
fn(1,2);
end;
For procedures with arguments instead of the STDCALL
modifier, the REGISTER
modifier is allowed, provided there are up to three arguments.
var
prc: procedure (a,b: word);
procedure test(a,b,c: cardinal); register;
begin
// a -> EDX
// b -> ECX
// c -> EAX
end;
begin
prc := @test;
prc(3,6);
end;
When no arguments are passed to the procedure/function, the use of modifier is not necessary.
File types
The FILE
type represents the file handle and defines the record size.
type
ftype = array [0..63] of cardinal;
var
f: file; // default record =128 bytes
f: file of byte; // 1 byte record
f: file of ftype; // 256 byte record (ftype = 64 * 4)
In the Atari 8-Bit memory, the FILE holder is represented by a pointer POINTER
to an array of structure (size 12 bytes):
.struct s@file
pfname .word ; pointer to string with filename
record .word ; record size
chanel .byte ; channel *$10
eof .byte ; EOF status
buffer .word ; load/write buffer
nrecord .word ; number of records for load/write
numread .word ; pointer to variable, length of loaded data
.ends
For procedures and functions, the FILE
type can only be passed as a variable VAR
.
Untyped
procedure Something (var Data);
procedure Something (const Data);
Failure to specify the type of the parameter means that only the address of the parameter without the type designation will be passed to the procedure/function.
This is equivalent to the following C/C++ declaration:
void Something(void* Data);
Inside a procedure/function with an unsigned parameter, if an unsigned parameter is used in an expression or a value must be assigned to it, always use type casting.
var x: word;
procedure test(var a);
begin
writeln(PWord(@a)^); // = 95
PWord(@a)^ := 11;
end;
begin
x:=95;
test(x); // = 11
end.