Oberon/ETH Oberon/faqlang
This document was originally hosted at the ETHZ. It remains under the ETH license and is in the WayBack archive.
Note: Questions not specifically related with the Oberon language or the compiler are dealt with in the FAQ on the ETH Oberon system.
General
Style
Types
- What is the size of the record?
- Why can't I assign a variable to another one of the same type?
- How does the compiler treat whole number constants?
Programming hints
- What is the longest identifier that can be used in Oberon sources?
- How can I handle 16-bit unsigned numbers in Oberon?
- How do I cast a variable?
- How do I cast REAL variables to INTEGER very fast? ( quasi-realtime)
- Is there a way to improve the speed of computation for a realtime application?
- How do I spread random numbers as uniformly as possible?
- Are there any bitwise operators available in Oberon?
- How do I write a constant value directly to an memory?
- Variable initialization can be at fault
- Is there a portable way to identify the caller of a procedure?
Compiler
- How do I locate an error in the source text?
- Why does the \z option cause the creation of a larger code?
- Is there a test set for Oberon compilers?
Built-in assembler
- Where do I find assembler documentation?
- What is the order of fields in a record?
- The compiler's behavior is unusual when casting anything to BOOLEAN.
- What is the correct syntax for operating on SYSTEM.BYTE?
- How to call a procedure from an assembler routine?
- How do I cast a large number of variables very fast?
- How to use in-line procedures
- What advantage do you see in using Oberon rather than other widespread programming languages?
A: The following was reported by Antonio Cisternino:
Reading the old book "Godel, Echer, Bach: an eternal golden braid", Hofstadter, 1980, I have found the following in chapter X:
"Programming in different languages is like composing pieces in different keys, particularly if you work at the keyboard. If you have learned or written pieces in many keys, each key will have its own special emotional aura. Also, certain kinds of figurations "lie in the hand" in one key but are awkward in another. So you are channeled by your choice of key. In some ways, even enharmonic keys, such as C-sharp and D-flat, are quite distinct in feeling. This shows how a notational system can play a significant role in shaping the final product."I think it's a nice way to say that multiple languages may help solving complex problems if each language is used to exploit its strength. It is related with CLR and the music context is appropriate to the name C#.
- What is the etymology of Reader, Scanner and Writer? The "er" suffix suggests they are procedures but in fact they are records.
A: Reader, Scanner and Writer are nouns, not verbs so, extrapolating from the naming guidelines in "Programming in Oberon" by Reiser and Wirth, they are appropriate for type names.
- What naming conventions can be recommended?
A: Extracted from "Object-Oriented Programming in Oberon-2" by Hanspeter Mössenböck:When declaring a pointer type X, the record pointed to should be called XDesc, as in this example.
- The following two definitions yield two apparently different sizes:
When I look at the SIZE of RecA, I see 28, when I look at the SIZE of RecB, I see 25 (!). Thus, if I form a packet to ship with UDP, and I depend on the structure set up using RecA to have a size of 25, I am in trouble. What is being done here? This will come up again as I take a data file built using, say, Delphi or M2, and attempt to read in the same file using Oberon.
A1: The size of a record is always padded at the end to a multiple of 4 bytes.A2: The most portable way to read files is byte by byte, converting the fields explicitly using *, DIV and MOD. Only if you need high performance with large files, should you use memory-mapped records.
The Files module provides procedures for reading little-endian ordered values (ReadInt, ReadLInt). - I cannot assign two variables that have the same type (err 113). Why?
A: Two variables are compatible only if they have the same type, not the same type structure. Take this example of two similar but different types:Just because they have the same structure, doesn't mean that they are compatible. Would you compare students and potatos? The problem is however less obvious when anonymous types are involved, declared on the fly. In this case types are always incompatible:
Although these two types look the same, they are incompatible because they have different declarations. To make a and b compatible you must declare the types either with:
or with:
In this common case of a procedure paramenter:
no type will ever have the same declaration as a, thus no variable will ever be compatible to a!
There is only one exception to this rule: open arrays. In this case, the base type of both variables must be compatible.
This allows to use anonymous open arrays in procedure parameters. - It appears that the compiler treats whole number constants as type INTEGER. I do not know how it treats a large whole value, say 40000. I suspect it treats it as a LONGINT. It does not appear to treat values like 125 as a byte.
A: An integer constant has the smallest integer type that will accomodate it. So -128, 0 and 127 would be SHORTINT, -32768, 128 and 32767 INTEGER, and -32769 and 32768 LONGINT. In low-level code, to be sure of the size, use SYSTEM.VAL to cast it. SYSTEM.VAL should be used in the 2nd argument of the SYSTEM procedures GET/PUT, PORTIN/PORTOUT, GETREG/PUTREG. Alternatively, you could also write:
- What is the longest identifier that can be used in Oberon sources? How many characters of the identifier are considered unique?
A: 32. 32. - How can I handle 16-bit unsigned numbers in Oberon?
A: Convert the number to a 32-bit LONGINT value. For example: The MOD is compiled efficiently as a logical AND operation, because the divisor is a constant power of 2. - How do I cast a variable? I am looking for an equivalent of p = (Process) c; in C.
A: Use a type guard - cfr. Chapter 11.2 in Programming in Oberon. Casting with SYSTEM.VAL is not a good idea, because it can break the garbage collector. - How do I cast REAL variables to INTEGER very fast?
A: Use the built-in assembler as explained. - Is there a way to improve the speed of computation for a realtime application?
A: A collection of CORDIC algorithms is under development. - How do I spread random numbers as uniformly as possible? I need to fill a 13*13*13 matrix with random numbers.
A: RandomNumbers.Uniform() is a first approximation. Unless you are just playing games, you should avoid it. Random numbers are full of tricks. If you like reading, have a look at the new edition of Knuth or the papers of George Marsaglia. For a general reference, see Wikipedia. Some common generators are found in a contribution. - Are there any bitwise operators available in Oberon?
A: Use the SET type and its operators. Here are the operators and their bitwise equivalents.You can use SYSTEM.VAL(SET, intexpr) to convert an expression to a set and SYSTEM.VAL(LONGINT, setexpr) to convert an expression from a set.
It is however preferable to declare straightaway SET variables which are then conveniently used in-line. That is better than declaring LONGINT variables and then to use SYSTEM.GET/PUT to convert them from or to SET type variables. SYSTEM.VAL should also be avoided. - How do I write a constant value directly to memory?
A:This is equivalent what is done, using MS Tools, with:
- Variable initialization can be at fault.
A: Take a look at this short procedure:Although the initialization of "i" is missing, this procedure works on PC Oberon. On Mac Oberon it fails most times because "i" has a random value, but not because of a compiler fault.
On PC Oberon the whole stack frame gets cleared at procedure entry. Thus missing initializations (to zero) do not attract attention. - Is there a portable way to identify the caller of a procedure?
A: Since ETH Oberon does not have full-featured metaprogramming facilities in all versions, there exists no portable way. However, the following works on Native Oberon and Windows Oberon.This gives you the calling module name and PC offset, which can be used with Compiler.Compile \f to find the calling location.
If you just want this for debugging, consider writing a HALT statement, which also gives a stack traceback. On Native Oberon you can use the hack HALT(MAX(INTEGER)) for a halt that produces a trap and then continues running.
Bluebottle: the following editing is required to adapt the text to Bluebottle:
replace Kernel by AosModules
replace Kernel.Module by AosModules.Module
replace Kernel.GetMod by AosModules.ThisModuleByAdr
- How do I locate an error in the source text?
A:- Select the error report line in the System.Log with MR+MR clicks
- Press F1 to mark (*) the source text viewer
- Activate the [Locate] Button in the System.Log menu bar
- Why does the \z option cause the creation of a larger code?
A: It is simpler to initialize the whole local-var block: this is done by a loop writing zero to the stack. Initializing only the pointers is more complex, because only the pointers must be (individually) initialized. A worst case example:Initialize all:
Initialize pointers only:
On the other hand, it is faster to initialize only the pointers. - Is there a test set for Oberon compilers?
A:The building of a test suite is progressing. Some tests are already available. Cfr. Project Hostess Homepage.
- Where do I find assembler documentation?
A: Start at PC Native Oberon - Compiler. Then, get some inspiration from the nicely written and informative documentation on the assembler used to teach a course in low-level programming by Jacques Eloff. - Given a type declaration:
If a procedure takes a VAR parameter, say r: RecDesc, the record fields are accessed in the following manner:
But, when a variable of the record type is declared locally inside a procedure, the fields are reversed:
Is there a specific reason for this?
A: The fields are not reversed! Offset -8 is smaller address than -4. You may look at it under another perspective: - The compiler's behavior is unusual when casting anything to BOOLEAN. For example, I will often input data from an I/O port, which is masked with a control word and the result is checked to see if TRUE or FALSE. On other compilers I am familiar with, if any bit is set in the low byte of the incoming word, when cast to type boolean, will result in TRUE. That is, any set bit yields a TRUE. I found that this does not seem to hold for the Oberon compiler. What I do now is, compare the byte to zero. This is not a problem, but I must watch for this.
A: This behavior is consistent with Pascal, where boolean was defined as enumerated type BOOLEAN = (FALSE, TRUE), i.e. ORD(FALSE) = 0 and ORD(TRUE) = 1. I will get a compiler error at the IF line, with a message indicative of invalid operands. It works fine if I either cast b to an integer or 0 to a byte.
To convert back to INTEGER, use ORD(b).
A: Only assignment is defined on SYSTEM.BYTE, not comparison. For 8-bit values, it is better to use a CHAR variable, so:- How to call a procedure from an assembler routine? The difficulty is that the assembler allows only the use of variables and not of procedures external to the routine.
A: Use a procedure variable as in this example: - How do I cast a large number of variables very fast? I have to process large picture data in quasi-realtime, converting REAL values to INTEGER. Using ENTIER for that purpose is too slow.
A: Use this model procedure. - How to use in-line procedures.
A: An in-line procedure is a procedure that is not called, but textually inserted into the caller procedure. A detailed documentation on how to use them is included in Jacques Eloff's assembler description.
22 Aug 2002 - Copyright © 2002 ETH Zürich. All rights reserved.
E-Mail: oberon-web at inf.ethz.ch
Homepage: http://www.ethoberon.ethz.ch/