Oberon for ZX Spectrum
Subtleties when developing on Oberon
in the ZXDev environment
Oleg N. Cher, VEDAsoft Oberon Club
The concept of the XDev environment (and its subsystem
ZXDev,aimed at development for Spect─
room) was formed over several
years, but found its implementation in the form of pen─
the second version is relatively recent ─ at the end
January 2015 Main features:
1.The input language is used
language Oberon and its supersets ─ languages
Oberon-2 andComponent Pascal(from after─
only some features are supported).
2.Code generation is implemented via trans─
fusionOberoninCwith subsequent call
Cash compiler. As the mainthe SDCC compiler is used, but by
editing assembly scripts is possible
connection of other compilers, for example─
measures, z88d.
3.Multi-targeting. In the repository about─
Project XDev(https://github.com/Oleg-N-Cher/
XDev) there are subsystems for MSX targets,
MS-DOS, Windows (32/64 bit), Linux, naho─
cooking in varying degrees of readiness. Not
published, but subsystems are also outlined
for development for NES/Nintendo, Java ME and
Android. The first version has already been released for─
ZXDev systems with a set of libraries in the computer─
lecture, although not very rich, and examples
simple games on Oberon ─
https://sourceforge.net/projects/bb-xdev
Oberon is a very compact modular
programming language (component, object─
technology-oriented - depending on the di─
alect) with a structural paradigm, strict
typing and automatic control
memory, designed for universal use─
th application (including systemic, where
it also plays the role of a scripting language─
ka ─ OS ETH Oberon, A2/Bluebottle). This
the quintessence of creativity classic software─
of Dr. Niklaus Wirth, author
PascalandModules-2. In contrast to modern ones─
by means of the "main thread" (main
stream) ─ large languages, also expanding─
With further complication,Oberon is based
on the most key concepts of computer science ─
module, procedure, data structure (for─
writing).
The Oberon paradigm is also subject to fra─
gmentation, like other areas of IT. Therefore
and in the Oberon paradigm there are dialects, for example─
merOberon-07 (ultra-minimalistic revie─
Zia Oberon-1 ), OberonX (mathematical
extension),Active Oberon (multithreaded
dialect),Oberon-2 (with advanced tools─
OOP by you), and also my especially favorite
Component Pascal (supersetObero─
na-2 for industrial applications).
Unsigned calculations
LanguageOberon does not have unsigned types
data, this is similar to the Z80 processor
has no unsigned registers or cells
memory. But their meanings can be interpreted─
act as unsigned numbers andto be under pressure─
are averse to unsigned operations.
This is also similar to the languageFort ─ meaning─
those on the stack do not have a type - they can
be interpreted as both signed and unsigned─
high (or even double - occupying two
words). And although most stack opera─
tions are iconic, but along with the iconic it’s smart─
multiplication (* ) is also unsigned multiplication
(U* ), the same with comparison and in practice─
niy, etc. In general, system software
the mist should not be embarrassed by the lack of ignorance─
kovyh types, it is enough for him to know that ZXDev
has three types for integer data
1, 2 and 4 bytes in size. Let's consider how
way to determine effective without─
sign operations in ZXDev.
Unsigned byte comparison. This trick
I came up with this while working on the port
games "Fool", where a lot of signs are used─
comparisons of bytes for more or less, to─
which can be freely changed to unsigned─
higher comparisons, thereby increasing the efficiency─
code.
Sign comparison:
IF a > b THEN ...
Unsigned comparison:
IF CHR( a ) > CHR( b ) THEN ...
Here the signed values are reduced to
unsigned typeCHAR and then compare─
s.
Addition and subtraction are implemented in one way─
hard for signed and unsigned. But this one─
Let's try to implement effective (without
overhead) unsigned operations
division and multiplication for bytes and words. By─
In this way you can implement any
other operations not present in the language
(after all, you can’t foresee everything):
MODULE UMath; IMPORT SYSTEM, B := Basic;
VAR a, b: SHORTINT;
PROCEDURE -UMultBytes (a, b: SHORTINT):
SHORTINT
"( ((CHAR)a) * ((CHAR)b) )";
PROCEDURE -UMultWords (a, b: INTEGER):
INTEGER
"(((unsigned int)a)*((unsigned int)b))";
PROCEDURE -UDivBytes (a, b: SHORTINT):
SHORTINT
"( ((CHAR)a) / ((CHAR)b) )";
PROCEDURE -UDivWords (a, b: INTEGER):
INTEGER
"(((unsigned int)a)/((unsigned int)b))";
BEGIN (*$MAIN*)
B.Init;
(* Приводим, т.к. значение >
SIZE(SHORTINT): *)
a := SYSTEM.VAL(SHORTINT, 255);
b := SYSTEM.VAL(SHORTINT, 255);
(* Печатает: 65025 (255*255): *)
B.PRWORD( UMultBytes(a, b) );
B.PRLN;
(* Печатает: 1 ( (-1)*(-1) ): *)
B.PRWORD( a * b );
B.PRLN;
a := SYSTEM.VAL(SHORTINT, 255); b := 5;
(* Печатает: 51 (255 DIV 5): *)
B.PRWORD( UDivBytes(a, b) );
B.PRLN;
(* Печатает: 0 (-1 DIV 5): *)
B.PRWORD( a DIV b );
B.Quit
END UMath.
Notice the weird one at first
view the result of procedures ─UMultiBytes and
rest ─ it is the same size as argu─
cops. This must mean that the overflow
when multiplying it will result in loss of bit depth re─
result, but in practice this is not
occurs because from within these operations
are arranged like this:
#define UMath_UDivBytes(a, b)
( ((CHAR)a) / ((CHAR)b) )
#define UMath_UDivWords(a, b)
(((unsigned int)a)/((unsigned int)b))
#define UMath_UMultBytes(a, b)
( ((CHAR)a) * ((CHAR)b) )
#define UMath_UMultWords(a, b)
(((unsigned int)a)*((unsigned int)b))
Here I used when describing the result─
that short type (byte) so that the result is
compatible with short type (in case of─
assigning the result to a variable length of 1
byte) without extension, i.e.:
short:=UMultBytes(short1,short2);
instead of the obvious characteristic forOberon
SHORT() to reduce the bit depth of the type
(guess which option is more effective─
veins?):
short:=SHORT(UMultBytes(short1,short2));
But if the result type has an average
bit depth (word), then, as you can see, senior
the result digit is not lost:
integer:=UMultBytes(short1,short2);
(InOberonan explicit indication is required
SHORT() to reduce the power of a numeric
type; This is done to make it easier to control─
lyrate possible distortion of the result in
case of casting larger types to smaller ones─
shim).
Bit calculations
Wirth tried to work with bits
more attractive appearance, consistent with
mathematical abstractions, so bits
machine words are represented as a set
integers ─ numbers of individual bits
(http://oberoncore.ru/library/wirth_sets).
For Oberon instead of universal sets
were selectedmany small integers
numbers. Type SET in Oberon can be considered─
as a bit set designed
so as to be independent of the order next─
transferring platform bytes (so-called
byte order: most significant byte ─ MSB, and
least significant byte ─ LSB). It's poetry─
therefore Oberon does not encourage violence
bringing integers to sets and vice versa,
because such a system type cast is ─ ope─
The radio is low enough to teach─
identify byte order and its use
may lead to unpredictable consequences
Wiyam on platforms with different sequence order─
bytes, although, of course, for the Z80 this is
uncritical.
Type sizeSET in Oberon is fixed
in accordance with modern processors
and is 4 bytes (in GPCP there is a type
LONGSET = 8 bytes), but in ZXDev we can
config Ofront.par specify arbitrary
size of sets, and I highly recommend─
I blow 1 byte, which is most efficient for
Z80 processor.
OperationMOD (remainder of integer
divisions) whencorresponding divisor bu─
child is optimized to match it
logical AND, for example, Oberon
a MOD 8 will be translated into sishnoea&7.
Equivalent to logical bitwise opera─
tions for integersa and b:
a AND b = ORD(BITS(a) * BITS(b))
a XOR b = ORD(BITS(a) / BITS(b))
a OR b = ORD(BITS(a) + BITS(b))
NOT a = ORD(-BITS(a))
Where ORD is the bitmap conversion
sets into a whole, andBITS ─ a whole into a many─
gesture. Oberon's ideology does not encourage ra─
bot with integers as with bits and vice versa, because
this, according to Wirth, leads to sloppiness
the use of types and eliminates the advantage─
strong typing properties, so functions
BITS() andORD(set) are not in the Oberon standard,
but is inComponent Pascal (and in XDev
too).
IF 0 IN set THEN(* if(set & 1) ... *)
(* if (set & 0x23) ... *)
IF BITS(23H) * set # {} THEN(* ... *)
(* if (set & 0x23) ... *)
IF {0, 1, 5} * set # {} THEN(* ... *)
The last option, it seems to me, is more
clearly shows what is in the set when checking─
The state of bits №№0,1 and 5 is indicated. Let you
not misleading by some pretentiousness─
the difficulty of recording bit operations, especially this
apparent “multiply” ─ the machine code is obtained
what you need:
; if ((0x23 & _set) != 0x0) {
ld a,(#_set + 0)
and a, #0x23
jr Z, ...
Thus, onOberon you can do
a fairly low-level program, for example─
mer, Spectrum emulator.
Constant arrays
To include resources and binary data─
directly into the codeOberon does not offer anything─
this is better than element-wise assignment. And I
very grateful to Oleg Komlev (Saferoll)
for his work in adding nonstan to ZXDev─
Dart language extension ─ constants─ny arrays. Let's give him the floor:
Saferoll:
What we managed to do using constant mass─
Sivam at the moment is May 2015.
1)Constant arrays of any nesting─
tee.
2)Element types - line of integer types
(includingBYTE),BOOLEANorCHAR.All of these
types in the C source become integer cons─
tantami.
3)If the array consists ofCHARorBYTE,
then elements can be specified either as re─
number of characters in brackets('f',20X,"7"),or─
Bo in the form of a string"ab"without extra brackets. But
the line necessarily implies at the end
symbol0Х,there should also be a place for it
in the array!
You can put fewer characters in quotes─
but then the characters after0Xcan be stored
filled with garbage - depends on the implementation of C-
compiler. Therefore, it is better to assume that it is notthe remainder of the array used by the string─
not with undefined characters.
An empty line "" can be specified for
any arrayARRAY N OF CHAR(orARRAY
N OF BYTE).
Examples:
TYPE
MsgStr = ARRAY 3, 7 OF CHAR;
CONST
Way = MsgStr("Hello","Error","Try");
TYPE
Labirint = ARRAY 3, 16 OF CHAR;
CONST
Map = Labirint(
"...o..##...oo12",
"...o..##...ooЗ5",
"...o..##...oo78"
);
Not done yet: exporting constants
arrays (we haven’t figured this out yet),
possibility to omitthe size of the array so that
Ofront automatically calculated it using co─
number of elements, and indication$to fix─
ary character array. I feel what's here
problems with the confusion of "sim" will appear again
ox or string." There is also room for development
implementation in terms of efficiency. But then
what has been done now is already very useful.
Sharing Oberon and C
You can insert pro─ into Oberon programs
arbitrary pieces of code written in the language
C (and built-in assembler).There are several─
about ways (http://zx.oberon2.ru/forum/
viewtopic.php?f=10&t=202), which I short─
I will list them.
1. Direct insertion of a C file
to the Oberon program
IMPORT SYSTEM;
PROCEDURE -includemain
'#include "Main.c"';
// --- Main.c ---
void main (void) {
Basic_Init();
Laser_InitScroll(65392);
Laser_InitSprites(Rsrc_SprStart, 4769);
...
Basic_Quit();
}
It would be most reasonable to insert it like this from─
useful functions, although it may turn out that
this method has much wider
possibilities. You just need to take into account that
The Oberon module must somehow know about this
C code to be able to interact with it─
howl.
As we know, Oberon strings are
null-terminated, but unlike si─
they have a maximum value attached to them─
no string length. If the procedures to work
with strings will always operate in
within this length ─ the code will always work─
That's correct. But is it possible to work for
Oberon with lines entirely in sish style
without additional field max. length? Co─
Surely, it’s possible. Here we will describe the calls of sishnyh
functions in a wrapper of Oberon procedures, and
they may not even match in parameters
(see for exampleIntToStr ):
TYPE
(* C-like null-terminated string: *)
CString = SYSTEM.PTR;
PROCEDURE -includestdlib
"#include ";
PROCEDURE -includestring
"#include ";
PROCEDURE -Length (
str: CString): INTEGER
"strlen((char*)str)";
PROCEDURE -CopyStr (dest, src: CString)
"strcpy((char*)dest, (char*)src)";
PROCEDURE -IntToStr (
n: INTEGER; str: CString)
"_itoa(n, (char*)str, 10)";
PROCEDURE -UIntToStr (
u: INTEGER; s: CString)
"_uitoa((unsigned int)u, (char*)s, 10)";
PROCEDURE -Concat (dest, src: CString)
"strcat((char*)dest, (char*)src)";
Пример использования:
IMPORT SYSTEM, B := Basic;
CONST
MaxIntSize = 7;(* ~-12345~ + 0X. *)
VAR
num: SHORTINT;
strBuf: ARRAY MaxIntSize OF CHAR;
BEGIN
num := B.RND(1, 4);
UIntToStr(num, SYSTEM.VAL(
CString, SYSTEM.ADR(strBuf))
);
Concat(SYSTEM.VAL(CString,
SYSTEM.ADR(strBuf)),
SYSTEM.VAL(CString,
SYSTEM.ADR(" is my number"))
);
2. Биндинг
Чтобы Оберон умел взаимодействовать с
кодом наСи ─ нужно как-то к нему прикре─
drink. It is necessary to make a description of the inter─
C library face in Oberon-mo style─
dulya so that other modules can call from
procedure, take the value of the constants and
etc. To do this we must prepare a bin─
ding-link in which the inter─
face of the alien module, all constants, types
and procedures (in the case of XDev ─ with empty ones─
lami). Please note that this is common practice for
bundles of modular languages with C-based libraries─
tekami (used not only inOberons,
but also in languagesAda, Modula-2, Modula-3).
XDev contains sufficient tools for
creating bindings that take into account the possibility
use different function calling models,
replacing some calls with others, description about─
types of functions, etc. With their help we create─
given bindings to WinAPI and libSDL for XDev/
WinDev. Let me give you an example of a simple binding:
referring to the forum for details
http://zx.oberon2.ru/forum/
viewtopic.php?f=10&t=94.
MODULE Input; IMPORT SYSTEM;
CONST
Backspace* = OCX;
Enter* = ODX;
Escape* = "E";
Space* = " ";
(* Arrows *)
Up * = "Q";
Down * = "A";
Right * = "P";
Left * = "O";
TYPE
Key* = CHAR;
(** Returns the number of keystrokes
in the keyboard input buffer. *)
PROCEDURE Available* (): SHORTINT;
BEGIN RETURN 0 END Available;
(** Read a key from the keyboard buffer.
Blocks if no key is available. *)
PROCEDURE Read* (): Key;
BEGIN RETURN 0X END Read;
PROCEDURE RunMe5OHz* ; END RunMe5OHz;
END Input.
Транслятор сгенерирует из этого биндин─
га сишный файл с пустыми телами функций, а
также более нужные нам 1) заголовочный
fileObj/Input.h, which will be connected
during compilation, and 2) symbol file Sym/
Input.sym, similar to the same ones, was generated─
nym for native Oberon modules. It contains food─
the encoded representation of the inter─
module face. Auto-generated empty
We ignore the current implementation, replacing
when compiled against the implementation written
pens (in C or ASMA), which is stored
in the folder/C and implements the one declared in the inter─
face functionality.
3. Binding using ready-made
nice standard header
and a homemade redirector header
I also allow this variation: if necessary─
Difficulties can be ignored and auto-generated─
Header specified by the Oberon translator(*.h).
A binding module is being created on Oberon (for
receiving a symbolic file), and with computer─
lation is connected by hand-made zago─
clever C file, from which1) is already in
in turn, the standard code is included
title not adapted to Oberonian
manipulating names with prefixes, etc.; and2) in
which describes Oberon-redirection
procedures into C functions or even macro─
sy. Binding to
library trdos.lib (see in the distribution kit
XDev ).
(about pairing with assembler, see the following
article)
Share your thoughts about the article