Systems - Oberon for ZX Spectrum: Subtleties when developing on Oberon in the ZXDev environment (part 1).

Info Guide #11
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