Files
parent
64fe6180a8
commit
59290ba718
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
|
@ -0,0 +1,6 @@
|
||||||
|
These fonts were especially designed to make E sources look good :-),
|
||||||
|
E/11 (and ELSE/11, the proportional version) were designed for non-interlaced
|
||||||
|
resolutions on 1084-type (fuzzy) monitors.
|
||||||
|
end/10 and 11 (monospaced) and except/12 and 13 (proportional) were designed
|
||||||
|
for square-pixel resolutions (such as DBLPAL) on a multisync/VGA type
|
||||||
|
monitor. Sizes 10 and 12 are relatively thin.
|
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
|
@ -0,0 +1,4 @@
|
||||||
|
Here you'll find most of the executables that come with E. Documentation for
|
||||||
|
the functioning of most of these can be found in 'Docs/E.doc'. The Tools
|
||||||
|
'Explorer', 'Aprof' and 'EYacc' have their respective directories in the
|
||||||
|
Tools directory.
|
|
@ -0,0 +1,37 @@
|
||||||
|
/* Ecompile.rexx: run E compiler from ced.
|
||||||
|
thanks to Rick Younie for improvements. */
|
||||||
|
|
||||||
|
epath = 'e:bin/' /* homedir of EC */
|
||||||
|
|
||||||
|
OPTIONS RESULTS
|
||||||
|
ADDRESS COMMAND
|
||||||
|
|
||||||
|
IF ~EXISTS('ram:ec') THEN 'copy 'epath'ec ram:' /* for slow sys: devices */
|
||||||
|
|
||||||
|
ADDRESS 'rexx_ced'
|
||||||
|
|
||||||
|
'status 19' /* ask ced filename */
|
||||||
|
file = result
|
||||||
|
|
||||||
|
'status 18'
|
||||||
|
IF result ~= 0 THEN DO /* save if changed */
|
||||||
|
'save' file
|
||||||
|
SAY 'saving changes..'
|
||||||
|
END
|
||||||
|
ELSE SAY 'no changes..'
|
||||||
|
|
||||||
|
PARSE VAR file comparg '.e' /* strip the extension */
|
||||||
|
SAY 'invoking E compiler with file' comparg'.e'
|
||||||
|
|
||||||
|
ADDRESS
|
||||||
|
OPTIONS FAILAT 1000000
|
||||||
|
'ram:ec -E' comparg /* run compiler */
|
||||||
|
ebyte = rc
|
||||||
|
|
||||||
|
IF EXISTS(comparg) THEN comparg /* run exe */
|
||||||
|
|
||||||
|
ADDRESS
|
||||||
|
pull /* wait for a <cr> */
|
||||||
|
'cedtofront'
|
||||||
|
IF ebyte>0 THEN 'jump to byte' ebyte /* jump to spot of error */
|
||||||
|
exit 0
|
|
@ -0,0 +1,101 @@
|
||||||
|
/* $VER: CompileE.ged v0.5 (8.8.94)
|
||||||
|
by Leon Woestenberg (leon@stack.urc.tue.nl)
|
||||||
|
|
||||||
|
This is an ARexx script for GoldED (© Dietmar Eilert), and enables
|
||||||
|
you to compile E programs from the editor. Just add a menu item,
|
||||||
|
and in the 'Command' window select this ARexx script. Also, set
|
||||||
|
the output to 'NIL:'. You might as well add keybindings, window
|
||||||
|
gadgets, a shortcut key or even API to call this script.
|
||||||
|
|
||||||
|
Now just edit your source in the active window, and select the
|
||||||
|
menu item (or key, gadget etc.) you added. The compiler will be
|
||||||
|
invoked, and feedback will be showed in the status line of the
|
||||||
|
active window. If necessary, unsaved changes will be saved first.
|
||||||
|
After compilation, the status line will show the result. If there
|
||||||
|
was an error, the cursor will jump exactly to the spot of error,
|
||||||
|
and the error message of the compiler will be shown in the status
|
||||||
|
line. Warnings or unreferences (not both) will also be shown but
|
||||||
|
will not jump, nor beep, as this would get annoying very soon.
|
||||||
|
Also, cursor slot (bookmark) 9 will contain the spot of trouble.
|
||||||
|
|
||||||
|
*/
|
||||||
|
|
||||||
|
/* adjust your EC filename here, especially after registering :) */
|
||||||
|
ec='ECDEMO'
|
||||||
|
|
||||||
|
OPTIONS RESULTS
|
||||||
|
|
||||||
|
IF (LEFT(ADDRESS(),6)~="GOLDED") THEN ADDRESS 'GOLDED.1'
|
||||||
|
'LOCK CURRENT QUIET'
|
||||||
|
IF rc THEN EXIT
|
||||||
|
|
||||||
|
options='ERRBYTE' 'QUIET'
|
||||||
|
|
||||||
|
'QUERY ANYTEXT'
|
||||||
|
IF (result='TRUE') THEN
|
||||||
|
DO
|
||||||
|
'QUERY DOC VAR FILEPATH'
|
||||||
|
IF (UPPER(RIGHT(result,2))='.E') THEN
|
||||||
|
DO
|
||||||
|
'QUERY MODIFY'
|
||||||
|
IF (result='TRUE') THEN
|
||||||
|
DO
|
||||||
|
'REQUEST STATUS="Saving changes..."'
|
||||||
|
'SAVE ALL'
|
||||||
|
END
|
||||||
|
'REQUEST STATUS="Compiling source..."'
|
||||||
|
ADDRESS COMMAND ec filepath options '>T:EC_Report'
|
||||||
|
errorbyte=rc
|
||||||
|
IF errorbyte>0 THEN
|
||||||
|
DO
|
||||||
|
'FOLD OPEN=TRUE ALL'
|
||||||
|
'GOTO UNFOLD=TRUE BYTE=' || errorbyte
|
||||||
|
'PING 9'
|
||||||
|
END
|
||||||
|
IF OPEN(filehandle,'T:EC_Report','Read')~=0 THEN
|
||||||
|
DO
|
||||||
|
importance=0
|
||||||
|
DO WHILE ~EOF(filehandle) & importance~='ERROR'
|
||||||
|
lastline=READLN(filehandle)
|
||||||
|
/* messages ordered in accending importance */
|
||||||
|
IF (INDEX(lastline,'UNREFERENCED:')~=0) & (importance<1) THEN
|
||||||
|
DO
|
||||||
|
importance=1
|
||||||
|
message=lastline
|
||||||
|
END
|
||||||
|
IF (INDEX(lastline,'WARNING:')~=0) & (importance<2) THEN
|
||||||
|
DO
|
||||||
|
importance=2
|
||||||
|
message=lastline
|
||||||
|
END
|
||||||
|
IF (INDEX(lastline,'ERROR:')~=0) & (importance<3) THEN
|
||||||
|
DO
|
||||||
|
importance=3
|
||||||
|
message=lastline
|
||||||
|
END
|
||||||
|
IF (INDEX(lastline,'EC INTERNAL ERROR')~=0) & (importance<4) THEN
|
||||||
|
DO
|
||||||
|
importance=4
|
||||||
|
message=lastline
|
||||||
|
END
|
||||||
|
END
|
||||||
|
ok=CLOSE(filehandle)
|
||||||
|
IF importance=0 THEN message='Compilation succesful.'
|
||||||
|
IF importance>=3 THEN 'BEEP'
|
||||||
|
message=TRANSLATE(message,'''','"')
|
||||||
|
'FIX VAR message'
|
||||||
|
'REQUEST STATUS="' || message ||'"'
|
||||||
|
END
|
||||||
|
END
|
||||||
|
ELSE
|
||||||
|
DO
|
||||||
|
'REQUEST STATUS="Source has no .e extension!"'
|
||||||
|
END
|
||||||
|
END
|
||||||
|
ELSE
|
||||||
|
DO
|
||||||
|
'REQUEST STATUS="Say what?! Try typing some e source first :)"'
|
||||||
|
END
|
||||||
|
'UNLOCK'
|
||||||
|
EXIT
|
||||||
|
|
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
|
@ -0,0 +1,867 @@
|
||||||
|
@DATABASE "AProf.guide"
|
||||||
|
|
||||||
|
@remark Profiler Dokumentation für Version 3.3
|
||||||
|
@remark
|
||||||
|
@remark $RCSfile: AProf.guide $
|
||||||
|
@remark
|
||||||
|
@remark $Revision: 1.7 $ $Date: 1994/10/04 11:28:35 $
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
@remark ******************
|
||||||
|
@remark * Main
|
||||||
|
@remark ******************
|
||||||
|
@NODE Main "AProf.guide"
|
||||||
|
|
||||||
|
Documentation for Amiga Profiler V3.30
|
||||||
|
© 1993,94 Michael G. Binz
|
||||||
|
|
||||||
|
@{" What is a profiler? " link l-what}
|
||||||
|
|
||||||
|
@{" System requirements " link l-sysrequ }
|
||||||
|
@{" Installation procedure " link l-install }
|
||||||
|
|
||||||
|
@{" Starting the profiler " link l-start }
|
||||||
|
@{" User interface " link l-ui }
|
||||||
|
@{" Configurability " link l-config }
|
||||||
|
|
||||||
|
@{" Notes " link l-notes }
|
||||||
|
@{" Tested Systems " link l-systeme }
|
||||||
|
@{" Caveats " link l-caveats }
|
||||||
|
@{" StripB " link l-stripb }
|
||||||
|
|
||||||
|
@{" Feedback " link l-feedback }
|
||||||
|
@{" AProf needs you..." link l-enforcer-blah }
|
||||||
|
@{" Copyright " link l-copyright}
|
||||||
|
|
||||||
|
@{" Changes " link l-changes }
|
||||||
|
@{" Q+A " link frageantwort }
|
||||||
|
@ENDNODE
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
@remark ******************
|
||||||
|
@remark * Was isses???
|
||||||
|
@remark ******************
|
||||||
|
@NODE l-what "AProf:What is a profiler"
|
||||||
|
|
||||||
|
A profiler is a development tool supporting the developer in code
|
||||||
|
optimization. A specified program is executed by the profiler
|
||||||
|
which collects several informations (ie. call counts, time values)
|
||||||
|
while execution. This data can be used to locate the so-called
|
||||||
|
hot spots in your code - functions where most of the execution time
|
||||||
|
is spent and optimization will gain most effect.
|
||||||
|
|
||||||
|
The following articles supply further information on profilers:
|
||||||
|
|
||||||
|
o Dr. Dobb's Journal, January 1993
|
||||||
|
Joseph Newcomer, 'Profiling for Performance', pp. 80
|
||||||
|
|
||||||
|
o Dr. Dobb's Journal, November 1993
|
||||||
|
Michael R. Dunlavey, 'Performance Tuning: Slugging it out', pp. 18
|
||||||
|
@ENDNODE
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
@remark ******************
|
||||||
|
@remark * Installation & Systemanforderungen
|
||||||
|
@remark ******************
|
||||||
|
@NODE l-sysrequ "AProf: System requirements"
|
||||||
|
|
||||||
|
o Hardware requirements
|
||||||
|
|
||||||
|
The profiler needs at least Workbench/Kickstart 2.04 and a minimum
|
||||||
|
of 500K available memory (depends on profilees sizes).
|
||||||
|
|
||||||
|
It has been tested successfully with Amiga 500, 2000, 3000, 4000
|
||||||
|
and OS versions 2.04, 2.1, 3.0.
|
||||||
|
|
||||||
|
|
||||||
|
o Software requirements
|
||||||
|
|
||||||
|
Your compiler must be able to create Amiga symbol hunks. Code
|
||||||
|
symbols included in this hunks should only address the starting
|
||||||
|
addresses of functions in your code. No intermediate jump
|
||||||
|
labels must be defined. (See @{ "Clean symbol tables" link frageantwort}, @{ "Tested compilers" link l-systeme })
|
||||||
|
@ENDNODE
|
||||||
|
|
||||||
|
@NODE l-install "AProf:Installation procedure"
|
||||||
|
|
||||||
|
Copy all files included in AProf3_30.lha into a directory and add
|
||||||
|
this directory to your PATH.
|
||||||
|
|
||||||
|
Do not delete AProf's icon (AProf.info), since configuration data
|
||||||
|
is located in this file (See @{"Configurability" link l-config}).
|
||||||
|
@ENDNODE
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
@remark ******************
|
||||||
|
@remark * Start & Oberfläche
|
||||||
|
@remark ******************
|
||||||
|
@NODE l-start "AProf: Starting the profiler"
|
||||||
|
|
||||||
|
From CLI the profiler is started with
|
||||||
|
|
||||||
|
1.> AProf [application]
|
||||||
|
|
||||||
|
The profiler starts up, displays its user interface, loads the
|
||||||
|
symbol tables from the file 'application' if specified and waits
|
||||||
|
for your actions.
|
||||||
|
|
||||||
|
From Workbench, doubleclick the AProf icon.
|
||||||
|
|
||||||
|
If no filename was specified or if started from workbench, you can
|
||||||
|
load a file from the menu @{ "[Files/Open]" link m-files }.
|
||||||
|
|
||||||
|
See also: @{ "User interface" link l-ui }
|
||||||
|
@ENDNODE
|
||||||
|
|
||||||
|
@NODE l-ui "AProf:User interface"
|
||||||
|
|
||||||
|
Title: Name of loaded profilee
|
||||||
|
|
||||||
|
Menus: @{ " Files " link m-files } @{ " Action " link m-action } @{ " Data " link m-data } @{ " Move " link m-move } @{ " Misc " link m-misc }
|
||||||
|
Displays: @{ " Mode " link gad-inex } @{ " Time unit " link gad-percmil } @{ " Sort " link gad-sort } @{ " Time " link gad-xtime }
|
||||||
|
Buttons: [ Shortcuts for often needed menu entrys ]
|
||||||
|
|
||||||
|
Description of table entrys:
|
||||||
|
|
||||||
|
Function HitCnt Per Call Over Min/Max
|
||||||
|
| | | | |
|
||||||
|
Symbol names* | | | |
|
||||||
|
| | | |
|
||||||
|
Call count** | | |
|
||||||
|
| | |
|
||||||
|
Average execution time per call | |
|
||||||
|
| |
|
||||||
|
Overall execution time for that function |
|
||||||
|
|
|
||||||
|
Minimum/maximum execution time per call
|
||||||
|
|
||||||
|
* The symbol *ENTRY*, if displayed, is generated by AProf if the symbol
|
||||||
|
table of the profilee contains no symbol sitting at the very first
|
||||||
|
executed address of its seglist.
|
||||||
|
|
||||||
|
** A call count value of -1 is shown for symbols being no function entrys.
|
||||||
|
Safe profiling must be active.
|
||||||
|
|
||||||
|
[ Status and error messages are displayed in the bottom window border ]
|
||||||
|
@ENDNODE
|
||||||
|
|
||||||
|
@NODE m-files "AProf:Menu: Files"
|
||||||
|
|
||||||
|
Menu: Files
|
||||||
|
Reading and writing profiler data.
|
||||||
|
|
||||||
|
|
||||||
|
Open: Read a new file (also accessable via button)
|
||||||
|
Save: Save report to file with same basename and suffix .pro
|
||||||
|
Save as: Save report to selectable file
|
||||||
|
Reset: Reset all timing values
|
||||||
|
Print: Print report
|
||||||
|
Exit: Leave the program
|
||||||
|
@ENDNODE
|
||||||
|
|
||||||
|
@NODE m-action "AProf:Menu: Action"
|
||||||
|
|
||||||
|
Menu: Action
|
||||||
|
Start the profiling process.
|
||||||
|
|
||||||
|
|
||||||
|
Start: Starts the profile run. If you restart a profilee, then times
|
||||||
|
are added.
|
||||||
|
If you want to run a profilee several times without adding the
|
||||||
|
times and hit counts, you must reset the timer and hitcounts
|
||||||
|
with @{ " Files/Reset " link m-files }.
|
||||||
|
(also accessable via button row)
|
||||||
|
@ENDNODE
|
||||||
|
|
||||||
|
@NODE m-data "AProf:Menu: Data"
|
||||||
|
|
||||||
|
Menu: Data
|
||||||
|
Runtime configuration and execution control.
|
||||||
|
|
||||||
|
|
||||||
|
Exec details:
|
||||||
|
Provide a command line and stack size for the profilee
|
||||||
|
See: @{ "Exec dialog" link dialog-exec }
|
||||||
|
|
||||||
|
Preferences:
|
||||||
|
Configure the profiler.
|
||||||
|
See: @{ "Preferences dialog" link dialog-preferences }, @{ "Configurability" link l-config }.
|
||||||
|
@ENDNODE
|
||||||
|
|
||||||
|
@NODE m-move "AProf:Menu: Move"
|
||||||
|
|
||||||
|
Menu: Move
|
||||||
|
Moving in the display area.
|
||||||
|
|
||||||
|
|
||||||
|
Find: Enter a search string and start search
|
||||||
|
Find next: Search for next occurence
|
||||||
|
Top: Go to top of symbol table
|
||||||
|
Bottom: Go to bottom of symbol table
|
||||||
|
Page up/down: One page up/down
|
||||||
|
@ENDNODE
|
||||||
|
|
||||||
|
@NODE m-misc "AProf:Menu: Misc"
|
||||||
|
|
||||||
|
Menu: Misc
|
||||||
|
Other functions
|
||||||
|
|
||||||
|
|
||||||
|
Help:
|
||||||
|
Start the AmigaGuide(TM) hypertext help system (if available)
|
||||||
|
|
||||||
|
Refresh Window:
|
||||||
|
Redisplay symbol table
|
||||||
|
|
||||||
|
About:
|
||||||
|
Display version and copyright information
|
||||||
|
@ENDNODE
|
||||||
|
|
||||||
|
@NODE gad-inex "AProf: Profiler mode: Separate/Combined"
|
||||||
|
|
||||||
|
Separate/Combined: (Mode)
|
||||||
|
Displays how function timing values are accumulated. If combined
|
||||||
|
is selected, the time value for a function includes the times of
|
||||||
|
all functions that are called by this function. Separate excludes
|
||||||
|
the times of called functions.
|
||||||
|
|
||||||
|
Note that if your code includes recursive procedure you must use
|
||||||
|
separate mode. If combined is used, the time for the recursive
|
||||||
|
procedure is wrong.
|
||||||
|
|
||||||
|
Example:
|
||||||
|
|
||||||
|
int main( void )
|
||||||
|
{
|
||||||
|
foo( 2 ); /* foo needs 2secs execution time */
|
||||||
|
|
||||||
|
bar( 3 ); /* bar needs 3secs execution time */
|
||||||
|
|
||||||
|
...burn 1sec proc. time in main()...
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
If 'Separate' is selected, the profile list will look like this:
|
||||||
|
Symbol HitCnt Per Call
|
||||||
|
main 1 1000 The timing values are updated for
|
||||||
|
foo 1 2000 each funktion separate.
|
||||||
|
bar 1 3000
|
||||||
|
|
||||||
|
Now if 'Combined' is selected:
|
||||||
|
Symbol HitCnt Per Call
|
||||||
|
main 1 6000 The timing values for foo() and
|
||||||
|
foo 1 2000 bar() are added to main(), since
|
||||||
|
bar 1 3000 main() calls both.
|
||||||
|
@ENDNODE
|
||||||
|
|
||||||
|
@NODE gad-percmil "AProf: Cycle: Percentual/Millisecond timevalues"
|
||||||
|
|
||||||
|
Millisecond/Percentual time units: (Units)
|
||||||
|
Displays the units used for for timing values. If percentual is
|
||||||
|
active, times shown are fractions of overall execution time in
|
||||||
|
percent.
|
||||||
|
@ENDNODE
|
||||||
|
|
||||||
|
@NODE gad-xtime "AProf: XTime"
|
||||||
|
|
||||||
|
Overall execution time of profilee. Units are seconds or milli-
|
||||||
|
seconds depending on execution time. Times longer than 1000 ms are
|
||||||
|
displayed in seconds (s), below in milliseconds (ms).
|
||||||
|
@ENDNODE
|
||||||
|
|
||||||
|
@NODE l-config "AProf: Configurability"
|
||||||
|
|
||||||
|
All configurable items of AProf must be set as ToolTypes in AProf's
|
||||||
|
.info file, which must reside in the same directory as the profiler.
|
||||||
|
|
||||||
|
+------------------------------------------------------------------+
|
||||||
|
| Never delete AProf.info! All configuration data will be |
|
||||||
|
| written to this file! |
|
||||||
|
+------------------------------------------------------------------+
|
||||||
|
|
||||||
|
Beginning with version 3.30 one can save AProf's current settings
|
||||||
|
with the 'Save' button in 'Preferences'. It is recommended to use
|
||||||
|
this instead of setting the ToolTypes manually.
|
||||||
|
|
||||||
|
|
||||||
|
o WINDIM=left/top/width/height
|
||||||
|
Use this to set size and position for the profiler window.
|
||||||
|
|
||||||
|
o TUNITS=(PERCENTUAL | MILLISECOND)
|
||||||
|
Default time units.
|
||||||
|
|
||||||
|
o PMODE=(COMBINED|SEPARATE)
|
||||||
|
Profiling mode
|
||||||
|
|
||||||
|
o PATTERN=(AmigaPattern)
|
||||||
|
Amiga pattern for symbols to hide.
|
||||||
|
Look in your Amiga User Manual for a description of pattern syntax
|
||||||
|
|
||||||
|
o SORT=(NONE | NAME | HIT | AVERAGE | OVER)
|
||||||
|
Specify the sort order you prefer.
|
||||||
|
|
||||||
|
o SAFE=(TRUE | FALSE)
|
||||||
|
Safe profiling on or off
|
||||||
|
@ENDNODE
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
@REMARK ****************
|
||||||
|
@REMARK * Allgemein Blah...
|
||||||
|
@REMARK ****************
|
||||||
|
@NODE l-notes "AProf: Notes"
|
||||||
|
|
||||||
|
Implementation
|
||||||
|
--------------
|
||||||
|
AProf is an active profiler. This means, hit counts and timing values
|
||||||
|
are measured by employing a sophisticated breakpoint scheme.
|
||||||
|
|
||||||
|
Before a profilee is executed by AProf, all function entry points
|
||||||
|
are marked with breakpoints.
|
||||||
|
|
||||||
|
After execution is started, AProf receives the breakpoint hits and
|
||||||
|
places additional breakpoints at the function return points.
|
||||||
|
|
||||||
|
The address for function return points is derived from the stack. This
|
||||||
|
should make it clear why AProf needs a 'clean' symbol table: There is
|
||||||
|
no foolproof way to figure out the function return address if there
|
||||||
|
are any function local data on top of stack.
|
||||||
|
|
||||||
|
Actually some testing can be undergone to ensure the correct values
|
||||||
|
are read from the stack. This can be activated in AProf versions
|
||||||
|
> 3.22 by the checkmark 'safe profiling' in AProf preferences.
|
||||||
|
Although this is not foolproof as stated, it works unexpected good
|
||||||
|
with most compilers. The disadvatage of this technique is the
|
||||||
|
additional time spent in the checking routines.
|
||||||
|
|
||||||
|
|
||||||
|
Profilee environment
|
||||||
|
--------------------
|
||||||
|
Wether AProf is started from CLI or workbench, the profilee's
|
||||||
|
environment is always a shell. If started from wb, a shell window will
|
||||||
|
be supplied for profilee IO.
|
||||||
|
|
||||||
|
ARexx port
|
||||||
|
----------
|
||||||
|
In the current version AProf provides no additional functions for
|
||||||
|
ARexx hosts.
|
||||||
|
@ENDNODE
|
||||||
|
|
||||||
|
@NODE l-caveats "AProf: Caveats"
|
||||||
|
|
||||||
|
Here is a list of known constructs which can result in problems if
|
||||||
|
you try to profile programs including them
|
||||||
|
|
||||||
|
o Non-standard startup code
|
||||||
|
-------------------------
|
||||||
|
The profilees must be able to run in the same process environment
|
||||||
|
as the profiler. It's not possible to use special startup codes for
|
||||||
|
detaching a program or making it resident.
|
||||||
|
|
||||||
|
|
||||||
|
o recursive procedures
|
||||||
|
--------------------
|
||||||
|
Use only '@{ "separate" link gad-inex }' profiling mode if your code includes recursive
|
||||||
|
procedures. In combined mode the time for the recursive procedure
|
||||||
|
is wrong.
|
||||||
|
|
||||||
|
|
||||||
|
o setjmp()/longjmp()
|
||||||
|
------------------
|
||||||
|
If setjmp()/longjmp() combinations are used in the profilee, the
|
||||||
|
time span between calling longjmp() and the next RTS instruction
|
||||||
|
will add to the function calling longjmp().
|
||||||
|
|
||||||
|
|
||||||
|
o signal()/raise()
|
||||||
|
----------------
|
||||||
|
Most compiler libraries contain signal()/raise() functions which
|
||||||
|
are not compatible with AProf.
|
||||||
|
|
||||||
|
|
||||||
|
o CIA Timer
|
||||||
|
---------
|
||||||
|
CIA timers are not available for profilees.
|
||||||
|
|
||||||
|
|
||||||
|
o Overlays
|
||||||
|
--------
|
||||||
|
Profiling of overlayed programs is not possible.
|
||||||
|
|
||||||
|
|
||||||
|
o Runtime limitation
|
||||||
|
------------------
|
||||||
|
Profiler timers can measure maximum time spans of about 99 mins.
|
||||||
|
|
||||||
|
|
||||||
|
o Static functions
|
||||||
|
----------------
|
||||||
|
Functions to be measured must be in the symbol table. This is not
|
||||||
|
the case for static (module local) functions in most programming
|
||||||
|
environments.
|
||||||
|
|
||||||
|
|
||||||
|
o Traphandlers
|
||||||
|
------------
|
||||||
|
If your program uses a private trap handler, traps not handled must
|
||||||
|
be propagated to the previous handler. Used traps must be allocated
|
||||||
|
with AllocTrap().
|
||||||
|
Profiling is NOT possible if you are using trace traps (#9).
|
||||||
|
|
||||||
|
|
||||||
|
o Switch- and Launchfunctions
|
||||||
|
---------------------------
|
||||||
|
Profilees using members tc_Switch and tc_Launch in Exec's Task
|
||||||
|
structure must propagate execution to previous defined handlers.
|
||||||
|
@ENDNODE
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
@NODE l-stripb "AProf: StripB"
|
||||||
|
|
||||||
|
StripB is a command line utility which can be used to remove all
|
||||||
|
HUNK_SYMBOL- and HUNK_DEBUG-hunks from an Amiga executable.
|
||||||
|
|
||||||
|
Command line: StripB infile outfile
|
||||||
|
@ENDNODE
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
@NODE l-systeme "AProf: Tested systems"
|
||||||
|
|
||||||
|
This is a list of systems I have tested the profiler with. If you
|
||||||
|
find an error or if you have tested a system not included here,
|
||||||
|
send a message please.
|
||||||
|
|
||||||
|
@{ " Amiga E " link amiga-e }
|
||||||
|
@{ " Aztec C " link manx-c }
|
||||||
|
@{ " DICE C " link dice-c }
|
||||||
|
@{ " GNU C/C++ " link gnu-cpp}
|
||||||
|
@{ " SAS C 6.3 " link sas-c }
|
||||||
|
@{ " Maxon C++ V1.2.1 " link maxon-cpp }
|
||||||
|
@{ " Assemblers " link assemblers }
|
||||||
|
|
||||||
|
@{ " Other systems... " link other }
|
||||||
|
@ENDNODE
|
||||||
|
|
||||||
|
|
||||||
|
@NODE amiga-e "AProf: Tested systems: Amiga E"
|
||||||
|
|
||||||
|
AProf works with all versions of Amiga E >2.1b.
|
||||||
|
|
||||||
|
Problems:
|
||||||
|
Profiling of programs using exeptions leads to meaningless results
|
||||||
|
|
||||||
|
Creation of symbol hunks:
|
||||||
|
Use -s switch
|
||||||
|
@ENDNODE
|
||||||
|
|
||||||
|
@NODE manx-c "AProf: Tested systems: Aztec C"
|
||||||
|
|
||||||
|
Manx Aztec C V3.4 - V5.2b
|
||||||
|
|
||||||
|
Creation of symbol hunks:
|
||||||
|
Use option -w for the linker
|
||||||
|
|
||||||
|
Problems:
|
||||||
|
|
||||||
|
o Remove symbols named '_H#[0-9]_org'
|
||||||
|
|
||||||
|
o Don't use detach.o with programs you want to profile.
|
||||||
|
|
||||||
|
o ANSI C functions signal() and raise() don't handle traps
|
||||||
|
the way they should. (see @{"Caveats" link l-caveats}, custom trap-handlers)
|
||||||
|
@ENDNODE
|
||||||
|
|
||||||
|
@NODE sas-c "AProf: Tested systems: SAS C"
|
||||||
|
|
||||||
|
SAS C ?
|
||||||
|
|
||||||
|
@ENDNODE
|
||||||
|
|
||||||
|
@NODE maxon-cpp "AProf: Tested systems: Maxon C++"
|
||||||
|
|
||||||
|
Maxon C++ V1.2.1
|
||||||
|
|
||||||
|
Problems:
|
||||||
|
Code and data symbols in link libraries reside in the same hunk. According
|
||||||
|
to Jens Gelhar this will change in future versions of the library. The
|
||||||
|
beta version of the updated compiler writes symbol hunks as needed by Aprof
|
||||||
|
so the library problem requires a simple recompile.
|
||||||
|
|
||||||
|
Compiler is adding labels named L'num' to symbol table. This must be
|
||||||
|
removed (Pattern: L#[0-9]) or you must activate 'save profiling'.
|
||||||
|
|
||||||
|
Creation of symbol hunks:
|
||||||
|
For command line compiler use option -bs.
|
||||||
|
In the integrated environment use menu 'Compiler options'.
|
||||||
|
|
||||||
|
Other:
|
||||||
|
Test the procedure rexx/maxoncpp.aprof for unmangling of c++ symbol names.
|
||||||
|
@ENDNODE
|
||||||
|
|
||||||
|
|
||||||
|
@NODE dice-c "AProf: Tested systems: DICE C"
|
||||||
|
|
||||||
|
DICE C V2.06.21 unregistered version
|
||||||
|
|
||||||
|
Problems:
|
||||||
|
Dice generates dirty symbol tables, activate 'Safe profiling'.
|
||||||
|
|
||||||
|
Creation of symbol hunks:
|
||||||
|
Use option -s for DCC.
|
||||||
|
|
||||||
|
Information about the commercial DICE system welcome.
|
||||||
|
@ENDNODE
|
||||||
|
|
||||||
|
@NODE gnu-cpp "AProf: Tested systems: GNU C/C++"
|
||||||
|
|
||||||
|
GNU gcc - 2.3.3
|
||||||
|
|
||||||
|
Problems:
|
||||||
|
Profiling programs using ixemul.library is not possible, use static linking.
|
||||||
|
Activate 'Safe profiling'.
|
||||||
|
|
||||||
|
Creation of symbol hunks:
|
||||||
|
Always created, to not create them use -s or @{ " StripB " link l-stripb }, which is
|
||||||
|
included in this AProf distribution.
|
||||||
|
@ENDNODE
|
||||||
|
|
||||||
|
@NODE assemblers "AProf: Tested systems: Assemblers"
|
||||||
|
|
||||||
|
AProf is tested with a number of assemblers (Aztec as, asm68, DevPack).
|
||||||
|
There seem to be no problems as long as the following rules are obeyed:
|
||||||
|
|
||||||
|
o If you export code symbols other than function labels, activate
|
||||||
|
'Safe profiling' in [Data/Prefs].
|
||||||
|
|
||||||
|
o Don't be too creative with your control flow, eg. several starting
|
||||||
|
points for functions etc. Although this will not crash AProf, the
|
||||||
|
results provided will be a bit random :)
|
||||||
|
|
||||||
|
o Do not mix data and code in one hunk.
|
||||||
|
|
||||||
|
o Do not use self modifying code.
|
||||||
|
@ENDNODE
|
||||||
|
|
||||||
|
@NODE other "AProf: Tested systems: Other..."
|
||||||
|
|
||||||
|
Here is a little strategy to test if your system is able to cooperate
|
||||||
|
with AProf.
|
||||||
|
|
||||||
|
Step 0.
|
||||||
|
-------
|
||||||
|
First, try to find out if your system can generate executables with
|
||||||
|
Amiga symbol hunks. You should find this information in your compiler
|
||||||
|
documentation.
|
||||||
|
|
||||||
|
Then after successfully creating a executable version of the program
|
||||||
|
to test, first check if your program is at least stable enough to run
|
||||||
|
stand-alone without crashing the machine.
|
||||||
|
|
||||||
|
Close all applications so that if a crash happens no data is lost!
|
||||||
|
|
||||||
|
Step 1.
|
||||||
|
-------
|
||||||
|
Start AProf and load your program. Then specify in [Data/Prefs] the
|
||||||
|
symbol pattern '#?' (without quotation marks). This removes the complete
|
||||||
|
symbol table, which results in a reduced protocol between AProf and
|
||||||
|
your program.
|
||||||
|
|
||||||
|
When this is done, close your eyes and start your program (if you
|
||||||
|
have problems in finding the right keys you can keep your eyes open).
|
||||||
|
If your machine crashed, then Aprof is definitely incompatible with
|
||||||
|
this programming system. The only thing to do is to reboot your machine
|
||||||
|
an delete AProf. If there was no problems and your program is running,
|
||||||
|
take all steps needed to stop your program.
|
||||||
|
|
||||||
|
Step 2.
|
||||||
|
-------
|
||||||
|
Next step is to remove all symbols but one, which should be generated by
|
||||||
|
you. If your programm contains a function 'foo' written by you then
|
||||||
|
open the prefs request and insert ~(foo) as symbol pattern. After selecting
|
||||||
|
'use', this should be the only symbol displayed (it is possible, that
|
||||||
|
there is an second symbol called *ENTRY* - this is used by AProf, ignore
|
||||||
|
it).
|
||||||
|
|
||||||
|
Start execution. If everything works, then again stop your program or
|
||||||
|
wait for termination. Now Aprof should display the function timings.
|
||||||
|
|
||||||
|
A crash is a sign for incompatibility. Forget AProf.
|
||||||
|
|
||||||
|
Step 3.
|
||||||
|
-------
|
||||||
|
Now remove the pattern in [Data/Prefs] and activate 'Save profiling'.
|
||||||
|
Then take a look at the symbol list. Remove temporary labels or
|
||||||
|
line number labels.
|
||||||
|
|
||||||
|
Examples:
|
||||||
|
_L0001, _L0002, _L0003, _L0004, ...
|
||||||
|
_H0_org, _H0_end, _H1_org, _H1_end, ...
|
||||||
|
@0001, @0002, @0003, ...
|
||||||
|
|
||||||
|
Then again start execution of your program. If everything works as it
|
||||||
|
should you did it. Exit your program and AProf will generate the
|
||||||
|
timing report. AProf seems to be compatible with your programming
|
||||||
|
system. Save the pattern created in the preceding step and use it
|
||||||
|
in future profiling sessions.
|
||||||
|
|
||||||
|
If your program crashed, the hard work begins. As we saw in Step 2,
|
||||||
|
your programming system basically should work with AProf. So there
|
||||||
|
must be one or several data objects in your code hunks. You will
|
||||||
|
have to find and remove them.
|
||||||
|
|
||||||
|
Step 4.
|
||||||
|
-------
|
||||||
|
Save all settings.
|
||||||
|
@ENDNODE
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
@NODE l-feedback "AProf: Feedback"
|
||||||
|
|
||||||
|
Contact me under
|
||||||
|
|
||||||
|
EMail: michab@informatik.fh-augsburg.de
|
||||||
|
|
||||||
|
If it's not possible to use EMail, write to:
|
||||||
|
|
||||||
|
Michael Binz
|
||||||
|
Bahnhofstr. 11
|
||||||
|
D-86459 Gessertshausen
|
||||||
|
|
||||||
|
If you made tests with a compiler not listed in 'systems', please
|
||||||
|
send me a small demo source file, the translated executable (with
|
||||||
|
symbol hunk) and some information about creating symbol hunks with
|
||||||
|
your compiler. If needed, send a description of problems and actions
|
||||||
|
you performed to solve them, too.
|
||||||
|
@ENDNODE
|
||||||
|
|
||||||
|
@NODE l-enforcer-blah "AProf needs you..."
|
||||||
|
|
||||||
|
... if you have access to an Amiga with MMU!
|
||||||
|
|
||||||
|
Since I'm developing AProf on an Amiga 2000 with 68000 I can't check
|
||||||
|
for ENFORCER HITS! Although much care was taken to keep this nasty
|
||||||
|
sort of bugs out of my code, there must be some. Anywhere.
|
||||||
|
|
||||||
|
So man, read the following!
|
||||||
|
|
||||||
|
if ( found_enforcer_hit() )
|
||||||
|
{
|
||||||
|
if ( have_access_to_email() )
|
||||||
|
|
||||||
|
Send_Micha_An_Email_Report( ); /* Really! */
|
||||||
|
|
||||||
|
else if ( !too_lazy() )
|
||||||
|
|
||||||
|
Send_Micha_SnailMail_Report(); /* Cool... */
|
||||||
|
|
||||||
|
else
|
||||||
|
|
||||||
|
GuruForever(); /* Gnah... */
|
||||||
|
}
|
||||||
|
|
||||||
|
@{ " Micha's address? " link l-feedback }
|
||||||
|
@ENDNODE
|
||||||
|
|
||||||
|
@NODE l-copyright "AProf: Copyright"
|
||||||
|
This software is freeware. You are allowed to copy, distribute and
|
||||||
|
use it, as long as you don't change the software and distribute only
|
||||||
|
the packed file (AProf3_30.lha).
|
||||||
|
|
||||||
|
You are not allowed to charge a fee higher than Fred Fish's for copy-
|
||||||
|
ing and distributing.
|
||||||
|
|
||||||
|
These files and their related documentation, utilities and examples
|
||||||
|
are provided "AS-IS" and subject to change without notice; no warran-
|
||||||
|
ties are made. All use is at your own risk. No liability or respon-
|
||||||
|
sibility is assumed.
|
||||||
|
|
||||||
|
This software, the included utilities and documentation are
|
||||||
|
© 1993-94 Michael G. Binz
|
||||||
|
@ENDNODE
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
@remark *************
|
||||||
|
@remark * Abgesang
|
||||||
|
@remark *************
|
||||||
|
@NODE l-changes "AProf: Changes"
|
||||||
|
|
||||||
|
V 3.34
|
||||||
|
Bugs fixed:
|
||||||
|
o Screen updates caused enforcer hits
|
||||||
|
o Shell display of command line caused enforcer hit
|
||||||
|
|
||||||
|
Changes:
|
||||||
|
o Console window opened when started from WB now has close gadget
|
||||||
|
o XTime display uses [s] if [ms] value exceeds 1000
|
||||||
|
|
||||||
|
|
||||||
|
V 3.30
|
||||||
|
Future:
|
||||||
|
o Passive (interrupt controlled) profiling mode
|
||||||
|
|
||||||
|
Known Problems:
|
||||||
|
o AmigaGuide needs 'topaz' or maybe another fixed width font
|
||||||
|
to be the system default font. If another font is used,
|
||||||
|
AmigaGuide fails silently.
|
||||||
|
|
||||||
|
Changes:
|
||||||
|
o If run from WB the profiler acts as shell for profilees
|
||||||
|
o System default font is used
|
||||||
|
o Added sorting functions
|
||||||
|
o Extended configurability
|
||||||
|
o Maximum command line length is now 256 chars
|
||||||
|
o Special 'safe' profiling mode added
|
||||||
|
o ARexx Port for custom symbol name unmangling
|
||||||
|
|
||||||
|
Bugs fixed:
|
||||||
|
o Use of other fonts than topaz.8 trashed window
|
||||||
|
o Catched some enforcer hits
|
||||||
|
o Initial window size was wrong
|
||||||
|
o Syntax error in a pattern led to an endless loop
|
||||||
|
|
||||||
|
|
||||||
|
V 3.20
|
||||||
|
o First released
|
||||||
|
@ENDNODE
|
||||||
|
|
||||||
|
@NODE frageantwort "AProf: Question and Answer"
|
||||||
|
|
||||||
|
o What is an 'clean' symbol table?
|
||||||
|
|
||||||
|
Clean symbol tables include only function entry addresses. Neither
|
||||||
|
intermediate labels are defined nor data symbols in code hunks.
|
||||||
|
|
||||||
|
@ENDNODE
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
@remark *******
|
||||||
|
@remark * Hilfeseiten für Dialoge. Aufruf erfolgt direkt aus AProf
|
||||||
|
@remark *
|
||||||
|
@remark * Texte werden in diesem Hilfedokument mehrfach verwendet.
|
||||||
|
@remark *******
|
||||||
|
|
||||||
|
@NODE dialog-preferences "AProf: Preferences dialog"
|
||||||
|
|
||||||
|
APROF: PREFERENCES DIALOG @{ " Exit help " close }
|
||||||
|
-------------------------
|
||||||
|
|
||||||
|
Symbol pattern:
|
||||||
|
Provide a regular expression for symbols you want to exclude from
|
||||||
|
profiling. For a description of Amiga regular expressions see
|
||||||
|
your dos manual.
|
||||||
|
Patterns can be used to remove compiler generated intermediate
|
||||||
|
labels or functions you aren't interested in. One advantage of
|
||||||
|
removing functions from the symbol table is faster profiling.
|
||||||
|
|
||||||
|
Example: 'L#[0-9]' removes symbols starting with an 'L' followed
|
||||||
|
by any number of digits (L1 L00 L4711).
|
||||||
|
|
||||||
|
|
||||||
|
Separate/Combined: (Mode)
|
||||||
|
Specifies how function timing values are accumulated. If combined
|
||||||
|
is selected, the time value for a function includes the times of
|
||||||
|
all functions that are called by this function. Separate excludes
|
||||||
|
the times of called functions.
|
||||||
|
|
||||||
|
Note that if your code includes recursive procedure you must use
|
||||||
|
separate mode. If combined is used, the time for the recursive
|
||||||
|
procedure is wrong.
|
||||||
|
|
||||||
|
See @{ "example" link gad-inex }.
|
||||||
|
|
||||||
|
|
||||||
|
Millisecond/Percentual time units: (Units)
|
||||||
|
Selects the units used for for timing values. If percentual is
|
||||||
|
active, times shown are fractions of overall execution time in
|
||||||
|
percent.
|
||||||
|
|
||||||
|
|
||||||
|
Safe profiling:
|
||||||
|
If safe profiling is on (selected), then while profiling, every
|
||||||
|
breakpoint must pass additional tests to ensure that it belongs
|
||||||
|
to a function entry or exit (See @{ "dirty symbol tables" link frageantwort }).
|
||||||
|
If off, this tests are not done, resulting in faster profiling.
|
||||||
|
Since it is hard to notice the time spent in doing the tests, it
|
||||||
|
is recommended to activate safe profiling.
|
||||||
|
|
||||||
|
|
||||||
|
Rexx Unmangler:
|
||||||
|
Specifiy the filename of the Rexx procedure to be used for name
|
||||||
|
unmangling.
|
||||||
|
Some programming languages (eg. C++) offer 'type-safe linking',
|
||||||
|
which is often implemented by coding the argument types into the
|
||||||
|
symbol names (this process is called symbol mangling). Since for
|
||||||
|
humans this symbol names are then hard to read, it is possible
|
||||||
|
to specify a Rexx procedure for translating the symbol back in
|
||||||
|
a more readable form.
|
||||||
|
|
||||||
|
See @{ "example Rexx unmangler" link rexxample }.
|
||||||
|
|
||||||
|
|
||||||
|
Sort order:
|
||||||
|
Specify the preferred sort order.
|
||||||
|
@ENDNODE
|
||||||
|
|
||||||
|
@NODE rexxample "AProf: Rexx example"
|
||||||
|
|
||||||
|
/* Dummy C Unmangler
|
||||||
|
*
|
||||||
|
* This demonstrates the basics for AProf unmanglers written in Rexx
|
||||||
|
*
|
||||||
|
* Don't use this in real life, since this is included in AProf
|
||||||
|
* as standard unmangler (No mangler selected in preferences)
|
||||||
|
*
|
||||||
|
* This unmangler removes a leading '_', if one exists
|
||||||
|
*/
|
||||||
|
|
||||||
|
/* Get the symbol name from AProf and put it in 'symnam' */
|
||||||
|
parse arg symnam
|
||||||
|
|
||||||
|
/* Check if there is a leading '_' */
|
||||||
|
if "_" = left( symnam, 1 ) then
|
||||||
|
/* Remove first char */
|
||||||
|
symnam = right( symnam, length( symnam ) -1 )
|
||||||
|
|
||||||
|
/* Return result */
|
||||||
|
exit symnam
|
||||||
|
|
||||||
|
@ENDNODE
|
||||||
|
|
||||||
|
|
||||||
|
@NODE dialog-exec "AProf: Exec dialog"
|
||||||
|
|
||||||
|
APROF: EXEC DIALOG @{ " Exit help " close }
|
||||||
|
------------------
|
||||||
|
|
||||||
|
Command line:
|
||||||
|
A command line for the profilee can be specified. The command
|
||||||
|
name must not be given.
|
||||||
|
|
||||||
|
Example: You have written a program named 'foo', which needs one
|
||||||
|
argument 'bar' to be specified on the command line. The following
|
||||||
|
steps are needed to get an execution profile of 'foo':
|
||||||
|
|
||||||
|
o Start AProf: aprof foo
|
||||||
|
o Select 'Data...' and insert 'bar' as command line
|
||||||
|
o Select 'Use'
|
||||||
|
o Select 'Start'
|
||||||
|
|
||||||
|
|
||||||
|
Stack size:
|
||||||
|
Provide a stack size for the profilee.
|
||||||
|
@ENDNODE
|
||||||
|
|
||||||
|
|
Binary file not shown.
|
@ -0,0 +1,33 @@
|
||||||
|
AProf has been included in the Amiga E distribution with the
|
||||||
|
approval of the author, Michael G. Binz. Thanks Michael!
|
||||||
|
|
||||||
|
[the AProf executable itself can be found in the bin directory]
|
||||||
|
|
||||||
|
usage of AProf with Amiga E is dead simple:
|
||||||
|
|
||||||
|
compile your source (say, 'bla.e') with the '-s' switch (SYM/S)
|
||||||
|
(= add symbols):
|
||||||
|
|
||||||
|
1> ec sym bla
|
||||||
|
|
||||||
|
as you can check with ShowHunk, bla now has a symbolhunk
|
||||||
|
added to it. Now, load 'bla' into AProf:
|
||||||
|
|
||||||
|
1> aprof bla
|
||||||
|
|
||||||
|
if everything went ok, you'll be seeing a GUI with some
|
||||||
|
buttons. You'll probably want to toggle 'inclusive subroutines'
|
||||||
|
off. now start the profile by pressing 'Start'. After your
|
||||||
|
program has finished, you'll see the amount of time spend
|
||||||
|
in each PROC. Especially the 'Average' column is interesting.
|
||||||
|
This will give you a clue which part of your program needs
|
||||||
|
optimising.
|
||||||
|
|
||||||
|
notes:
|
||||||
|
- as you might have noticed, E system functions CAN be profiled
|
||||||
|
seperatedly, but library calls CANNOT, i.e. the'll always
|
||||||
|
be added to the PROC that calls them. Be aware of this.
|
||||||
|
- AProf is not always reliable when the executation time
|
||||||
|
of the whole program is very short.
|
||||||
|
|
||||||
|
For further infos, check AProf.guide
|
|
@ -0,0 +1,5 @@
|
||||||
|
Here you'll find the distributions/docs of currently only
|
||||||
|
Aprof (a profiler), and the docs for Explorer (an object browser)
|
||||||
|
and EFindHit (an enforcer tool). Mac2E has been removed due to the
|
||||||
|
new internal macro-preprocessor in E v3.1a, EE is not being updated
|
||||||
|
anymore, and the EYacc stuff has moved to the Src/Lang directory.
|
|
@ -0,0 +1,197 @@
|
||||||
|
Short: EFindHit v1.2 (for Enforcer output)
|
||||||
|
Type: dev/e
|
||||||
|
Author: jason@fsel.com (Jason R. Hulance)
|
||||||
|
|
||||||
|
EFindHit can be used to find the line number of an Enforcer hit in an
|
||||||
|
E program, given the offset information that Enforcer produces.
|
||||||
|
Unlike normal FindHit programs, EFindHit reports the line numbers of
|
||||||
|
hits in E modules, too (i.e., not just the main program). And it can
|
||||||
|
also tell you which function the code was in.
|
||||||
|
|
||||||
|
|
||||||
|
Usage
|
||||||
|
-----
|
||||||
|
|
||||||
|
To use EFindHit, the main source file *must* be compiled with the
|
||||||
|
LINEDEBUG or DEBUG switches. And if you want to track hits in modules
|
||||||
|
used by the program, they too must be compiled with one of these
|
||||||
|
switches.
|
||||||
|
|
||||||
|
If you want to see the names of functions as well, then you need to
|
||||||
|
use the SYM switch, too, but only on the main source file. This is
|
||||||
|
especially useful for tracking hits in E internal functions. If you
|
||||||
|
don't use the SYM switch when compiling your source then EFindHit
|
||||||
|
cannot reliably identify hits that occur in E internal functions (it
|
||||||
|
may report them as coming from the last line of one of the sources,
|
||||||
|
and flag those offsets that appear to be too far beyond the last
|
||||||
|
line).
|
||||||
|
|
||||||
|
EFindHit takes the following arguments:
|
||||||
|
|
||||||
|
EXECUTABLE/A,DEBUG/S,OFFSETS/M
|
||||||
|
|
||||||
|
So, you can supply a list of offsets. Hexadecimal offsets can be
|
||||||
|
specified using a leading '$' or '0x'. Without these prefixes the
|
||||||
|
offset is interpreted as decimal.
|
||||||
|
|
||||||
|
New to v1.2 is the DEBUG switch: see below for an illustrative
|
||||||
|
example.
|
||||||
|
|
||||||
|
|
||||||
|
Example
|
||||||
|
-------
|
||||||
|
|
||||||
|
For example, consider the following output from Enforcer (when
|
||||||
|
SegTracker is running):
|
||||||
|
|
||||||
|
BYTE-READ from 00000000 PC: 7833432C
|
||||||
|
USP: 7834B4B0 SR: 0004 SW: 0751 (U0)(-)(-) TCB: 7828F188
|
||||||
|
Data: 00000000 0000000D 78348FB8 7829016C 00000001 1E0CCF39 78348FB8 00000001
|
||||||
|
Addr: 00000000 7828F1E4 783343AE 7829016C 7834B6C0 7834B4B4 78019864 --------
|
||||||
|
Stck: 00000000 7829016C 78334306 783341F2 00000000 00000000 00000000 00000000
|
||||||
|
Stck: 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000
|
||||||
|
----> 7833432C - "xxx" Hunk 0000 Offset 00000244
|
||||||
|
Name: "Shell Process" CLI: "xxx" Hunk 0000 Offset 00000244
|
||||||
|
|
||||||
|
The interested lines are the first and last. The first line says the
|
||||||
|
hit was a byte read from $00000000 (probably from dereferencing a NIL
|
||||||
|
pointer). The last line says that the culprit was the program "xxx"
|
||||||
|
and that the hit happened at offset $00000244. (You might also
|
||||||
|
consider using the STACKCHECK option of Enforcer to get the backtrace
|
||||||
|
of the call stack.)
|
||||||
|
|
||||||
|
If "xxx" has been compiled with LINEDEBUG (or DEBUG) then you can run
|
||||||
|
EFindHit on it to find the line number of the hit in one of the source
|
||||||
|
files:
|
||||||
|
|
||||||
|
1.System:> efindhit xxx $244
|
||||||
|
EFindHit works better if you compile with the SYM option
|
||||||
|
Offset $244: "xxx.e" line 5 [$242]
|
||||||
|
|
||||||
|
The first line of output is a reminder that using SYM makes EFindHit
|
||||||
|
work better, then the information about the offset. The trailing
|
||||||
|
"[$242]" indicates the offset of the start of the code for line 5.
|
||||||
|
|
||||||
|
If you've compiled "xxx" with the SYM option too, then things get a
|
||||||
|
bit better:
|
||||||
|
|
||||||
|
1.System:> efindhit xxx $244
|
||||||
|
Offset $244: "xxx.e" line 5 [$242], in "fun()" [$238]
|
||||||
|
|
||||||
|
This also states the function the hit occurred in, and gives the
|
||||||
|
offset of the start of code for that function ($238).
|
||||||
|
|
||||||
|
If you don't trust the output of EFindHit (???), then you can use the
|
||||||
|
DEBUG switch to dump all the information from the executable that
|
||||||
|
EFindHit uses. (If this switch is specified then any supplied offsets
|
||||||
|
are ignored.) For example:
|
||||||
|
|
||||||
|
1.System:> efindhit xxx debug
|
||||||
|
$00000000 *** Startup1 ***
|
||||||
|
$00000196 *** End of startup1 ***
|
||||||
|
$00000374 *** End of code ***
|
||||||
|
$00000236 fun()
|
||||||
|
$000001D2 zzz()
|
||||||
|
$00000196 external()
|
||||||
|
$00000284 main()
|
||||||
|
$000002DA WriteF()
|
||||||
|
$0000036A Char()
|
||||||
|
$0000023A 14 xxx.e
|
||||||
|
$00000240 15 xxx.e
|
||||||
|
$00000244 16 xxx.e
|
||||||
|
$00000288 22 xxx.e
|
||||||
|
$0000028E 23 xxx.e
|
||||||
|
$000001D6 4 zzz.e
|
||||||
|
$0000019A 8 xyz.e
|
||||||
|
$000001EE *** Startup2 ***
|
||||||
|
$00000200 *** End of Startup2 ***
|
||||||
|
|
||||||
|
The first column is the offset, the second (if present) is the line
|
||||||
|
number and the last column is the name of the file, the function or
|
||||||
|
another significant part of the executable. You could sort this
|
||||||
|
output if you have pipes set up:
|
||||||
|
|
||||||
|
1.System:> efindhit xxx debug | sort in: out:
|
||||||
|
$00000000 *** Startup1 ***
|
||||||
|
$00000196 *** End of startup1 ***
|
||||||
|
$00000196 external()
|
||||||
|
$0000019A 8 xyz.e
|
||||||
|
$000001D2 zzz()
|
||||||
|
$000001D6 4 zzz.e
|
||||||
|
$000001EE *** Startup2 ***
|
||||||
|
$00000200 *** End of Startup2 ***
|
||||||
|
$00000236 fun()
|
||||||
|
$0000023A 14 xxx.e
|
||||||
|
$00000240 15 xxx.e
|
||||||
|
$00000244 16 xxx.e
|
||||||
|
$00000284 main()
|
||||||
|
$00000288 22 xxx.e
|
||||||
|
$0000028E 23 xxx.e
|
||||||
|
$000002DA WriteF()
|
||||||
|
$0000036A Char()
|
||||||
|
$00000374 *** End of code ***
|
||||||
|
|
||||||
|
Now it's really easy to see where any particular offset lies...
|
||||||
|
|
||||||
|
|
||||||
|
Possible output
|
||||||
|
---------------
|
||||||
|
|
||||||
|
As well as complaining about errors (like you forgot to compile with
|
||||||
|
LINEDEBUG), EFindHit might report:
|
||||||
|
|
||||||
|
1) Offset $244: "xxx.e" line 5 [$242], in "fun()" [$238]
|
||||||
|
|
||||||
|
The offset is in the identified function in the main source.
|
||||||
|
|
||||||
|
2) Offset $200: "xyz.e" line 11 [$1E8], near EXPORTed "external()" [$196]
|
||||||
|
|
||||||
|
The line number information for code in modules is more accurate than
|
||||||
|
the function information, since only EXPORTed functions can be
|
||||||
|
followed. So EFindHit reminds you that the code at the offset is only
|
||||||
|
"near" the EXPORTed function (i.e., some point after it but before the
|
||||||
|
next EXPORTed function).
|
||||||
|
|
||||||
|
3) Offset $280: E internal function "Char()"
|
||||||
|
|
||||||
|
The hit has occurred in an E internal function. You probably want the
|
||||||
|
backtrace in this case (the STACKCHECK option of Enforcer), so you can
|
||||||
|
spot which function this was called from.
|
||||||
|
|
||||||
|
4) Offset $190: E startup code
|
||||||
|
|
||||||
|
The hit has occurred outside user code, either when starting or
|
||||||
|
cleaning up. This didn't ought to happen unless your code has
|
||||||
|
scribbled all over the place. (These offsets will show up in the
|
||||||
|
backtrace produced by the STACKCHECK option of Enforcer, as E code
|
||||||
|
always starts in the startup code!)
|
||||||
|
|
||||||
|
5) Invalid offset $330: not in code part
|
||||||
|
|
||||||
|
The hit didn't occur within the code of the executable being analysed.
|
||||||
|
This might happen if you're running EFindHit on the wrong program or a
|
||||||
|
different version from that which produced the hit.
|
||||||
|
|
||||||
|
6) No line number for offset $200
|
||||||
|
|
||||||
|
EFindHit couldn't find a line number (but might still be able to
|
||||||
|
identify a function, nonetheless, and this will be reported as "in" or
|
||||||
|
"near" as appropriate [assuming you've compiled with the SYM option]).
|
||||||
|
|
||||||
|
|
||||||
|
Limitations
|
||||||
|
-----------
|
||||||
|
|
||||||
|
There's no symbol information (currently?) for any methods or for
|
||||||
|
functions which are not EXPORTed from modules. So it's not possible
|
||||||
|
to put names to offsets in these bits of code (the line numbers should
|
||||||
|
be sufficicient in these cases). And offsets in methods/INCBIN/other
|
||||||
|
data in the main source file will be reported as being "in" the
|
||||||
|
nearest function before it.
|
||||||
|
|
||||||
|
The startup code recognition is pretty much specific to EC v3.2e.
|
||||||
|
It's likely to change in future...
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
Have fun!
|
|
@ -0,0 +1,249 @@
|
||||||
|
Short: Amazing System Browser and Debugger
|
||||||
|
Type: dev/e
|
||||||
|
Author: jason@fsel.com (Jason R. Hulance)
|
||||||
|
|
||||||
|
|
||||||
|
Explorer 2.2j
|
||||||
|
=============
|
||||||
|
Allows you to browse around memory as E objects (which are like C structs).
|
||||||
|
|
||||||
|
|
||||||
|
Basic Function
|
||||||
|
--------------
|
||||||
|
You enter an address and then select the object which is (supposed to be?)
|
||||||
|
at that address. You can then see the value that each element of the
|
||||||
|
object takes, and follow pointers etc. by double-clicking on the element (a
|
||||||
|
single click changes the address to the element's address, which is an
|
||||||
|
offset from the address you specified for the object).
|
||||||
|
|
||||||
|
A double-click may therefore change the object being browsed, so there is a
|
||||||
|
way of returning to the original object via the "Back Up" button. As you
|
||||||
|
double-click and select new objects your choices are recorded. The current
|
||||||
|
depth of this path is shown as the 'Depth:' text. The action of the "Back
|
||||||
|
Up" button is duplicated by the "Back Up" menu item on the "Selections"
|
||||||
|
menu. There is also an option to clear the current path on this menu.
|
||||||
|
|
||||||
|
The list of objects may be created in several ways (these are found on the
|
||||||
|
"Project" menu):
|
||||||
|
|
||||||
|
1) "Open" Opens a previously saved config file, containing any number of
|
||||||
|
objects. By default, when Explorer starts it tries to load
|
||||||
|
objects from "explorer.config" in the current directory, and
|
||||||
|
then "s:explorer.config" if that fails. The supplied
|
||||||
|
"explorer.config" file contains all the standard Amiga OS
|
||||||
|
objects. (This file was created by using "Load Modules" on the
|
||||||
|
"Emodules:" directory and then saving the config using the next
|
||||||
|
option.)
|
||||||
|
2) "Save" Saves the current list of objects as an Explorer config file.
|
||||||
|
3) "Load Modules" Scan the selected module or all the modules in the
|
||||||
|
selected directory and its sub-directories. The found objects
|
||||||
|
will replace any current list of objects. This operation can
|
||||||
|
take a long time, especially if you try to load all the modules
|
||||||
|
in "Emodules:". You can interrupt it at any time by closing the
|
||||||
|
status window.
|
||||||
|
4) "Merge Modules" Same as 3) but any objects found are added to the
|
||||||
|
current list of objects rather than replacing them.
|
||||||
|
5) "Load Cache" Same as 3) but the current E module cache is scanned.
|
||||||
|
This is most useful when Explorer is used to help debug your
|
||||||
|
programs.
|
||||||
|
|
||||||
|
The address and object can also be specified via ARexx, making Explorer an
|
||||||
|
extremely useful debugging tool as well as a system browser! In fact, EDBG
|
||||||
|
v3.3a+ makes use of Explorer to analyse typed variables. (A module is
|
||||||
|
supplied to show how easy it is to integrate the appropriate ARexx call
|
||||||
|
into your program.)
|
||||||
|
|
||||||
|
|
||||||
|
Object Layout
|
||||||
|
-------------
|
||||||
|
The elements of the selected object are listed in order. If you choose to
|
||||||
|
see the value of element it will be displayed in hex in parenthesis after
|
||||||
|
the element, and if you choose to see the element address (an offset from
|
||||||
|
the object address) it will be displayed after an '@'. (Use the
|
||||||
|
"Selections" menu to decide which, if any, you want to see.)
|
||||||
|
|
||||||
|
|
||||||
|
Where to Start
|
||||||
|
--------------
|
||||||
|
Explorer starts off with the address of an internal hail and copyright
|
||||||
|
string. The items on the "Presets" menu give some other, interesting
|
||||||
|
starting points:
|
||||||
|
|
||||||
|
1) "Dos" Views the address stored in the 'dosbase' global variable as a
|
||||||
|
'doslibrary' object. The dos.library is one of the fundamental
|
||||||
|
Amiga system libraries, and 'dosbase' is its corresponding
|
||||||
|
library base object.
|
||||||
|
2) "Exec" Same as 1) but for the 'execbase' variable and object for the
|
||||||
|
exec.library (the most fundamental Amiga system library).
|
||||||
|
3) "Graphics" Same as 1) but for the 'gfxbase' variable and object for the
|
||||||
|
graphics.library.
|
||||||
|
4) "Intuition." Same as 1) but for the 'intuitionbase' variable and
|
||||||
|
object for the intuition.library.
|
||||||
|
5) "This Process." Views the 'process' object for the running Explorer
|
||||||
|
program!
|
||||||
|
|
||||||
|
For a typical Explorer session, you would probably start at one of these
|
||||||
|
points and then double-click on elements and use the "Back Up" button to
|
||||||
|
browse around. You never really need to set the address explicitly on the
|
||||||
|
GUI (at worst you'd do it by ARexx), but the option is there if you really
|
||||||
|
want to...
|
||||||
|
|
||||||
|
|
||||||
|
Simple Types
|
||||||
|
------------
|
||||||
|
The "CHAR", "INT" and "LONG" buttons view the contents of the address as if
|
||||||
|
it were a pointer to the corresponding type, and fills in the 'Hex:',
|
||||||
|
'Decimal:' and 'String:' texts with the value(s) being pointed at.
|
||||||
|
|
||||||
|
"BSTR" is similar to "CHAR" but it first converts the address from a BCPL
|
||||||
|
pointer and then skips over the length byte. "BADDR" just converts the
|
||||||
|
address to a BCPL pointer.
|
||||||
|
|
||||||
|
"--" and "++" treat the address as if it were pointing to an array of the
|
||||||
|
type last requested (i.e., an object or simple type). "--" moves the
|
||||||
|
address back to the previous element in the array, and "++" moves it on to
|
||||||
|
the next. The size of the last requested type is shown as the 'Size:'
|
||||||
|
text. (Note: "--" can be activated also by the "p" key and "++" by the "n"
|
||||||
|
key.)
|
||||||
|
|
||||||
|
|
||||||
|
Display Beeps!
|
||||||
|
--------------
|
||||||
|
The screen will flash (or you'll get a beep or whatever your Workbench is
|
||||||
|
set up to do) if you do one of the following:
|
||||||
|
|
||||||
|
1) Select something when the address is NIL (zero). A NIL address is not
|
||||||
|
valid (although Explorer will behave as if it were).
|
||||||
|
2) Select an element which is a NIL pointer or a LONG that is NIL.
|
||||||
|
3) Select an element with object or PTR TO object type, where the object
|
||||||
|
is not in the current list of objects.
|
||||||
|
4) Try to backup past a depth of zero.
|
||||||
|
|
||||||
|
|
||||||
|
Command Line Options
|
||||||
|
--------------------
|
||||||
|
The command line template is:
|
||||||
|
|
||||||
|
OBJECT,ADDR,ALL/S,CACHE/S,NONE/S,CONFIG/K,SCREEN/K,NOAUTOREPLY/S
|
||||||
|
|
||||||
|
1) OBJECT Specify an initial object to be displayed (to be useful, the
|
||||||
|
appropriate module must also be loaded using 'ALL' or 'CACHE').
|
||||||
|
2) ADDR Specify an initial address to be displayed (NIL is ignored!).
|
||||||
|
3) ALL Load all objects from the modules in Emodules: and its
|
||||||
|
sub-directories (overrides 'CACHE' and 'CONFIG').
|
||||||
|
4) CACHE Load all objects from the modules in the current E module cache
|
||||||
|
(overrides 'CONFIG').
|
||||||
|
5) NONE Start with no objects (overrides 'ALL', 'CACHE' and 'CONFIG').
|
||||||
|
6) CONFIG Specify the config file to load. If this load fails then
|
||||||
|
Explorer will try "s:explorer.config".
|
||||||
|
7) SCREEN Specify the public screen to open on. Normally Explorer will
|
||||||
|
open on the default public screen.
|
||||||
|
8) NOAUTOREPLY Specifies that the "Auto Reply" option (described below)
|
||||||
|
should start as off rather than on.
|
||||||
|
|
||||||
|
By default, Explorer will try to load the "explorer.config" in the current
|
||||||
|
directory (as if you'd used the CONFIG option -- so it will fall back to
|
||||||
|
trying "s:explorer.config"). The starting address will be that of an
|
||||||
|
internal hail and copyright string (which will be displayed as the
|
||||||
|
'String:' text).
|
||||||
|
|
||||||
|
|
||||||
|
ARexx
|
||||||
|
-----
|
||||||
|
The "Reply" menu item on the "ARexx" menu manually replies to an ARexx
|
||||||
|
message that may have been received. You can also let Explorer
|
||||||
|
automatically reply to these messages by checking the "Auto Reply" option,
|
||||||
|
but the manual method is best since you can be sure that the pointers
|
||||||
|
remain valid while you browse (since the calling program is halted). Once
|
||||||
|
you've replied to a message the calling program is free to scribble all
|
||||||
|
over the memory at the address it just gave you...
|
||||||
|
|
||||||
|
A module 'sendexplorer' is supplied to simplify the debugging facilities of
|
||||||
|
Explorer. It defines several functions:
|
||||||
|
|
||||||
|
1) PROC sendExplorer(addr,obj=NIL,repPort=NIL,message=NIL,quiet=FALSE)
|
||||||
|
|
||||||
|
'addr' is the address you wish to use.
|
||||||
|
'obj' is the name of the object you are interested in.
|
||||||
|
'repPort' is a message port for replies. If you don't supply this then a
|
||||||
|
temporary port is created for the duration of the function call.
|
||||||
|
(The purpose of this parameter is to allow you to re-use the
|
||||||
|
same port during your program if you call 'sendExplorer' a lot.)
|
||||||
|
'message' is a short string to be displayed in the 'Message:' part of
|
||||||
|
Explorer. Use this to communicate some information about the
|
||||||
|
address and object (EDBG uses this to give the name, type and
|
||||||
|
scope of the variable being explored).
|
||||||
|
'quiet' is a boolean to say whether Explorer should complain if the
|
||||||
|
object is not loaded.
|
||||||
|
|
||||||
|
The result is TRUE if the message was successfully delivered and did
|
||||||
|
not cause an ARexx error, and FALSE otherwise.
|
||||||
|
|
||||||
|
An example call is:
|
||||||
|
|
||||||
|
sendExplorer(packet, 'dospacket')
|
||||||
|
|
||||||
|
To browse using your own objects you'd separate them out into modules and
|
||||||
|
load these modules in your program. Compiling your program would add these
|
||||||
|
to the E module cache, which can then be used to set-up Explorer (using the
|
||||||
|
"Load Cache" menu item). Alternatively, you could "Load" or "Merge" the
|
||||||
|
appropriate module directly, or use a config file if the objects don't
|
||||||
|
change very often.
|
||||||
|
|
||||||
|
2) PROC quitExplorer(repPort=NIL)
|
||||||
|
|
||||||
|
'repPort' is as above.
|
||||||
|
|
||||||
|
This sends a 'QUIT' message to Explorer. Again, it returns TRUE if
|
||||||
|
everything succeeded.
|
||||||
|
|
||||||
|
3) PROC isExplorerRunning()
|
||||||
|
|
||||||
|
Returns TRUE if Explorer is running. It works this out by testing for
|
||||||
|
the presence of Explorer's ARexx port, so it may be fooled..
|
||||||
|
|
||||||
|
|
||||||
|
Altering Data
|
||||||
|
-------------
|
||||||
|
By popular demand, if you hold down the shift key when you click on an
|
||||||
|
element of an object or on the 'CHAR', 'INT' or 'LONG' buttons, then a
|
||||||
|
small window pops up which allows you to edit the appropriate value. It's
|
||||||
|
not very safe to randomly edit data (especially system data), so use of
|
||||||
|
this feature is discouraged unless you really know what you're doing...
|
||||||
|
|
||||||
|
|
||||||
|
Example Program
|
||||||
|
---------------
|
||||||
|
To show how useful Explorer can be, there is a modified version of the
|
||||||
|
'empty' handler supplied. This uses the above call to 'sendExplorer' to
|
||||||
|
communicate the value of the packet currently being processed. Since the
|
||||||
|
handler is not a full process (it's just a task) there would normally be no
|
||||||
|
simple way of debugging it or examining the packets as they arrive (none of
|
||||||
|
the DOS functions, such as printing text, are available to simple tasks).
|
||||||
|
However, the handler can send messages to ARexx and thus to Explorer.
|
||||||
|
|
||||||
|
To try it out:
|
||||||
|
|
||||||
|
1) Make sure ARexx is up (by running RexxMast, if necessary).
|
||||||
|
2) Start up Explorer using,
|
||||||
|
run explorer all
|
||||||
|
3) Now copy the 'empty-handler' to L:,
|
||||||
|
copy empty-handler L:
|
||||||
|
4) Mount it using,
|
||||||
|
mount empty: from empty.mountlist
|
||||||
|
5) Use the handler,
|
||||||
|
type empty:10
|
||||||
|
6) Look at Explorer. It will be displaying the first packet. Browse
|
||||||
|
around it, remembering that most elements will be BCPL strings.
|
||||||
|
7) Select "Reply" from the ARexx menu (or press RAmiga-R) to reply to this
|
||||||
|
message.
|
||||||
|
8) Another packet arrives and the message is sent to Explorer.
|
||||||
|
9) Repeat 6)-8) until the 'type' command finishes and you get the Shell
|
||||||
|
prompt back.
|
||||||
|
|
||||||
|
|
||||||
|
EDBG
|
||||||
|
----
|
||||||
|
EDBG v3.3a+ communicates data about a variable if you hold down the shift
|
||||||
|
key and double-click on a variable that's within the current scope (EDBG
|
||||||
|
also adds it to the list of watched variables, if necessary).
|
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
|
@ -0,0 +1,497 @@
|
||||||
|
@database beginner.guide
|
||||||
|
|
||||||
|
@Master beginner
|
||||||
|
|
||||||
|
@Width 75
|
||||||
|
|
||||||
|
|
||||||
|
This is the AmigaGuide® file beginner.guide, produced by Makeinfo-1.55 from
|
||||||
|
the input file beginner.
|
||||||
|
|
||||||
|
|
||||||
|
@NODE "main" "Common Problems"
|
||||||
|
@Next "Other Information"
|
||||||
|
@Prev "Examples.guide/Recursion Example"
|
||||||
|
@Toc "Contents.guide/main"
|
||||||
|
|
||||||
|
Common Problems
|
||||||
|
***************
|
||||||
|
|
||||||
|
If you are new to programming or the Amiga E language then you might
|
||||||
|
appreciate some help locating problems (or @{fg shine }bugs@{fg text }) in your programs. This
|
||||||
|
Appendix details some of the most common mistakes people make.
|
||||||
|
|
||||||
|
|
||||||
|
@{" Assignment and Copying " Link "Assignment and Copying" }
|
||||||
|
@{" Pointers and Memory Allocation " Link "Pointers and Memory Allocation" }
|
||||||
|
@{" String and List Misuse " Link "String and List Misuse" }
|
||||||
|
@{" Initialising Data " Link "Initialising Data" }
|
||||||
|
@{" Freeing Resources " Link "Freeing Resources" }
|
||||||
|
@{" Pointers and Dereferencing " Link "Pointers and Dereferencing" }
|
||||||
|
@{" Mathematics Functions " Link "Mathematics Functions" }
|
||||||
|
@{" Signed and Unsigned Values " Link "Signed and Unsigned Values" }
|
||||||
|
|
||||||
|
|
||||||
|
@ENDNODE
|
||||||
|
|
||||||
|
@NODE "Assignment and Copying" "Assignment and Copying"
|
||||||
|
@Next "Pointers and Memory Allocation"
|
||||||
|
@Toc "main"
|
||||||
|
|
||||||
|
Assignment and Copying
|
||||||
|
======================
|
||||||
|
|
||||||
|
This is probably the most common problem encountered by people who are
|
||||||
|
used to languages like BASIC. Strings, lists, arrays and objects cannot
|
||||||
|
be initialised using an assignment statement: data must be copied. Unlike
|
||||||
|
BASIC, this kind of data is represented by a pointer (see @{"PTR Type" Link "Types.guide/PTR Type" }), so
|
||||||
|
only the pointer would be copied by an assignment statement, not the data
|
||||||
|
it points to. The following examples all copy a pointer rather than the
|
||||||
|
data, and so the memory for the data is shared (and this is probably not
|
||||||
|
what was intended).
|
||||||
|
|
||||||
|
DEF s[30]:STRING, t[30]:STRING,
|
||||||
|
l[10]:LIST, m[10]:LIST,
|
||||||
|
x:myobj, y:myobj,
|
||||||
|
a[25]:ARRAY OF INT, b[25]:ARRAY OF INT
|
||||||
|
|
||||||
|
/* You probably don't want to do any of these */
|
||||||
|
s:='Some text in a string'
|
||||||
|
l:=[-6,4,-9]
|
||||||
|
x:=[1,2,3]:myobj
|
||||||
|
a:=[1,-3,8,7]:INT
|
||||||
|
|
||||||
|
t:=s
|
||||||
|
m:=l
|
||||||
|
y:=x
|
||||||
|
b:=a
|
||||||
|
|
||||||
|
All the declarations allocate memory for the appropriate data. The first
|
||||||
|
four assignments replace the pointers to this memory with pointers to some
|
||||||
|
statically allocated memory. The memory allocated by the declarations is
|
||||||
|
probably now unreachable, because the only pointers to it have been
|
||||||
|
over-written. BASIC programmers might expect, say, the assignment to @{b }s@{ub } to
|
||||||
|
have copied the string into the memory allocated for @{b }s@{ub } by its declaration,
|
||||||
|
but this is not the case (only the pointer to the string is copied).
|
||||||
|
|
||||||
|
For the E-string, @{b }s@{ub }, and E-list, @{b }l@{ub }, there is another, disastrous
|
||||||
|
side-effect. The assignment to @{b }s@{ub }, for example, means that @{b }s@{ub } will point to
|
||||||
|
a normal string, not an E-string. So, @{b }s@{ub } can no longer be used with any of
|
||||||
|
the E-string functions. The same considerations apply to the E-list, @{b }l@{ub },
|
||||||
|
as well.
|
||||||
|
|
||||||
|
The final four assignments also copy only the pointers. This means
|
||||||
|
that @{b }s@{ub } and @{b }t@{ub } will point to exactly the same memory. So they will
|
||||||
|
represent exactly the same string, and any change to one of them (by a
|
||||||
|
@{b }StrAdd@{ub }, for example) will appear to change both (of course, only one lump
|
||||||
|
of memory is being changed, but there are two references to it). This is
|
||||||
|
called memory @{fg shine }sharing@{fg text }, and is only a problem if you didn't intend to do
|
||||||
|
it!
|
||||||
|
|
||||||
|
To get the result that a BASIC programmer might have intended you need
|
||||||
|
to copy the appropriate data. For E-strings and E-lists the functions to
|
||||||
|
use are, respectively, @{b }StrCopy@{ub } and @{b }ListCopy@{ub }. All other data must be
|
||||||
|
copied using a function like @{b }CopyMem@{ub } (an Amiga system function from the
|
||||||
|
Exec library). (Normal strings can be copied using @{b }AstrCopy@{ub } built-in
|
||||||
|
function, see the `Reference Manual'.) Here's the revised forms of the
|
||||||
|
above assignments:
|
||||||
|
|
||||||
|
DEF s[30]:STRING, t[30]:STRING,
|
||||||
|
l[10]:LIST, m[10]:LIST,
|
||||||
|
x:myobj, y:myobj,
|
||||||
|
a[25]:ARRAY OF INT, b[25]:ARRAY OF INT
|
||||||
|
|
||||||
|
StrCopy(s, 'Some text in a string') /* Defaults to ALL */
|
||||||
|
ListCopy(l, [-6,4,-9]) /* Defaults to ALL */
|
||||||
|
CopyMem([1,2,3]:myobj, x, SIZEOF myobj)
|
||||||
|
CopyMem([1,-3,8,7]:INT, a, 4*SIZEOF INT)
|
||||||
|
|
||||||
|
StrCopy(t, s) /* Defaults to ALL */
|
||||||
|
ListCopy(m, l) /* Defaults to ALL */
|
||||||
|
CopyMem(x, y, SIZEOF myobj)
|
||||||
|
CopyMem(a, b, 4*SIZEOF INT)
|
||||||
|
|
||||||
|
Notice that you need to supply the size (in bytes) of the data being
|
||||||
|
copied when you use @{b }CopyMem@{ub }. The parameters are also given in a slightly
|
||||||
|
different order to the E-string and E-list copying functions (i.e., the
|
||||||
|
source must be the first parameter and the destination the second). The
|
||||||
|
@{b }CopyMem@{ub } function does a byte-by-byte copy, something like this:
|
||||||
|
|
||||||
|
PROC copymem(src, dest, size)
|
||||||
|
DEF i
|
||||||
|
FOR i:=1 TO size DO dest[]++:=src[]++
|
||||||
|
ENDPROC
|
||||||
|
|
||||||
|
Of course, you can use string constants and lists to give initialised
|
||||||
|
arrays, but in this case you should be initialising an appropriately typed
|
||||||
|
pointer. You must also be careful not to run into a static data problem
|
||||||
|
(see @{"Static data" Link "Types.guide/Static data" }).
|
||||||
|
|
||||||
|
DEF s:PTR TO CHAR, l:PTR TO LONG, x:PTR TO myobj, a:PTR TO INT
|
||||||
|
s:='Some text in a string'
|
||||||
|
l:=[-6,4,-9]
|
||||||
|
x:=[1,2,3]:myobj
|
||||||
|
a:=[1,-3,8,7]:INT
|
||||||
|
|
||||||
|
|
||||||
|
@ENDNODE
|
||||||
|
|
||||||
|
@NODE "Pointers and Memory Allocation" "Pointers and Memory Allocation"
|
||||||
|
@Next "String and List Misuse"
|
||||||
|
@Prev "Assignment and Copying"
|
||||||
|
@Toc "main"
|
||||||
|
|
||||||
|
Pointers and Memory Allocation
|
||||||
|
==============================
|
||||||
|
|
||||||
|
Another common error is to declare a pointer (usually a pointer to an
|
||||||
|
object) and then use it without the memory for the target data being
|
||||||
|
allocated.
|
||||||
|
|
||||||
|
/* You don't want to do this */
|
||||||
|
DEF p:PTR TO object
|
||||||
|
p.element:=99
|
||||||
|
|
||||||
|
There are two ways of correcting this: either dynamically allocate the
|
||||||
|
memory using @{b }NEW@{ub } or, more simply, let an appropriate declaration allocate
|
||||||
|
it. See @{"Memory Allocation" Link "Memory.guide/main" }.
|
||||||
|
|
||||||
|
DEF p:PTR TO object
|
||||||
|
NEW p
|
||||||
|
p.element:=99
|
||||||
|
|
||||||
|
DEF p:object
|
||||||
|
p.element:=99
|
||||||
|
|
||||||
|
|
||||||
|
@ENDNODE
|
||||||
|
|
||||||
|
@NODE "String and List Misuse" "String and List Misuse"
|
||||||
|
@Next "Initialising Data"
|
||||||
|
@Prev "Pointers and Memory Allocation"
|
||||||
|
@Toc "main"
|
||||||
|
|
||||||
|
String and List Misuse
|
||||||
|
======================
|
||||||
|
|
||||||
|
Some of the string functions can only be used with E-strings.
|
||||||
|
Generally, these are the ones that might extend the string. If you use a
|
||||||
|
normal string instead you can run into some serious (but subtle) problems.
|
||||||
|
Commonly misused functions are @{b }ReadStr@{ub }, @{b }MidStr@{ub } and @{b }RightStr@{ub }. Similar
|
||||||
|
problems can arise by using a list when an E-list is required by a list
|
||||||
|
function.
|
||||||
|
|
||||||
|
String constants and normal lists are static data, so you shouldn't try
|
||||||
|
to alter their contents unless you know what you're doing (see
|
||||||
|
@{"Static data" Link "Types.guide/Static data" }).
|
||||||
|
|
||||||
|
|
||||||
|
@ENDNODE
|
||||||
|
|
||||||
|
@NODE "Initialising Data" "Initialising Data"
|
||||||
|
@Next "Freeing Resources"
|
||||||
|
@Prev "String and List Misuse"
|
||||||
|
@Toc "main"
|
||||||
|
|
||||||
|
Initialising Data
|
||||||
|
=================
|
||||||
|
|
||||||
|
Probably one of the most common mistakes that even seasoned programmers
|
||||||
|
make is to forget to initialise variables (especially pointers). The
|
||||||
|
rules in the `Reference Manual' state which declarations initialise
|
||||||
|
variables to zero values, but it is often wise to make even these explicit
|
||||||
|
(using initialised declarations). Variable initialisation becomes even
|
||||||
|
more important when using automatic exceptions.
|
||||||
|
|
||||||
|
|
||||||
|
@ENDNODE
|
||||||
|
|
||||||
|
@NODE "Freeing Resources" "Freeing Resources"
|
||||||
|
@Next "Pointers and Dereferencing"
|
||||||
|
@Prev "Initialising Data"
|
||||||
|
@Toc "main"
|
||||||
|
|
||||||
|
Freeing Resources
|
||||||
|
=================
|
||||||
|
|
||||||
|
Unlike a Unix operating system, the Amiga operating system requires the
|
||||||
|
programmer to release or free any resources used by a program. In
|
||||||
|
practice, this means that all windows, screens, libraries, etc., that are
|
||||||
|
successfully opened must be closed before the program terminates. Amiga E
|
||||||
|
provides some help, though: the four most commonly used libraries (Dos,
|
||||||
|
Exec, Graphics and Intuition) are opened before the start of an E program
|
||||||
|
and closed at the end (or when @{b }CleanUp@{ub } is called). Also, memory allocated
|
||||||
|
using any of @{b }List@{ub }, @{b }String@{ub }, @{b }New@{ub }, @{b }NEW@{ub }, @{b }NewR@{ub }, @{b }NewM@{ub } and @{b }FastNew@{ub } is
|
||||||
|
automatically freed at the end of a program.
|
||||||
|
|
||||||
|
|
||||||
|
@ENDNODE
|
||||||
|
|
||||||
|
@NODE "Pointers and Dereferencing" "Pointers and Dereferencing"
|
||||||
|
@Next "Mathematics Functions"
|
||||||
|
@Prev "Freeing Resources"
|
||||||
|
@Toc "main"
|
||||||
|
|
||||||
|
Pointers and Dereferencing
|
||||||
|
==========================
|
||||||
|
|
||||||
|
C programmers may think that the @{b } ^@{ub }@{fg shine }var@{fg text }@{b }@{ub } and @{b }{@{ub }@{fg shine }var@{fg text } @{b } }@{ub } expressions
|
||||||
|
are the direct equivalent of C's @{b } &@{ub }@{fg shine }var@{fg text }@{b }@{ub } and @{b } *@{ub }@{fg shine }var@{fg text }@{b }@{ub } expressions.
|
||||||
|
However, in E dereferencing is normally achieved using array and object
|
||||||
|
element selection, and pointers to large amounts of data (like E-strings
|
||||||
|
or objects) are made by declarations. This means that the @{b } ^@{ub }@{fg shine }var@{fg text }@{b }@{ub } and
|
||||||
|
@{b }{@{ub }@{fg shine }var@{fg text } @{b } }@{ub } expressions are rarely used, whilst @{b }@{ub }@{fg shine }var@{fg text }@{b }[]@{ub } is very
|
||||||
|
common.
|
||||||
|
|
||||||
|
|
||||||
|
@ENDNODE
|
||||||
|
|
||||||
|
@NODE "Mathematics Functions" "Mathematics Functions"
|
||||||
|
@Next "Signed and Unsigned Values"
|
||||||
|
@Prev "Pointers and Dereferencing"
|
||||||
|
@Toc "main"
|
||||||
|
|
||||||
|
Mathematics Functions
|
||||||
|
=====================
|
||||||
|
|
||||||
|
The standard mathematical operators @{b }/@{ub } and @{b }*@{ub } do not use full 32-bit
|
||||||
|
values in their calculations, as noted previously (see
|
||||||
|
@{"Maths and logic functions" Link "BuiltIns.guide/Maths and logic functions" }). A common problem is to forget this and use
|
||||||
|
them where the values will exceed the 16-bit limit. A typical example is
|
||||||
|
the position calculations used with proportional gadgets. See
|
||||||
|
@{"Signed and Unsigned Values" Link "Signed and Unsigned Values" }.
|
||||||
|
|
||||||
|
|
||||||
|
@ENDNODE
|
||||||
|
|
||||||
|
@NODE "Signed and Unsigned Values" "Signed and Unsigned Values"
|
||||||
|
@Prev "Mathematics Functions"
|
||||||
|
@Toc "main"
|
||||||
|
|
||||||
|
Signed and Unsigned Values
|
||||||
|
==========================
|
||||||
|
|
||||||
|
This is a quite advanced topic, but might be the cause of some strange
|
||||||
|
bugs in your programs. Basically, E does not have a way of
|
||||||
|
differentiating signed and unsigned values from, say, the @{b }LONG@{ub } type. That
|
||||||
|
is, all values from the 32-bit, @{b }LONG@{ub } type are considered to be signed
|
||||||
|
values, so the range of values is from -2,147,483,648 to 2,147,483,647.
|
||||||
|
If the values from this type were taken to be unsigned then no negative
|
||||||
|
values would be allowed but more positive values would be possible (i.e.,
|
||||||
|
the range of values would be from zero to 4,294,967,295). This
|
||||||
|
distinction would also affect the mathematical operators.
|
||||||
|
|
||||||
|
In practice, though, it is not the @{b }LONG@{ub } type that can cause problems.
|
||||||
|
Instead, it is the 16-bit, @{b }INT@{ub } type, which again is considered to be
|
||||||
|
signed. This means that the range of values is -32,768 to 32,767.
|
||||||
|
However, the Amiga system objects contain a number of 16-bit, @{b }INT@{ub } elements
|
||||||
|
which are actually interpreted as unsigned, ranging from zero to 65,535.
|
||||||
|
A prominent example is the proportional gadget which forms a part of a
|
||||||
|
scroll-bar on a window (for example, a drawer window on Workbench). This
|
||||||
|
works with unsigned 16-bit values, which is at odds with the @{b }INT@{ub } type in E.
|
||||||
|
These values are commonly used in calculations to determine the position
|
||||||
|
of something displayed in a window, and if the @{b }INT@{ub } type is used without
|
||||||
|
taking into account this signed/unsigned problem the results can be quite
|
||||||
|
wrong. Luckily it is very simple to convert the signed @{b }INT@{ub } values into
|
||||||
|
unsigned values if they are part of some expression, since the value of
|
||||||
|
any expression is taken from the @{b }LONG@{ub } type (and unsigned @{b }INT@{ub } values fit
|
||||||
|
well within the range of even signed @{b }LONG@{ub } values).
|
||||||
|
|
||||||
|
PROC unsigned_int(x) IS x AND $FFFF
|
||||||
|
|
||||||
|
The function @{b }unsigned_int@{ub }, above, is very specific to the way the Amiga
|
||||||
|
handles values internally, so to understand how it works is beyond the
|
||||||
|
scope of this Guide. It should be used wherever an unsigned 16-bit value
|
||||||
|
is stored in an @{b }INT@{ub } element of, say, an Amiga system object. For example,
|
||||||
|
the position of the top of a (vertical) proportional gadget as a
|
||||||
|
percentage (zero to one hundred) of its size can be calculated like this:
|
||||||
|
|
||||||
|
/* propinfo is from the module 'intuition/intuition' */
|
||||||
|
DEF gad:PTR TO propinfo, pct
|
||||||
|
/* Set up gad... */
|
||||||
|
/* Calculate percentage (MAXPOT is from 'intuition/intuition') */
|
||||||
|
pct:=Div(Mul(100,unsigned_int(gad.vertpot)),MAXPOT)
|
||||||
|
|
||||||
|
Notice that the full 32-bit functions @{b }Div@{ub } and @{b }Mul@{ub } need to be used since
|
||||||
|
the arithmetic may be well over the normal 16-bits used in the @{b }/@{ub } and @{b }*@{ub }
|
||||||
|
operators.
|
||||||
|
|
||||||
|
The remaining type, @{b }CHAR@{ub }, is not, in practice, a problem. It is the
|
||||||
|
only unsigned type, with a range of values from zero to 255. There is a
|
||||||
|
fairly simple way to convert these values to signed values (and again this
|
||||||
|
is particular to the way the Amiga stores values internally). One good
|
||||||
|
example of a signed @{b }CHAR@{ub } value is the priority value associated with a
|
||||||
|
node of an Amiga list (i.e., the @{b }pri@{ub } element of an @{b }ln@{ub } object from the
|
||||||
|
module @{b }exec/nodes@{ub }).
|
||||||
|
|
||||||
|
PROC signed_char(x) IS IF x<128 THEN x ELSE x-256
|
||||||
|
|
||||||
|
|
||||||
|
@ENDNODE
|
||||||
|
|
||||||
|
@NODE "Other Information" "Other Information"
|
||||||
|
@Next "EIndex.guide/main"
|
||||||
|
@Prev "main"
|
||||||
|
@Toc "Contents.guide/main"
|
||||||
|
|
||||||
|
Other Information
|
||||||
|
*****************
|
||||||
|
|
||||||
|
This Appendix contains some useful, miscellaneous information.
|
||||||
|
|
||||||
|
|
||||||
|
@{" Amiga E Versions " Link "Amiga E Versions" }
|
||||||
|
@{" Further Reading " Link "Further Reading" }
|
||||||
|
@{" Amiga E Author " Link "Amiga E Author" }
|
||||||
|
@{" Guide Author " Link "Guide Author" }
|
||||||
|
|
||||||
|
|
||||||
|
@ENDNODE
|
||||||
|
|
||||||
|
@NODE "Amiga E Versions" "Amiga E Versions"
|
||||||
|
@Next "Further Reading"
|
||||||
|
@Toc "Other Information"
|
||||||
|
|
||||||
|
Amiga E Versions
|
||||||
|
================
|
||||||
|
|
||||||
|
As I write, the current version of Amiga E is version 3.2e (which is
|
||||||
|
minor update of v3.2a). This edition of the Guide is based primarily on
|
||||||
|
that version, but the majority still applies to the older versions,
|
||||||
|
including the last Public Domain version (v2.1b). Version 3.3 is
|
||||||
|
imminent, and the new `Reference Manual' will contain details of the new
|
||||||
|
features and changes.
|
||||||
|
|
||||||
|
Please note that, as of v3.0a, Amiga E is a commercial product so you
|
||||||
|
must pay a fee to get a version of the full compiler (which will be
|
||||||
|
registered to you). The Public Domain distribution contains only a
|
||||||
|
demonstration version of the compiler, with limited functionality. See
|
||||||
|
the `Reference Manual' for more details.
|
||||||
|
|
||||||
|
|
||||||
|
@ENDNODE
|
||||||
|
|
||||||
|
@NODE "Further Reading" "Further Reading"
|
||||||
|
@Next "Amiga E Author"
|
||||||
|
@Prev "Amiga E Versions"
|
||||||
|
@Toc "Other Information"
|
||||||
|
|
||||||
|
Further Reading
|
||||||
|
===============
|
||||||
|
|
||||||
|
`Amiga E Language Reference'
|
||||||
|
Referred to as the `Reference Manual' in this Guide. This is one of
|
||||||
|
the documents that comes with the Amiga E package, and is essential
|
||||||
|
reading since it was written by Wouter (the author of Amiga E). It
|
||||||
|
contains a lot of extra information.
|
||||||
|
|
||||||
|
`Rom Kernel Reference Manual' (Addison-Wesley)
|
||||||
|
This is the official Commodore documentation on the Amiga system
|
||||||
|
functions and is a must if you want to use these functions properly.
|
||||||
|
At the time of writing the Third Edition is the most current and it
|
||||||
|
covers the Amiga system functions up to Release 2 (i.e., AmigaDOS
|
||||||
|
2.04 and KickStart 37). Because there is so much information it
|
||||||
|
comes in three separate volumes: `Libraries', `Includes and
|
||||||
|
Autodocs', and `Devices'. The `Libraries' volume is probably the
|
||||||
|
most useful as it contains many examples and a lot of tutorial
|
||||||
|
material. However, the examples are written mainly in C (the
|
||||||
|
remainder are in Assembly). To alleviate this problem I have
|
||||||
|
undertaken to re-code them in E, and Part One and Part Two of this
|
||||||
|
effort should be available from Aminet or good PD houses (the archive
|
||||||
|
names will be something like @{b }JRH-RKRM-1@{ub } and @{b }JRH-RKRM-2@{ub }).
|
||||||
|
(Unfortunately, it seems that the actual manuals may be hard to find
|
||||||
|
since there are now out-of-print.)
|
||||||
|
|
||||||
|
`The AmigaDOS Manual' (Bantam Books)
|
||||||
|
This is the companion to the `Rom Kernel Reference Manual' and is the
|
||||||
|
official Commodore book on AmigaDOS (both the AmigaDOS programs and
|
||||||
|
the DOS library functions). Again, the Third Edition is the most
|
||||||
|
current.
|
||||||
|
|
||||||
|
Example sources
|
||||||
|
Amiga E comes with a large collection of example programs. When
|
||||||
|
you're familiar with the language you should be able to learn quite a
|
||||||
|
bit from these. There are a lot of small, tutorial programs and a
|
||||||
|
few large, complicated programs.
|
||||||
|
|
||||||
|
|
||||||
|
@ENDNODE
|
||||||
|
|
||||||
|
@NODE "Amiga E Author" "Amiga E Author"
|
||||||
|
@Next "Guide Author"
|
||||||
|
@Prev "Further Reading"
|
||||||
|
@Toc "Other Information"
|
||||||
|
|
||||||
|
Amiga E Author
|
||||||
|
==============
|
||||||
|
|
||||||
|
In case you didn't know the author and creator of Amiga E is Wouter van
|
||||||
|
Oortmerssen (or @{b }$#%!@{ub }). You can reach him by normal mail at the following
|
||||||
|
address:
|
||||||
|
|
||||||
|
Wouter van Oortmerssen (@{b }$#%!@{ub })
|
||||||
|
Levendaal 87
|
||||||
|
2311 JG Leiden
|
||||||
|
HOLLAND
|
||||||
|
|
||||||
|
However, he much prefers to chat by E-mail, and you can reach him at the
|
||||||
|
following addresses:
|
||||||
|
|
||||||
|
@{b }Wouter@alf.let.uva.nl@{ub } (E-programming support)
|
||||||
|
@{b }Wouter@mars.let.uva.nl@{ub } (personal)
|
||||||
|
@{b }Oortmers@gene.fwi.uva.nl@{ub } (other)
|
||||||
|
|
||||||
|
Better still, if your problem or enquiry is of general interest to Amiga E
|
||||||
|
users you may find it useful joining the Amiga E mailing list. Wouter
|
||||||
|
regularly contributes to this list and there are a number of good
|
||||||
|
programmers who are at hand to help or discuss problems. To join send a
|
||||||
|
message to:
|
||||||
|
|
||||||
|
@{b }amigae-request@bkhouse.cts.com@{ub }
|
||||||
|
|
||||||
|
Once you're subscribed, you will receive a copy of each message mailed to
|
||||||
|
the list. You will also receive a message telling you how you can
|
||||||
|
contribute (i.e., ask questions!).
|
||||||
|
|
||||||
|
|
||||||
|
@ENDNODE
|
||||||
|
|
||||||
|
@NODE "Guide Author" "Guide Author"
|
||||||
|
@Prev "Amiga E Author"
|
||||||
|
@Toc "Other Information"
|
||||||
|
|
||||||
|
Guide Author
|
||||||
|
============
|
||||||
|
|
||||||
|
This Guide was written by Jason Hulance, with a lot of help and
|
||||||
|
guidance from Wouter. The original aim was to produce something that
|
||||||
|
might be a useful introduction to Amiga E for beginners, so that the
|
||||||
|
language could (rightly) become more widespread. The hidden agenda was to
|
||||||
|
free Wouter from such a task so that he could concentrate his efforts on
|
||||||
|
improving Amiga E.
|
||||||
|
|
||||||
|
You can reach me by normal mail most easily at the following (work)
|
||||||
|
address:
|
||||||
|
|
||||||
|
Jason R. Hulance
|
||||||
|
Formal Systems (Europe) Ltd.
|
||||||
|
3 Alfred Street
|
||||||
|
Oxford
|
||||||
|
OX1 4EH
|
||||||
|
ENGLAND
|
||||||
|
|
||||||
|
Alternatively, you can find me on the Amiga E mailing list, or E-mail me
|
||||||
|
directly at one of the following addresses:
|
||||||
|
|
||||||
|
@{b }jason@fsel.com@{ub }
|
||||||
|
@{b }m88jrh@ecs.oxford.ac.uk@{ub }
|
||||||
|
|
||||||
|
If you have any changes or additions you'd like to see then I'd be very
|
||||||
|
happy to consider them. Criticism of the text is also welcome, especially
|
||||||
|
if you can suggest a better way of explaining things. I am also keen to
|
||||||
|
hear from people who can highlight areas that are particularly confusing
|
||||||
|
or badly worded!
|
||||||
|
|
||||||
|
|
||||||
|
@ENDNODE
|
||||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,254 @@
|
||||||
|
@database beginner.guide
|
||||||
|
|
||||||
|
@Master beginner
|
||||||
|
|
||||||
|
@Width 75
|
||||||
|
|
||||||
|
|
||||||
|
This is the AmigaGuide® file beginner.guide, produced by Makeinfo-1.55 from
|
||||||
|
the input file beginner.
|
||||||
|
|
||||||
|
|
||||||
|
@NODE "main" "Constants"
|
||||||
|
@Next "Types.guide/main"
|
||||||
|
@Prev "Procedures.guide/main"
|
||||||
|
@Toc "Contents.guide/main"
|
||||||
|
|
||||||
|
Constants
|
||||||
|
*********
|
||||||
|
|
||||||
|
A @{fg shine }constant@{fg text } is a value that does not change. A (literal) number like
|
||||||
|
121 is a good example of a constant--its value is always 121. We've
|
||||||
|
already met another kind of constant: string constants (see @{"Strings" Link "Introduction.guide/Strings" }). As
|
||||||
|
you can doubtless tell, constants are pretty important things.
|
||||||
|
|
||||||
|
|
||||||
|
@{" Numeric Constants " Link "Numeric Constants" }
|
||||||
|
@{" String Constants Special Character Sequences " Link "String Constants Special Character Sequences" }
|
||||||
|
@{" Named Constants " Link "Named Constants" }
|
||||||
|
@{" Enumerations " Link "Enumerations" }
|
||||||
|
@{" Sets " Link "Sets" }
|
||||||
|
|
||||||
|
|
||||||
|
@ENDNODE
|
||||||
|
|
||||||
|
@NODE "Numeric Constants" "Numeric Constants"
|
||||||
|
@Next "String Constants Special Character Sequences"
|
||||||
|
@Toc "main"
|
||||||
|
|
||||||
|
Numeric Constants
|
||||||
|
=================
|
||||||
|
|
||||||
|
We've met a lot of numbers in the previous examples. Technically
|
||||||
|
speaking, these were numeric constants (constant because they don't change
|
||||||
|
value like a variable might). They were all decimal numbers, but you can
|
||||||
|
use hexadecimal and binary numbers as well. There's also a way of
|
||||||
|
specifying a number using characters. To specify a hexadecimal number you
|
||||||
|
use a @{b }$@{ub } before the digits (and after the optional minus sign @{b }-@{ub } to
|
||||||
|
represent a negative value). To specify a binary number you use a @{b }%@{ub }
|
||||||
|
instead.
|
||||||
|
|
||||||
|
Specifying numbers using characters is more complicated, because the
|
||||||
|
base of this system is 256 (the base of decimal is ten, that of
|
||||||
|
hexadecimal is 16 and that of binary is two). The digits are enclosed in
|
||||||
|
double-quotes (the " character), and there can be at most four digits.
|
||||||
|
Each digit is a character representing its ASCII value. Therefore, the
|
||||||
|
character @{b }A@{ub } represents 65 and the character @{b }0@{ub } (zero) represents 48. This
|
||||||
|
upshot of this is that character @{b }A@{ub } has ASCII value @{b }"A"@{ub } in E, and @{b }"0z"@{ub }
|
||||||
|
represents ("0" * 256) + "z" = (48 * 256) + 122 = 12,410. However, you
|
||||||
|
probably don't need to worry about anything other than the single
|
||||||
|
character case, which gives you the ASCII value of the character.
|
||||||
|
|
||||||
|
The following table shows the decimal value of several numeric
|
||||||
|
constants. Notice that you can use upper- or lower-case letters for the
|
||||||
|
hexadecimal constants. Obviously the case of characters is significant
|
||||||
|
for character numbers.
|
||||||
|
|
||||||
|
@{i }Number@{ui } @{i }Decimal value@{ui }
|
||||||
|
----------------------
|
||||||
|
21 21
|
||||||
|
-143 -143
|
||||||
|
$1a 26
|
||||||
|
-$B1 -177
|
||||||
|
%1110 14
|
||||||
|
-%1010 -10
|
||||||
|
"z" 122
|
||||||
|
"Je" 19,045
|
||||||
|
-"A" -65
|
||||||
|
|
||||||
|
|
||||||
|
@ENDNODE
|
||||||
|
|
||||||
|
@NODE "String Constants Special Character Sequences" "String Constants Special Character Sequences"
|
||||||
|
@Next "Named Constants"
|
||||||
|
@Prev "Numeric Constants"
|
||||||
|
@Toc "main"
|
||||||
|
|
||||||
|
String Constants: Special Character Sequences
|
||||||
|
=============================================
|
||||||
|
|
||||||
|
We have seen that in a string the character sequence @{b }\\n@{ub } means a
|
||||||
|
linefeed (see @{"Strings" Link "Introduction.guide/Strings" }). There are several other similar such special
|
||||||
|
character sequences which represent useful characters that can't be typed
|
||||||
|
in a string. The following table shows all these sequences. Note that
|
||||||
|
there are some other similar sequences which are used to control
|
||||||
|
formatting with built-in procedures like @{b }WriteF@{ub }. These are listed where
|
||||||
|
@{b }WriteF@{ub } and similar procedures are described (see
|
||||||
|
@{"Input and output functions" Link "BuiltIns.guide/Input and output functions" }).
|
||||||
|
|
||||||
|
@{i }Sequence@{ui } @{i }Meaning@{ui }
|
||||||
|
--------------------------------------
|
||||||
|
\\0 A null (ASCII zero)
|
||||||
|
\\a An apostrophe '
|
||||||
|
\\b A carriage return (ASCII 13)
|
||||||
|
\\e An escape (ASCII 27)
|
||||||
|
\\n A linefeed (ASCII 10)
|
||||||
|
\\q A double quote (ASCII 34)
|
||||||
|
\\t A tab (ASCII 9)
|
||||||
|
\\\\ A backslash \\
|
||||||
|
|
||||||
|
An apostrophe can also be produced by typing two apostrophes in a row in a
|
||||||
|
string. It's best to use this only in the middle of a string, where it's
|
||||||
|
nice and obvious:
|
||||||
|
|
||||||
|
WriteF('Here\\as an apostrophe.\\n') /* Using \\a */
|
||||||
|
|
||||||
|
WriteF('Here''s another apostrophe.\\n') /* Using '' */
|
||||||
|
|
||||||
|
|
||||||
|
@ENDNODE
|
||||||
|
|
||||||
|
@NODE "Named Constants" "Named Constants"
|
||||||
|
@Next "Enumerations"
|
||||||
|
@Prev "String Constants Special Character Sequences"
|
||||||
|
@Toc "main"
|
||||||
|
|
||||||
|
Named Constants
|
||||||
|
===============
|
||||||
|
|
||||||
|
It is often nice to be able to give names to certain constants. For
|
||||||
|
instance, as we saw earlier, the truth value @{b }TRUE@{ub } actually represents the
|
||||||
|
value -1, and @{b }FALSE@{ub } represents zero (see @{"Logic and comparison" Link "Introduction.guide/Logic and comparison" }). These are
|
||||||
|
our first examples of named constants. To define your own you use the
|
||||||
|
@{b }CONST@{ub } keyword as follows:
|
||||||
|
|
||||||
|
CONST ONE=1, LINEFEED=10, BIG_NUM=999999
|
||||||
|
|
||||||
|
This has defined the constant @{b }ONE@{ub } to represent one, @{b }LINEFEED@{ub } ten and
|
||||||
|
@{b }BIG_NUM@{ub } 999,999. Named constants must begin with two uppercase letters,
|
||||||
|
as mentioned before (see @{"Identifiers" Link "Format.guide/Identifiers" }).
|
||||||
|
|
||||||
|
You can use previously defined constants to give the value of a new
|
||||||
|
constant, but in this case the definitions must occur on different @{b }CONST@{ub }
|
||||||
|
lines.
|
||||||
|
|
||||||
|
CONST ZERO=0
|
||||||
|
CONST ONE=ZERO+1
|
||||||
|
CONST TWO=ONE+1
|
||||||
|
|
||||||
|
The expression used to define the value of a constant can use only simple
|
||||||
|
operators (no function calls) and constants.
|
||||||
|
|
||||||
|
|
||||||
|
@ENDNODE
|
||||||
|
|
||||||
|
@NODE "Enumerations" "Enumerations"
|
||||||
|
@Next "Sets"
|
||||||
|
@Prev "Named Constants"
|
||||||
|
@Toc "main"
|
||||||
|
|
||||||
|
Enumerations
|
||||||
|
============
|
||||||
|
|
||||||
|
Often you want to define a whole lot of constants and you just want
|
||||||
|
them all to have a different value so you can tell them apart easily. For
|
||||||
|
instance, if you wanted to define some constants to represent some famous
|
||||||
|
cities and you only needed to know how to distinguish one from another
|
||||||
|
then you could use an @{fg shine }enumeration@{fg text } like this:
|
||||||
|
|
||||||
|
ENUM LONDON, MOSCOW, NEW_YORK, PARIS, ROME, TOKYO
|
||||||
|
|
||||||
|
The @{b }ENUM@{ub } keyword begins the definitions (like the @{b }CONST@{ub } keyword does for
|
||||||
|
an ordinary constant definition). The actual values of the constants
|
||||||
|
start at zero and stretch up to five. In fact, this is exactly the same
|
||||||
|
as writing:
|
||||||
|
|
||||||
|
CONST LONDON=0, MOSCOW=1, NEW_YORK=2, PARIS=3, ROME=4, TOKYO=5
|
||||||
|
|
||||||
|
The enumeration does not have to start at zero, though. You can change
|
||||||
|
the starting value at any point by specifying a value for an enumerated
|
||||||
|
constant. For example, the following constant definitions are equivalent:
|
||||||
|
|
||||||
|
ENUM APPLE, ORANGE, CAT=55, DOG, GOLDFISH, FRED=-2,
|
||||||
|
BARNEY, WILMA, BETTY
|
||||||
|
|
||||||
|
CONST APPLE=0, ORANGE=1, CAT=55, DOG=56, GOLDFISH=57,
|
||||||
|
FRED=-2, BARNEY=-1, WILMA=0, BETTY=1
|
||||||
|
|
||||||
|
|
||||||
|
@ENDNODE
|
||||||
|
|
||||||
|
@NODE "Sets" "Sets"
|
||||||
|
@Prev "Enumerations"
|
||||||
|
@Toc "main"
|
||||||
|
|
||||||
|
Sets
|
||||||
|
====
|
||||||
|
|
||||||
|
Yet another kind of constant definition is the @{fg shine }set@{fg text } definition. This
|
||||||
|
useful for defining flag sets, i.e., a number of options each of which can
|
||||||
|
be on or off. The definition is like a simple enumeration, but using the
|
||||||
|
@{b }SET@{ub } keyword and this time the values start at one and increase as powers
|
||||||
|
of two (so the next value is two, the next is four, the next eight, and so
|
||||||
|
on). Therefore, the following definitions are equivalent:
|
||||||
|
|
||||||
|
SET ENGLISH, FRENCH, GERMAN, JAPANESE, RUSSIAN
|
||||||
|
|
||||||
|
CONST ENGLISH=1, FRENCH=2, GERMAN=4, JAPANESE=8, RUSSIAN=16
|
||||||
|
|
||||||
|
However, the significance of the values it is best shown by using binary
|
||||||
|
constants:
|
||||||
|
|
||||||
|
CONST ENGLISH=%00001, FRENCH=%00010, GERMAN=%00100,
|
||||||
|
JAPANESE=%01000, RUSSIAN=%10000
|
||||||
|
|
||||||
|
If a person speaks just English then we can use the constant @{b }ENGLISH@{ub }. If
|
||||||
|
they also spoke Japanese then to represent this with a single value we'd
|
||||||
|
normally need a new constant (something like @{b }ENG_JAP@{ub }). In fact, we'd
|
||||||
|
probably need a constant for each combination of languages a person might
|
||||||
|
know. However, with the set definition we can @{b }OR@{ub } the @{b }ENGLISH@{ub } and @{b }JAPANESE@{ub }
|
||||||
|
values together to get a new value, @{b }%01001@{ub }, and this represents a set
|
||||||
|
containing both @{b }ENGLISH@{ub } and @{b }JAPANESE@{ub }. On the other hand, to find out if
|
||||||
|
someone speaks French we would @{b }AND@{ub } the value for the languages they know
|
||||||
|
with @{b }%00010@{ub } (or the constant @{b }FRENCH@{ub }). (As you might have guessed, @{b }AND@{ub } and
|
||||||
|
@{b }OR@{ub } are really bit-wise operators, not simply logical operators. See
|
||||||
|
@{"Bitwise AND and OR" Link "MoreExpressions.guide/Bitwise AND and OR" }.)
|
||||||
|
|
||||||
|
Consider this program fragment:
|
||||||
|
|
||||||
|
speak:=GERMAN OR ENGLISH OR RUSSIAN /* Speak any of these */
|
||||||
|
IF speak AND JAPANESE
|
||||||
|
WriteF('Can speak in Japanese\\n')
|
||||||
|
ELSE
|
||||||
|
WriteF('Unable to speak in Japanese\\n')
|
||||||
|
ENDIF
|
||||||
|
IF speak AND (GERMAN OR FRENCH)
|
||||||
|
WriteF('Can speak in German or French\\n')
|
||||||
|
ELSE
|
||||||
|
WriteF('Unable to speak in German or French\\n')
|
||||||
|
ENDIF
|
||||||
|
|
||||||
|
The assignment sets @{b }speak@{ub } to show that the person can speak in German,
|
||||||
|
English or Russian. The first @{b }IF@{ub } block tests whether the person can speak
|
||||||
|
in Japanese, and the second tests whether they can speak in German or
|
||||||
|
French.
|
||||||
|
|
||||||
|
When using sets be careful you don't get tempted to add values instead
|
||||||
|
of @{b }OR@{ub }-ing them. Adding two different constants from the same set is the
|
||||||
|
same as @{b }OR@{ub }-ing them, but adding the same set constant to itself isn't.
|
||||||
|
This is not the only time addition doesn't give the same answer, but it's
|
||||||
|
the most obvious. If you to stick to using @{b }OR@{ub } you won't have a problem.
|
||||||
|
|
||||||
|
|
||||||
|
@ENDNODE
|
||||||
|
|
|
@ -0,0 +1,69 @@
|
||||||
|
@database beginner.guide
|
||||||
|
|
||||||
|
@Master beginner
|
||||||
|
|
||||||
|
@Width 75
|
||||||
|
|
||||||
|
|
||||||
|
This is the AmigaGuide® file beginner.guide, produced by Makeinfo-1.55 from
|
||||||
|
the input file beginner.
|
||||||
|
|
||||||
|
|
||||||
|
@NODE "main" "beginner.guide"
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
Copyright (c) 1994-1997, Jason R. Hulance.
|
||||||
|
|
||||||
|
A Beginner's Guide to Amiga E
|
||||||
|
*****************************
|
||||||
|
|
||||||
|
This Guide gives an introduction to the Amiga E programming language
|
||||||
|
and, to some extent, programming in general.
|
||||||
|
|
||||||
|
|
||||||
|
Part One: Getting Started
|
||||||
|
|
||||||
|
@{" Introduction to Amiga E " Link "Introduction.guide/main" }
|
||||||
|
@{" Understanding a Simple Program " Link "Introduction.guide/Understanding a Simple Program" }
|
||||||
|
@{" Variables and Expressions " Link "Introduction.guide/Variables and Expressions" }
|
||||||
|
@{" Program Flow Control " Link "Introduction.guide/Program Flow Control" }
|
||||||
|
@{" Summary " Link "Introduction.guide/Summary" }
|
||||||
|
|
||||||
|
Part Two: The E Language
|
||||||
|
|
||||||
|
@{" Format and Layout " Link "Format.guide/main" }
|
||||||
|
@{" Procedures and Functions " Link "Procedures.guide/main" }
|
||||||
|
@{" Constants " Link "Constants.guide/main" }
|
||||||
|
@{" Types " Link "Types.guide/main" }
|
||||||
|
@{" More About Statements and Expressions " Link "MoreExpressions.guide/main" }
|
||||||
|
@{" E Built-In Constants Variables and Functions " Link "BuiltIns.guide/main" }
|
||||||
|
@{" Modules " Link "Modules.guide/main" }
|
||||||
|
@{" Exception Handling " Link "Exceptions.guide/main" }
|
||||||
|
@{" Memory Allocation " Link "Memory.guide/main" }
|
||||||
|
@{" Floating-Point Numbers " Link "FloatingPoint.guide/main" }
|
||||||
|
@{" Recursion " Link "Recursion.guide/main" }
|
||||||
|
@{" Object Oriented E " Link "OOE.guide/main" }
|
||||||
|
|
||||||
|
Part Three: Worked Examples
|
||||||
|
|
||||||
|
@{" Introduction to the Examples " Link "Examples.guide/main" }
|
||||||
|
@{" String Handling and I-O " Link "Examples.guide/String Handling and I-O" }
|
||||||
|
@{" Timing Expressions " Link "Examples.guide/Timing Expressions" }
|
||||||
|
@{" Argument Parsing " Link "Examples.guide/Argument Parsing" }
|
||||||
|
@{" Gadgets IDCMP and Graphics " Link "Examples.guide/Gadgets IDCMP and Graphics" }
|
||||||
|
@{" Recursion Example " Link "Examples.guide/Recursion Example" }
|
||||||
|
|
||||||
|
Part Four: Appendices
|
||||||
|
|
||||||
|
@{" Common Problems " Link "Appendices.guide/main" }
|
||||||
|
@{" Other Information " Link "Appendices.guide/Other Information" }
|
||||||
|
|
||||||
|
Indices
|
||||||
|
|
||||||
|
@{" E Language Index " Link "EIndex.guide/main" }
|
||||||
|
@{" Main Index " Link "Index.guide/main" }
|
||||||
|
|
||||||
|
|
||||||
|
@ENDNODE
|
||||||
|
|
Binary file not shown.
|
@ -0,0 +1,294 @@
|
||||||
|
@database beginner.guide
|
||||||
|
|
||||||
|
@Master beginner
|
||||||
|
|
||||||
|
@Width 75
|
||||||
|
|
||||||
|
|
||||||
|
This is the AmigaGuide® file beginner.guide, produced by Makeinfo-1.55 from
|
||||||
|
the input file beginner.
|
||||||
|
|
||||||
|
|
||||||
|
@NODE "main" "E Language Index"
|
||||||
|
@Next "Index.guide/main"
|
||||||
|
@Prev "Appendices.guide/Other Information"
|
||||||
|
@Toc "Contents.guide/main"
|
||||||
|
|
||||||
|
E Language Index
|
||||||
|
****************
|
||||||
|
|
||||||
|
This index should be used to find detailed information about the
|
||||||
|
keywords, functions, variables and constants which are part of the Amiga E
|
||||||
|
language. There is a separate index which deals with concepts etc. (see
|
||||||
|
@{"Main Index" Link "Index.guide/main" }).
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
@{" Symbol, close curly brace " Link "Types.guide/Finding addresses (making pointers)" 0 } Finding addresses (making pointers)
|
||||||
|
@{" Symbol, double-quote " Link "Constants.guide/Numeric Constants" 11 } Numeric Constants
|
||||||
|
@{" Symbol, open curly brace " Link "Types.guide/Finding addresses (making pointers)" 0 } Finding addresses (making pointers)
|
||||||
|
@{" Symbol, ! " Link "FloatingPoint.guide/Floating-Point Calculations" 0 } Floating-Point Calculations
|
||||||
|
@{" Symbol, $ " Link "Constants.guide/Numeric Constants" 0 } Numeric Constants
|
||||||
|
@{" Symbol, % " Link "Constants.guide/Numeric Constants" 0 } Numeric Constants
|
||||||
|
@{" Symbol, ' .. ' (string) " Link "Types.guide/Normal strings and E-strings" 0 } Normal strings and E-strings
|
||||||
|
@{" Symbol, * " Link "Introduction.guide/Mathematics" 0 } Mathematics
|
||||||
|
@{" Symbol, + " Link "Introduction.guide/Mathematics" 0 } Mathematics
|
||||||
|
@{" Symbol, + (strings) " Link "Format.guide/Statements" 58 } Statements
|
||||||
|
@{" Symbol, ++ " Link "Types.guide/Point to other elements" 0 } Point to other elements
|
||||||
|
@{" Symbol, - " Link "Introduction.guide/Mathematics" 0 } Mathematics
|
||||||
|
@{" Symbol, -- " Link "Types.guide/Point to other elements" 0 } Point to other elements
|
||||||
|
@{" Symbol, -> " Link "Format.guide/Comments" 0 } Comments
|
||||||
|
@{" Symbol, / " Link "Introduction.guide/Mathematics" 0 } Mathematics
|
||||||
|
@{" Symbol, /* .. */ " Link "Format.guide/Comments" 0 } Comments
|
||||||
|
@{" Symbol, : " Link "MoreExpressions.guide/Labelling and the JUMP statement" 0 } Labelling and the JUMP statement
|
||||||
|
@{" Symbol, := " Link "Introduction.guide/Assignment" 0 } Assignment
|
||||||
|
@{" Symbol, ; " Link "Format.guide/Statements" 18 } Statements
|
||||||
|
@{" Symbol, < " Link "Introduction.guide/Logic and comparison" 11 } Logic and comparison
|
||||||
|
@{" Symbol, <= " Link "Introduction.guide/Logic and comparison" 11 } Logic and comparison
|
||||||
|
@{" Symbol, <=> " Link "MoreExpressions.guide/Unification" 0 } Unification
|
||||||
|
@{" Symbol, <> " Link "Introduction.guide/Logic and comparison" 11 } Logic and comparison
|
||||||
|
@{" Symbol, = " Link "Introduction.guide/Logic and comparison" 11 } Logic and comparison
|
||||||
|
@{" Symbol, > " Link "Introduction.guide/Logic and comparison" 11 } Logic and comparison
|
||||||
|
@{" Symbol, >= " Link "Introduction.guide/Logic and comparison" 11 } Logic and comparison
|
||||||
|
@{" Symbol, [ .. , .. ] (list) " Link "Types.guide/Lists and E-lists" 12 } Lists and E-lists
|
||||||
|
@{" Symbol, [ .. , .. ]:type (typed list) " Link "Types.guide/Typed lists" 0 } Typed lists
|
||||||
|
@{" Symbol, [ .. ] (array) " Link "Types.guide/Tables of data" 0 } Tables of data
|
||||||
|
@{" Symbol, [] (array) " Link "Types.guide/Accessing array data" 55 } Accessing array data
|
||||||
|
@{" Symbol, \0 " Link "Constants.guide/String Constants Special Character Sequences" 0 } String Constants Special Character Sequences
|
||||||
|
@{" Symbol, \a " Link "Constants.guide/String Constants Special Character Sequences" 0 } String Constants Special Character Sequences
|
||||||
|
@{" Symbol, \b " Link "Constants.guide/String Constants Special Character Sequences" 0 } String Constants Special Character Sequences
|
||||||
|
@{" Symbol, \c " Link "BuiltIns.guide/Input and output functions" 16 } Input and output functions
|
||||||
|
@{" Symbol, \d " Link "BuiltIns.guide/Input and output functions" 16 } Input and output functions
|
||||||
|
@{" Symbol, \d " Link "Introduction.guide/Changing the example" 0 } Changing the example
|
||||||
|
@{" Symbol, \e " Link "Constants.guide/String Constants Special Character Sequences" 0 } String Constants Special Character Sequences
|
||||||
|
@{" Symbol, \h " Link "BuiltIns.guide/Input and output functions" 16 } Input and output functions
|
||||||
|
@{" Symbol, \l " Link "BuiltIns.guide/Input and output functions" 54 } Input and output functions
|
||||||
|
@{" Symbol, \n " Link "Constants.guide/String Constants Special Character Sequences" 0 } String Constants Special Character Sequences
|
||||||
|
@{" Symbol, \n " Link "Introduction.guide/Strings" 0 } Strings
|
||||||
|
@{" Symbol, \q " Link "Constants.guide/String Constants Special Character Sequences" 0 } String Constants Special Character Sequences
|
||||||
|
@{" Symbol, \r " Link "BuiltIns.guide/Input and output functions" 54 } Input and output functions
|
||||||
|
@{" Symbol, \s " Link "BuiltIns.guide/Input and output functions" 16 } Input and output functions
|
||||||
|
@{" Symbol, \t " Link "Constants.guide/String Constants Special Character Sequences" 0 } String Constants Special Character Sequences
|
||||||
|
@{" Symbol, \z " Link "BuiltIns.guide/Input and output functions" 54 } Input and output functions
|
||||||
|
@{" Symbol, \\ " Link "Constants.guide/String Constants Special Character Sequences" 0 } String Constants Special Character Sequences
|
||||||
|
@{" Symbol, ^ " Link "Types.guide/Extracting data (dereferencing pointers)" 0 } Extracting data (dereferencing pointers)
|
||||||
|
@{" Symbol, ` (backquote) " Link "MoreExpressions.guide/Quoted Expressions" 0 } Quoted Expressions
|
||||||
|
@{" Abs " Link "BuiltIns.guide/Maths and logic functions" 30 } Maths and logic functions
|
||||||
|
@{" ALL " Link "BuiltIns.guide/Built-In Constants" 12 } Built-In Constants
|
||||||
|
@{" And " Link "BuiltIns.guide/Maths and logic functions" 14 } Maths and logic functions
|
||||||
|
@{" AND " Link "MoreExpressions.guide/Bitwise AND and OR" 0 } Bitwise AND and OR
|
||||||
|
@{" arg " Link "BuiltIns.guide/Built-In Variables" 7 } Built-In Variables
|
||||||
|
@{" ARRAY " Link "Types.guide/Tables of data" 0 } Tables of data
|
||||||
|
@{" ARRAY OF type " Link "Types.guide/Tables of data" 0 } Tables of data
|
||||||
|
@{" Bounds " Link "BuiltIns.guide/Maths and logic functions" 55 } Maths and logic functions
|
||||||
|
@{" Box " Link "BuiltIns.guide/Graphics functions" 30 } Graphics functions
|
||||||
|
@{" BUT " Link "MoreExpressions.guide/BUT expression" 0 } BUT expression
|
||||||
|
@{" CASE " Link "Introduction.guide/SELECT..OF block" 0 } SELECT..OF block
|
||||||
|
@{" CASE " Link "Introduction.guide/SELECT block" 0 } SELECT block
|
||||||
|
@{" CASE ..TO.. " Link "Introduction.guide/SELECT..OF block" 0 } SELECT..OF block
|
||||||
|
@{" CHAR " Link "Types.guide/Indirect types" 0 } Indirect types
|
||||||
|
@{" Char " Link "BuiltIns.guide/Maths and logic functions" 116 } Maths and logic functions
|
||||||
|
@{" CHAR " Link "MoreExpressions.guide/Static memory" 0 } Static memory
|
||||||
|
@{" CleanUp " Link "BuiltIns.guide/System support functions" 65 } System support functions
|
||||||
|
@{" CloseS " Link "BuiltIns.guide/Intuition support functions" 178 } Intuition support functions
|
||||||
|
@{" CloseW " Link "BuiltIns.guide/Intuition support functions" 122 } Intuition support functions
|
||||||
|
@{" Colour " Link "BuiltIns.guide/Graphics functions" 35 } Graphics functions
|
||||||
|
@{" conout " Link "BuiltIns.guide/Built-In Variables" 34 } Built-In Variables
|
||||||
|
@{" CONST " Link "Constants.guide/Named Constants" 0 } Named Constants
|
||||||
|
@{" CtrlC " Link "BuiltIns.guide/System support functions" 74 } System support functions
|
||||||
|
@{" DEC " Link "MoreExpressions.guide/INC and DEC statements" 0 } INC and DEC statements
|
||||||
|
@{" DEF " Link "Introduction.guide/Variable declaration" 0 } Variable declaration
|
||||||
|
@{" DEFAULT " Link "Introduction.guide/SELECT..OF block" 0 } SELECT..OF block
|
||||||
|
@{" DEFAULT " Link "Introduction.guide/SELECT block" 0 } SELECT block
|
||||||
|
@{" Dispose " Link "BuiltIns.guide/System support functions" 39 } System support functions
|
||||||
|
@{" DisposeLink " Link "BuiltIns.guide/System support functions" 44 } System support functions
|
||||||
|
@{" Div " Link "BuiltIns.guide/Maths and logic functions" 0 } Maths and logic functions
|
||||||
|
@{" DO, (FOR loop) " Link "Introduction.guide/FOR loop" 55 } FOR loop
|
||||||
|
@{" DO, (WHILE loop) " Link "Introduction.guide/WHILE loop" 57 } WHILE loop
|
||||||
|
@{" dosbase " Link "BuiltIns.guide/Built-In Variables" 51 } Built-In Variables
|
||||||
|
@{" ELSE " Link "Introduction.guide/IF block" 0 } IF block
|
||||||
|
@{" ELSEIF " Link "Introduction.guide/IF block" 0 } IF block
|
||||||
|
@{" EMPTY " Link "OOE.guide/Inheritance in E" 275 } Inheritance in E
|
||||||
|
@{" end " Link "OOE.guide/Methods in E" 118 } Methods in E
|
||||||
|
@{" END " Link "Memory.guide/NEW and END Operators" 0 } NEW and END Operators
|
||||||
|
@{" ENDFOR " Link "Introduction.guide/FOR loop" 0 } FOR loop
|
||||||
|
@{" ENDIF " Link "Introduction.guide/IF block" 0 } IF block
|
||||||
|
@{" ENDLOOP " Link "MoreExpressions.guide/LOOP block" 0 } LOOP block
|
||||||
|
@{" ENDOBJECT " Link "Types.guide/Example object" 23 } Example object
|
||||||
|
@{" ENDPROC " Link "Introduction.guide/Procedure Definition" 0 } Procedure Definition
|
||||||
|
@{" ENDPROC value " Link "Procedures.guide/Functions" 23 } Functions
|
||||||
|
@{" ENDSELECT " Link "Introduction.guide/SELECT..OF block" 0 } SELECT..OF block
|
||||||
|
@{" ENDSELECT " Link "Introduction.guide/SELECT block" 0 } SELECT block
|
||||||
|
@{" ENDWHILE " Link "Introduction.guide/WHILE loop" 0 } WHILE loop
|
||||||
|
@{" ENUM " Link "Constants.guide/Enumerations" 0 } Enumerations
|
||||||
|
@{" Eor " Link "BuiltIns.guide/Maths and logic functions" 14 } Maths and logic functions
|
||||||
|
@{" EstrLen " Link "Types.guide/String functions" 125 } String functions
|
||||||
|
@{" Eval " Link "MoreExpressions.guide/Evaluation" 0 } Evaluation
|
||||||
|
@{" Even " Link "BuiltIns.guide/Maths and logic functions" 41 } Maths and logic functions
|
||||||
|
@{" EXCEPT " Link "Exceptions.guide/Procedures with Exception Handlers" 0 } Procedures with Exception Handlers
|
||||||
|
@{" EXCEPT DO " Link "Exceptions.guide/Raising an Exception" 73 } Raising an Exception
|
||||||
|
@{" exception " Link "Exceptions.guide/Raising an Exception" 36 } Raising an Exception
|
||||||
|
@{" exceptioninfo " Link "Exceptions.guide/Raising an Exception" 50 } Raising an Exception
|
||||||
|
@{" execbase " Link "BuiltIns.guide/Built-In Variables" 51 } Built-In Variables
|
||||||
|
@{" Exists " Link "MoreExpressions.guide/Lists and quoted expressions" 46 } Lists and quoted expressions
|
||||||
|
@{" EXIT " Link "MoreExpressions.guide/EXIT statement" 0 } EXIT statement
|
||||||
|
@{" Fabs " Link "FloatingPoint.guide/Floating-Point Functions" 55 } Floating-Point Functions
|
||||||
|
@{" FALSE " Link "Introduction.guide/Logic and comparison" 11 } Logic and comparison
|
||||||
|
@{" FALSE " Link "BuiltIns.guide/Built-In Constants" 5 } Built-In Constants
|
||||||
|
@{" FastDispose " Link "BuiltIns.guide/System support functions" 59 } System support functions
|
||||||
|
@{" FastDisposeList " Link "Memory.guide/List and typed list allocation" 66 } List and typed list allocation
|
||||||
|
@{" FastNew " Link "BuiltIns.guide/System support functions" 50 } System support functions
|
||||||
|
@{" Fceil " Link "FloatingPoint.guide/Floating-Point Functions" 59 } Floating-Point Functions
|
||||||
|
@{" Fcos " Link "FloatingPoint.guide/Floating-Point Functions" 51 } Floating-Point Functions
|
||||||
|
@{" Fexp " Link "FloatingPoint.guide/Floating-Point Functions" 66 } Floating-Point Functions
|
||||||
|
@{" Ffloor " Link "FloatingPoint.guide/Floating-Point Functions" 59 } Floating-Point Functions
|
||||||
|
@{" FileLength " Link "BuiltIns.guide/Input and output functions" 154 } Input and output functions
|
||||||
|
@{" Flog " Link "FloatingPoint.guide/Floating-Point Functions" 73 } Floating-Point Functions
|
||||||
|
@{" Flog10 " Link "FloatingPoint.guide/Floating-Point Functions" 73 } Floating-Point Functions
|
||||||
|
@{" FOR " Link "Introduction.guide/FOR loop" 0 } FOR loop
|
||||||
|
@{" ForAll " Link "MoreExpressions.guide/Lists and quoted expressions" 29 } Lists and quoted expressions
|
||||||
|
@{" Forward " Link "Types.guide/Linked Lists" 56 } Linked Lists
|
||||||
|
@{" Fpow " Link "FloatingPoint.guide/Floating-Point Functions" 66 } Floating-Point Functions
|
||||||
|
@{" FreeStack " Link "BuiltIns.guide/System support functions" 79 } System support functions
|
||||||
|
@{" Fsin " Link "FloatingPoint.guide/Floating-Point Functions" 51 } Floating-Point Functions
|
||||||
|
@{" Fsqrt " Link "FloatingPoint.guide/Floating-Point Functions" 63 } Floating-Point Functions
|
||||||
|
@{" Ftan " Link "FloatingPoint.guide/Floating-Point Functions" 51 } Floating-Point Functions
|
||||||
|
@{" Gadget " Link "BuiltIns.guide/Intuition support functions" 187 } Intuition support functions
|
||||||
|
@{" GADGETSIZE " Link "BuiltIns.guide/Built-In Constants" 16 } Built-In Constants
|
||||||
|
@{" gfxbase " Link "BuiltIns.guide/Built-In Variables" 51 } Built-In Variables
|
||||||
|
@{" HANDLE " Link "Exceptions.guide/Procedures with Exception Handlers" 0 } Procedures with Exception Handlers
|
||||||
|
@{" IF " Link "Introduction.guide/IF block" 0 } IF block
|
||||||
|
@{" IF, (expression) " Link "Introduction.guide/IF expression" 0 } IF expression
|
||||||
|
@{" INC " Link "MoreExpressions.guide/INC and DEC statements" 0 } INC and DEC statements
|
||||||
|
@{" INCBIN " Link "MoreExpressions.guide/Static memory" 0 } Static memory
|
||||||
|
@{" Inp " Link "BuiltIns.guide/Input and output functions" 109 } Input and output functions
|
||||||
|
@{" InStr " Link "Types.guide/String functions" 156 } String functions
|
||||||
|
@{" Int " Link "BuiltIns.guide/Maths and logic functions" 116 } Maths and logic functions
|
||||||
|
@{" INT " Link "MoreExpressions.guide/Static memory" 0 } Static memory
|
||||||
|
@{" INT " Link "Types.guide/Indirect types" 0 } Indirect types
|
||||||
|
@{" intuitionbase " Link "BuiltIns.guide/Built-In Variables" 51 } Built-In Variables
|
||||||
|
@{" IS " Link "Procedures.guide/One-Line Functions" 0 } One-Line Functions
|
||||||
|
@{" JUMP " Link "MoreExpressions.guide/Labelling and the JUMP statement" 0 } Labelling and the JUMP statement
|
||||||
|
@{" KickVersion " Link "BuiltIns.guide/System support functions" 84 } System support functions
|
||||||
|
@{" LeftMouse " Link "BuiltIns.guide/Intuition support functions" 273 } Intuition support functions
|
||||||
|
@{" Line " Link "BuiltIns.guide/Graphics functions" 27 } Graphics functions
|
||||||
|
@{" Link " Link "Types.guide/Linked Lists" 23 } Linked Lists
|
||||||
|
@{" LIST " Link "Types.guide/Lists and E-lists" 35 } Lists and E-lists
|
||||||
|
@{" List " Link "Types.guide/List functions" 12 } List functions
|
||||||
|
@{" ListAdd " Link "Types.guide/List functions" 47 } List functions
|
||||||
|
@{" ListCmp " Link "Types.guide/List functions" 28 } List functions
|
||||||
|
@{" ListCopy " Link "Types.guide/List functions" 37 } List functions
|
||||||
|
@{" ListItem " Link "Types.guide/List functions" 65 } List functions
|
||||||
|
@{" ListLen " Link "Types.guide/List functions" 55 } List functions
|
||||||
|
@{" ListMax " Link "Types.guide/List functions" 59 } List functions
|
||||||
|
@{" Long " Link "BuiltIns.guide/Maths and logic functions" 116 } Maths and logic functions
|
||||||
|
@{" LONG " Link "Types.guide/LONG Type" 0 } LONG Type
|
||||||
|
@{" LONG " Link "MoreExpressions.guide/Static memory" 0 } Static memory
|
||||||
|
@{" LONG, preliminary " Link "Introduction.guide/Variable types" 0 } Variable types
|
||||||
|
@{" LOOP " Link "MoreExpressions.guide/LOOP block" 0 } LOOP block
|
||||||
|
@{" LowerStr " Link "Types.guide/String functions" 169 } String functions
|
||||||
|
@{" main " Link "Introduction.guide/Procedures" 0 } Procedures
|
||||||
|
@{" MapList " Link "MoreExpressions.guide/Lists and quoted expressions" 8 } Lists and quoted expressions
|
||||||
|
@{" Max " Link "BuiltIns.guide/Maths and logic functions" 49 } Maths and logic functions
|
||||||
|
@{" MidStr " Link "Types.guide/String functions" 144 } String functions
|
||||||
|
@{" Min " Link "BuiltIns.guide/Maths and logic functions" 52 } Maths and logic functions
|
||||||
|
@{" Mod " Link "BuiltIns.guide/Maths and logic functions" 70 } Maths and logic functions
|
||||||
|
@{" MODULE " Link "Modules.guide/Using Modules" 0 } Using Modules
|
||||||
|
@{" Mouse " Link "BuiltIns.guide/Intuition support functions" 235 } Intuition support functions
|
||||||
|
@{" MouseX " Link "BuiltIns.guide/Intuition support functions" 259 } Intuition support functions
|
||||||
|
@{" MouseY " Link "BuiltIns.guide/Intuition support functions" 266 } Intuition support functions
|
||||||
|
@{" MsgCode " Link "BuiltIns.guide/Intuition support functions" 313 } Intuition support functions
|
||||||
|
@{" MsgIaddr " Link "BuiltIns.guide/Intuition support functions" 316 } Intuition support functions
|
||||||
|
@{" MsgQualifier " Link "BuiltIns.guide/Intuition support functions" 321 } Intuition support functions
|
||||||
|
@{" Mul " Link "BuiltIns.guide/Maths and logic functions" 0 } Maths and logic functions
|
||||||
|
@{" NEW " Link "Memory.guide/NEW and END Operators" 0 } NEW and END Operators
|
||||||
|
@{" New " Link "BuiltIns.guide/System support functions" 0 } System support functions
|
||||||
|
@{" NEWFILE " Link "BuiltIns.guide/Built-In Constants" 20 } Built-In Constants
|
||||||
|
@{" NewM " Link "BuiltIns.guide/System support functions" 19 } System support functions
|
||||||
|
@{" NewR " Link "BuiltIns.guide/System support functions" 14 } System support functions
|
||||||
|
@{" Next " Link "Types.guide/Linked Lists" 38 } Linked Lists
|
||||||
|
@{" NIL " Link "BuiltIns.guide/Built-In Constants" 8 } Built-In Constants
|
||||||
|
@{" Not " Link "BuiltIns.guide/Maths and logic functions" 14 } Maths and logic functions
|
||||||
|
@{" OBJECT " Link "Types.guide/Example object" 23 } Example object
|
||||||
|
@{" OBJECT..OF " Link "OOE.guide/Inheritance in E" 0 } Inheritance in E
|
||||||
|
@{" Odd " Link "BuiltIns.guide/Maths and logic functions" 45 } Maths and logic functions
|
||||||
|
@{" OLDFILE " Link "BuiltIns.guide/Built-In Constants" 20 } Built-In Constants
|
||||||
|
@{" OpenS " Link "BuiltIns.guide/Intuition support functions" 129 } Intuition support functions
|
||||||
|
@{" OpenW " Link "BuiltIns.guide/Intuition support functions" 16 } Intuition support functions
|
||||||
|
@{" OR " Link "MoreExpressions.guide/Bitwise AND and OR" 0 } Bitwise AND and OR
|
||||||
|
@{" Or " Link "BuiltIns.guide/Maths and logic functions" 14 } Maths and logic functions
|
||||||
|
@{" Out " Link "BuiltIns.guide/Input and output functions" 99 } Input and output functions
|
||||||
|
@{" Plot " Link "BuiltIns.guide/Graphics functions" 16 } Graphics functions
|
||||||
|
@{" PrintF " Link "BuiltIns.guide/Input and output functions" 85 } Input and output functions
|
||||||
|
@{" PRIVATE " Link "OOE.guide/Data-Hiding in E" 0 } Data-Hiding in E
|
||||||
|
@{" PROC " Link "Introduction.guide/Procedure Definition" 0 } Procedure Definition
|
||||||
|
@{" PROC..OF " Link "OOE.guide/Methods in E" 0 } Methods in E
|
||||||
|
@{" PTR TO type " Link "Types.guide/PTR Type" 0 } PTR Type
|
||||||
|
@{" PUBLIC " Link "OOE.guide/Data-Hiding in E" 0 } Data-Hiding in E
|
||||||
|
@{" PutChar " Link "BuiltIns.guide/Maths and logic functions" 123 } Maths and logic functions
|
||||||
|
@{" PutInt " Link "BuiltIns.guide/Maths and logic functions" 123 } Maths and logic functions
|
||||||
|
@{" PutLong " Link "BuiltIns.guide/Maths and logic functions" 123 } Maths and logic functions
|
||||||
|
@{" Raise " Link "Exceptions.guide/Raising an Exception" 0 } Raising an Exception
|
||||||
|
@{" RAISE " Link "Exceptions.guide/Automatic Exceptions" 25 } Automatic Exceptions
|
||||||
|
@{" ReadStr " Link "BuiltIns.guide/Input and output functions" 114 } Input and output functions
|
||||||
|
@{" RealF " Link "FloatingPoint.guide/Floating-Point Functions" 19 } Floating-Point Functions
|
||||||
|
@{" RealVal " Link "FloatingPoint.guide/Floating-Point Functions" 10 } Floating-Point Functions
|
||||||
|
@{" REPEAT " Link "Introduction.guide/REPEAT..UNTIL loop" 0 } REPEAT..UNTIL loop
|
||||||
|
@{" ReThrow " Link "Exceptions.guide/Raise within an Exception Handler" 68 } Raise within an Exception Handler
|
||||||
|
@{" RETURN " Link "Procedures.guide/Functions" 32 } Functions
|
||||||
|
@{" RightStr " Link "Types.guide/String functions" 136 } String functions
|
||||||
|
@{" Rnd " Link "BuiltIns.guide/Maths and logic functions" 84 } Maths and logic functions
|
||||||
|
@{" RndQ " Link "BuiltIns.guide/Maths and logic functions" 93 } Maths and logic functions
|
||||||
|
@{" SELECT " Link "Introduction.guide/SELECT block" 0 } SELECT block
|
||||||
|
@{" SELECT " Link "Introduction.guide/SELECT..OF block" 0 } SELECT..OF block
|
||||||
|
@{" SELECT..OF " Link "Introduction.guide/SELECT..OF block" 0 } SELECT..OF block
|
||||||
|
@{" SelectList " Link "MoreExpressions.guide/Lists and quoted expressions" 62 } Lists and quoted expressions
|
||||||
|
@{" self " Link "OOE.guide/Methods in E" 67 } Methods in E
|
||||||
|
@{" SET " Link "Constants.guide/Sets" 0 } Sets
|
||||||
|
@{" SetColour " Link "BuiltIns.guide/Graphics functions" 49 } Graphics functions
|
||||||
|
@{" SetList " Link "Types.guide/List functions" 62 } List functions
|
||||||
|
@{" SetStdIn " Link "BuiltIns.guide/Input and output functions" 161 } Input and output functions
|
||||||
|
@{" SetStdOut " Link "BuiltIns.guide/Input and output functions" 170 } Input and output functions
|
||||||
|
@{" SetStdRast " Link "BuiltIns.guide/Graphics functions" 58 } Graphics functions
|
||||||
|
@{" SetStr " Link "Types.guide/String functions" 178 } String functions
|
||||||
|
@{" SetTopaz " Link "BuiltIns.guide/Graphics functions" 67 } Graphics functions
|
||||||
|
@{" Shl " Link "BuiltIns.guide/Maths and logic functions" 100 } Maths and logic functions
|
||||||
|
@{" Shr " Link "BuiltIns.guide/Maths and logic functions" 108 } Maths and logic functions
|
||||||
|
@{" Sign " Link "BuiltIns.guide/Maths and logic functions" 36 } Maths and logic functions
|
||||||
|
@{" SIZEOF " Link "MoreExpressions.guide/SIZEOF expression" 0 } SIZEOF expression
|
||||||
|
@{" stdin " Link "BuiltIns.guide/Built-In Variables" 34 } Built-In Variables
|
||||||
|
@{" stdout " Link "BuiltIns.guide/Built-In Variables" 34 } Built-In Variables
|
||||||
|
@{" stdrast " Link "BuiltIns.guide/Built-In Variables" 46 } Built-In Variables
|
||||||
|
@{" STEP " Link "Introduction.guide/FOR loop" 37 } FOR loop
|
||||||
|
@{" StrAdd " Link "Types.guide/String functions" 99 } String functions
|
||||||
|
@{" StrCmp " Link "Types.guide/String functions" 40 } String functions
|
||||||
|
@{" StrCopy " Link "Types.guide/String functions" 58 } String functions
|
||||||
|
@{" STRING " Link "Types.guide/Normal strings and E-strings" 49 } Normal strings and E-strings
|
||||||
|
@{" String " Link "Types.guide/String functions" 14 } String functions
|
||||||
|
@{" StringF " Link "BuiltIns.guide/Input and output functions" 90 } Input and output functions
|
||||||
|
@{" StrLen " Link "Types.guide/String functions" 108 } String functions
|
||||||
|
@{" STRLEN " Link "BuiltIns.guide/Built-In Constants" 24 } Built-In Constants
|
||||||
|
@{" StrMax " Link "Types.guide/String functions" 131 } String functions
|
||||||
|
@{" SUPER " Link "OOE.guide/Inheritance in E" 110 } Inheritance in E
|
||||||
|
@{" TextF " Link "BuiltIns.guide/Graphics functions" 42 } Graphics functions
|
||||||
|
@{" THEN " Link "Introduction.guide/IF block" 29 } IF block
|
||||||
|
@{" Throw " Link "Exceptions.guide/Raising an Exception" 0 } Raising an Exception
|
||||||
|
@{" TO " Link "Introduction.guide/FOR loop" 0 } FOR loop
|
||||||
|
@{" TO, (CASE range) " Link "Introduction.guide/SELECT..OF block" 0 } SELECT..OF block
|
||||||
|
@{" TO, (FOR loop) " Link "Introduction.guide/FOR loop" 0 } FOR loop
|
||||||
|
@{" TrimStr " Link "Types.guide/String functions" 161 } String functions
|
||||||
|
@{" TRUE " Link "Introduction.guide/Logic and comparison" 11 } Logic and comparison
|
||||||
|
@{" TRUE " Link "BuiltIns.guide/Built-In Constants" 5 } Built-In Constants
|
||||||
|
@{" UNTIL " Link "Introduction.guide/REPEAT..UNTIL loop" 0 } REPEAT..UNTIL loop
|
||||||
|
@{" UpperStr " Link "Types.guide/String functions" 174 } String functions
|
||||||
|
@{" Val " Link "Types.guide/String functions" 199 } String functions
|
||||||
|
@{" VOID " Link "MoreExpressions.guide/Turning an Expression into a Statement" 0 } Turning an Expression into a Statement
|
||||||
|
@{" WaitIMessage " Link "BuiltIns.guide/Intuition support functions" 282 } Intuition support functions
|
||||||
|
@{" WaitLeftMouse " Link "BuiltIns.guide/Intuition support functions" 324 } Intuition support functions
|
||||||
|
@{" wbmessage " Link "BuiltIns.guide/Built-In Variables" 23 } Built-In Variables
|
||||||
|
@{" WHILE " Link "Introduction.guide/WHILE loop" 0 } WHILE loop
|
||||||
|
@{" WriteF " Link "BuiltIns.guide/Input and output functions" 0 } Input and output functions
|
||||||
|
|
||||||
|
|
||||||
|
@ENDNODE
|
||||||
|
|
Binary file not shown.
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,405 @@
|
||||||
|
@database beginner.guide
|
||||||
|
|
||||||
|
@Master beginner
|
||||||
|
|
||||||
|
@Width 75
|
||||||
|
|
||||||
|
|
||||||
|
This is the AmigaGuide® file beginner.guide, produced by Makeinfo-1.55 from
|
||||||
|
the input file beginner.
|
||||||
|
|
||||||
|
|
||||||
|
@NODE "main" "Exception Handling"
|
||||||
|
@Next "Memory.guide/main"
|
||||||
|
@Prev "Modules.guide/main"
|
||||||
|
@Toc "Contents.guide/main"
|
||||||
|
|
||||||
|
Exception Handling
|
||||||
|
******************
|
||||||
|
|
||||||
|
Often your program has to check the results of functions and do
|
||||||
|
different things if errors have occurred. For instance, if you try to
|
||||||
|
open a window (using @{b }OpenW@{ub }), you may get a @{b }NIL@{ub } pointer returned which
|
||||||
|
shows that the window could not be opened for some reason. In this case
|
||||||
|
you normally can't continue with the program, so you must tidy up and
|
||||||
|
terminate. Tidying up can sometimes involve closing windows, screens and
|
||||||
|
libraries, so sometimes your error cases can make your program cluttered
|
||||||
|
and messy. This is where exceptions come in--an @{fg shine }exception@{fg text } is simply an
|
||||||
|
error case, and @{fg shine }exception handling@{fg text } is dealing with error cases. The
|
||||||
|
exception handling in E neatly separates error specific code from the real
|
||||||
|
code of your program.
|
||||||
|
|
||||||
|
|
||||||
|
@{" Procedures with Exception Handlers " Link "Procedures with Exception Handlers" }
|
||||||
|
@{" Raising an Exception " Link "Raising an Exception" }
|
||||||
|
@{" Automatic Exceptions " Link "Automatic Exceptions" }
|
||||||
|
@{" Raise within an Exception Handler " Link "Raise within an Exception Handler" }
|
||||||
|
|
||||||
|
|
||||||
|
@ENDNODE
|
||||||
|
|
||||||
|
@NODE "Procedures with Exception Handlers" "Procedures with Exception Handlers"
|
||||||
|
@Next "Raising an Exception"
|
||||||
|
@Toc "main"
|
||||||
|
|
||||||
|
Procedures with Exception Handlers
|
||||||
|
==================================
|
||||||
|
|
||||||
|
A procedure with an exception handler looks like this:
|
||||||
|
|
||||||
|
PROC fred(params...) HANDLE
|
||||||
|
/* Main, real code */
|
||||||
|
EXCEPT
|
||||||
|
/* Error handling code */
|
||||||
|
ENDPROC
|
||||||
|
|
||||||
|
This is very similar to a normal procedure, apart from the @{b }HANDLE@{ub } and
|
||||||
|
@{b }EXCEPT@{ub } keywords. The @{b }HANDLE@{ub } keyword means the procedure is going to have
|
||||||
|
an exception handler, and the @{b }EXCEPT@{ub } keyword marks the end of the normal
|
||||||
|
code and the start of the exception handling code. The procedure works
|
||||||
|
just as normal, executing the code in the part before the @{b }EXCEPT@{ub }, but when
|
||||||
|
an error happens you can pass control to the exception handler (i.e., the
|
||||||
|
code after the @{b }EXCEPT@{ub } is executed).
|
||||||
|
|
||||||
|
|
||||||
|
@ENDNODE
|
||||||
|
|
||||||
|
@NODE "Raising an Exception" "Raising an Exception"
|
||||||
|
@Next "Automatic Exceptions"
|
||||||
|
@Prev "Procedures with Exception Handlers"
|
||||||
|
@Toc "main"
|
||||||
|
|
||||||
|
Raising an Exception
|
||||||
|
====================
|
||||||
|
|
||||||
|
When an error occurs (and you want to handle it), you @{fg shine }raise@{fg text } an
|
||||||
|
exception using either the @{b }Raise@{ub } or @{b }Throw@{ub } function. You call @{b }Raise@{ub } with a
|
||||||
|
number which identifies the kind of error that occurred. The code in the
|
||||||
|
exception handler is responsible for decoding the number and then doing
|
||||||
|
the appropriate thing. @{b }Throw@{ub } is very similar to @{b }Raise@{ub }, and the following
|
||||||
|
description of @{b }Raise@{ub } also applies to @{b }Throw@{ub }. The difference is that @{b }Throw@{ub }
|
||||||
|
takes a second argument which can be used to pass extra information to a
|
||||||
|
handler (usually a string). The terms `raising' and `throwing' an
|
||||||
|
exception can be used interchangeably.
|
||||||
|
|
||||||
|
When @{b }Raise@{ub } is called it immediately stops the execution of the current
|
||||||
|
procedure code and passes control to the exception handler of most recent
|
||||||
|
procedure which has a handler (which may be the current procedure). This
|
||||||
|
is a bit complicated, but you can stick to raising exceptions and handling
|
||||||
|
them in the same procedure, as in the next example:
|
||||||
|
|
||||||
|
CONST BIG_AMOUNT = 100000
|
||||||
|
|
||||||
|
ENUM ERR_MEM=1
|
||||||
|
|
||||||
|
PROC main() HANDLE
|
||||||
|
DEF block
|
||||||
|
block:=New(BIG_AMOUNT)
|
||||||
|
IF block=NIL THEN Raise(ERR_MEM)
|
||||||
|
WriteF('Got enough memory\\n')
|
||||||
|
EXCEPT
|
||||||
|
IF exception=ERR_MEM
|
||||||
|
WriteF('Not enough memory\\n')
|
||||||
|
ELSE
|
||||||
|
WriteF('Unknown exception\\n')
|
||||||
|
ENDIF
|
||||||
|
ENDPROC
|
||||||
|
|
||||||
|
This uses an exception handler to print a message saying there wasn't
|
||||||
|
enough memory if the call to @{b }New@{ub } returns @{b }NIL@{ub }. The parameter to @{b }Raise@{ub } is
|
||||||
|
stored in the special variable @{b }exception@{ub } in the exception handler part of
|
||||||
|
the code, so if @{b }Raise@{ub } is called with a number other than @{b }ERR_MEM@{ub } a message
|
||||||
|
saying "Unknown exception" will be printed.
|
||||||
|
|
||||||
|
Try running this program with a really large @{b }BIG_AMOUNT@{ub } constant, so
|
||||||
|
that the @{b }New@{ub } can't allocate the memory. Notice that the "Got enough
|
||||||
|
memory" is not printed if @{b }Raise@{ub } is called. That's because the execution
|
||||||
|
of the normal procedure code stops when @{b }Raise@{ub } is called, and control
|
||||||
|
passes to the appropriate exception handler. When the end of the
|
||||||
|
exception handler is reached the procedure is finished, and in this case
|
||||||
|
the program terminates because the procedure was the @{b }main@{ub } procedure.
|
||||||
|
|
||||||
|
If @{b }Throw@{ub } is used instead of @{b }Raise@{ub } then, in the handler, the special
|
||||||
|
variable @{b }exceptioninfo@{ub } will contain the value of the second parameter.
|
||||||
|
This can be used in conjunction with @{b }exception@{ub } to provide the handler with
|
||||||
|
more information about the error. Here's the above example re-written to
|
||||||
|
use @{b }Throw@{ub }:
|
||||||
|
|
||||||
|
CONST BIG_AMOUNT = 100000
|
||||||
|
|
||||||
|
ENUM ERR_MEM=1
|
||||||
|
|
||||||
|
PROC main() HANDLE
|
||||||
|
DEF block
|
||||||
|
block:=New(BIG_AMOUNT)
|
||||||
|
IF block=NIL THEN Throw(ERR_MEM, 'Not enough memory\\n')
|
||||||
|
WriteF('Got enough memory\\n')
|
||||||
|
EXCEPT
|
||||||
|
IF exception=ERR_MEM
|
||||||
|
WriteF(exceptioninfo)
|
||||||
|
ELSE
|
||||||
|
WriteF('Unknown exception\\n')
|
||||||
|
ENDIF
|
||||||
|
ENDPROC
|
||||||
|
|
||||||
|
An enumeration (using @{b }ENUM@{ub }) is a good way of getting different
|
||||||
|
constants for various exceptions. It's always a good idea to use
|
||||||
|
constants for the parameter to @{b }Raise@{ub } and in the exception handler, because
|
||||||
|
it makes everything a lot more readable: @{b }Raise(ERR_MEM)@{ub } is much clearer
|
||||||
|
than @{b }Raise(1)@{ub }. The enumeration starts at one because zero is a special
|
||||||
|
exception: it usually means that no error occurred. This is useful when
|
||||||
|
the handler does the same cleaning up that would normally be done when the
|
||||||
|
program terminates successfully. For this reason there is a special form
|
||||||
|
of @{b }EXCEPT@{ub } which automatically raises a zero exception when the code in the
|
||||||
|
procedure successfully terminates. This is @{b }EXCEPT DO@{ub }, with the @{b }DO@{ub }
|
||||||
|
suggesting to the reader that the exception handler is called even if no
|
||||||
|
error occurs. Also, the argument to the @{b }Raise@{ub } function defaults to zero
|
||||||
|
if it is omitted (see @{"Default Arguments" Link "Procedures.guide/Default Arguments" }).
|
||||||
|
|
||||||
|
So, what happens if you call @{b }Raise@{ub } in a procedure without an exception
|
||||||
|
handler? Well, this is where the real power of the handling mechanism
|
||||||
|
comes to light. In this case, control passes to the exception handler of
|
||||||
|
the most @{fg shine }recent@{fg text } procedure with a handler. If none are found then the
|
||||||
|
program terminates. `Recent' means one of the procedures involved in
|
||||||
|
calling your procedure. So, if the procedure @{b }fred@{ub } calls @{b }barney@{ub }, then when
|
||||||
|
@{b }barney@{ub } is being executed @{b }fred@{ub } is a recent procedure. Because the @{b }main@{ub }
|
||||||
|
procedure is where the program starts it is a recent procedure for every
|
||||||
|
other procedure in the program. This means, in practice:
|
||||||
|
|
||||||
|
@{b }*@{ub } If you define @{b }fred@{ub } to be a procedure with an exception handler then
|
||||||
|
any procedures called by @{b }fred@{ub } will have their exceptions handled by
|
||||||
|
the handler in @{b }fred@{ub } if they don't have their own handler.
|
||||||
|
|
||||||
|
@{b }*@{ub } If you define @{b }main@{ub } to be a procedure with an exception handler then
|
||||||
|
any exceptions that are raised will always be dealt with by some
|
||||||
|
exception handling code (i.e., the handler of @{b }main@{ub } or some other
|
||||||
|
procedure).
|
||||||
|
|
||||||
|
Here's a more complicated example:
|
||||||
|
|
||||||
|
ENUM FRED=1, BARNEY
|
||||||
|
|
||||||
|
PROC main()
|
||||||
|
WriteF('Hello from main\\n')
|
||||||
|
fred()
|
||||||
|
barney()
|
||||||
|
WriteF('Goodbye from main\\n')
|
||||||
|
ENDPROC
|
||||||
|
|
||||||
|
PROC fred() HANDLE
|
||||||
|
WriteF(' Hello from fred\\n')
|
||||||
|
Raise(FRED)
|
||||||
|
WriteF(' Goodbye from fred\\n')
|
||||||
|
EXCEPT
|
||||||
|
WriteF(' Handler fred: \\d\\n', exception)
|
||||||
|
ENDPROC
|
||||||
|
|
||||||
|
PROC barney()
|
||||||
|
WriteF(' Hello from barney\\n')
|
||||||
|
Raise(BARNEY)
|
||||||
|
WriteF(' Goodbye from barney\\n')
|
||||||
|
ENDPROC
|
||||||
|
|
||||||
|
When you run this program you get the following output:
|
||||||
|
|
||||||
|
Hello from main
|
||||||
|
Hello from fred
|
||||||
|
Handler fred: 1
|
||||||
|
Hello from barney
|
||||||
|
|
||||||
|
This is because the @{b }fred@{ub } procedure is terminated by the @{b }Raise(FRED)@{ub } call,
|
||||||
|
and the whole program is terminated by the @{b }Raise(BARNEY)@{ub } call (since
|
||||||
|
@{b }barney@{ub } and @{b }main@{ub } do not have handlers).
|
||||||
|
|
||||||
|
Now try this:
|
||||||
|
|
||||||
|
ENUM FRED=1, BARNEY
|
||||||
|
|
||||||
|
PROC main()
|
||||||
|
WriteF('Hello from main\\n')
|
||||||
|
fred()
|
||||||
|
WriteF('Goodbye from main\\n')
|
||||||
|
ENDPROC
|
||||||
|
|
||||||
|
PROC fred() HANDLE
|
||||||
|
WriteF(' Hello from fred\\n')
|
||||||
|
barney()
|
||||||
|
Raise(FRED)
|
||||||
|
WriteF(' Goodbye from fred\\n')
|
||||||
|
EXCEPT
|
||||||
|
WriteF(' Handler fred: \\d\\n', exception)
|
||||||
|
ENDPROC
|
||||||
|
|
||||||
|
PROC barney()
|
||||||
|
WriteF(' Hello from barney\\n')
|
||||||
|
Raise(BARNEY)
|
||||||
|
WriteF(' Goodbye from barney\\n')
|
||||||
|
ENDPROC
|
||||||
|
|
||||||
|
When you run this you get the following output:
|
||||||
|
|
||||||
|
Hello from main
|
||||||
|
Hello from fred
|
||||||
|
Hello from barney
|
||||||
|
Handler fred: 2
|
||||||
|
Goodbye from main
|
||||||
|
|
||||||
|
Now the @{b }fred@{ub } procedure calls @{b }barney@{ub }, so @{b }main@{ub } and @{b }fred@{ub } are recent
|
||||||
|
procedures when @{b }Raise(BARNEY)@{ub } is executed, and therefore the @{b }fred@{ub }
|
||||||
|
exception handler is called. When this handler finishes the call to @{b }fred@{ub }
|
||||||
|
in @{b }main@{ub } is finished, so the @{b }main@{ub } procedure is completed and we see the
|
||||||
|
`Goodbye' message. In the previous program the @{b }Raise(BARNEY)@{ub } call did not
|
||||||
|
get handled and the whole program terminated at that point.
|
||||||
|
|
||||||
|
|
||||||
|
@ENDNODE
|
||||||
|
|
||||||
|
@NODE "Automatic Exceptions" "Automatic Exceptions"
|
||||||
|
@Next "Raise within an Exception Handler"
|
||||||
|
@Prev "Raising an Exception"
|
||||||
|
@Toc "main"
|
||||||
|
|
||||||
|
Automatic Exceptions
|
||||||
|
====================
|
||||||
|
|
||||||
|
In the previous section we saw an example of raising an exception when
|
||||||
|
a call to @{b }New@{ub } returned @{b }NIL@{ub }. We can re-write this example to use
|
||||||
|
@{fg shine }automatic@{fg text } exception raising:
|
||||||
|
|
||||||
|
CONST BIG_AMOUNT = 100000
|
||||||
|
|
||||||
|
ENUM ERR_MEM=1
|
||||||
|
|
||||||
|
RAISE ERR_MEM IF New()=NIL
|
||||||
|
|
||||||
|
PROC main() HANDLE
|
||||||
|
DEF block
|
||||||
|
block:=New(BIG_AMOUNT)
|
||||||
|
WriteF('Got enough memory\\n')
|
||||||
|
EXCEPT
|
||||||
|
IF exception=ERR_MEM
|
||||||
|
WriteF('Not enough memory\\n')
|
||||||
|
ELSE
|
||||||
|
WriteF('Unknown exception\\n')
|
||||||
|
ENDIF
|
||||||
|
ENDPROC
|
||||||
|
|
||||||
|
The only difference is the removal of the @{b }IF@{ub } which checked the value of
|
||||||
|
@{b }block@{ub }, and the addition of a @{b }RAISE@{ub } part. This @{b }RAISE@{ub } part means that
|
||||||
|
whenever the @{b }New@{ub } function is called in the program, the exception @{b }ERR_MEM@{ub }
|
||||||
|
will be raised if it returns @{b }NIL@{ub } (i.e., the exception @{b }ERR_MEM@{ub } is
|
||||||
|
automatically raised). This unclutters the program by removing a lot of
|
||||||
|
error checking @{b }IF@{ub } statements.
|
||||||
|
|
||||||
|
The precise form of the @{b }RAISE@{ub } part is:
|
||||||
|
|
||||||
|
RAISE @{fg shine }exception@{fg text } IF @{fg shine }function@{fg text }() @{fg shine }compare@{fg text } @{fg shine }value@{fg text } ,
|
||||||
|
@{fg shine }exception2@{fg text } IF @{fg shine }function2@{fg text }() @{fg shine }compare2@{fg text } @{fg shine }value2@{fg text } ,
|
||||||
|
...
|
||||||
|
@{fg shine }exceptionN@{fg text } IF @{fg shine }functionN@{fg text }() @{fg shine }compareN@{fg text } @{fg shine }valueN@{fg text }
|
||||||
|
|
||||||
|
The @{fg shine }exception@{fg text } is a constant (or number) which represents the exception
|
||||||
|
to be raised, @{fg shine }function@{fg text } is the E built-in or system function to be
|
||||||
|
automatically checked, @{fg shine }value@{fg text } is the return value to be checked against,
|
||||||
|
and @{fg shine }compare@{fg text } is the method of checking (i.e., @{b }=@{ub }, @{b }<>@{ub }, @{b }<@{ub }, @{b }<=@{ub }, @{b }>@{ub } or @{b }>=@{ub }).
|
||||||
|
This mechanism only exists for built-in or library functions because they
|
||||||
|
would otherwise have no way of raising exceptions. The procedures you
|
||||||
|
define yourself can, of course, use @{b }Raise@{ub } to raise exceptions in a much
|
||||||
|
more flexible way.
|
||||||
|
|
||||||
|
|
||||||
|
@ENDNODE
|
||||||
|
|
||||||
|
@NODE "Raise within an Exception Handler" "Raise within an Exception Handler"
|
||||||
|
@Prev "Automatic Exceptions"
|
||||||
|
@Toc "main"
|
||||||
|
|
||||||
|
@{b }Raise@{ub } within an Exception Handler
|
||||||
|
=================================
|
||||||
|
|
||||||
|
If you call @{b }Raise@{ub } within an exception handler then control passes to
|
||||||
|
the next most recent handler. In this way you can write procedures which
|
||||||
|
have handlers that perform local tidying up. By using @{b }Raise@{ub } at the end of
|
||||||
|
the handler code you can invoke the next layer of tidying up.
|
||||||
|
|
||||||
|
As an example we'll use the Amiga system functions @{b }AllocMem@{ub } and @{b }FreeMem@{ub }
|
||||||
|
which are like the built-in function @{b }New@{ub } and @{b }Dispose@{ub }, but the memory
|
||||||
|
allocated by @{b }AllocMem@{ub } @{i }must@{ui } be deallocated (using @{b }FreeMem@{ub }) when it's
|
||||||
|
finished with, before the end of the program.
|
||||||
|
|
||||||
|
CONST SMALL=100, BIG=123456789
|
||||||
|
|
||||||
|
ENUM ERR_MEM=1
|
||||||
|
|
||||||
|
RAISE ERR_MEM IF AllocMem()=NIL
|
||||||
|
|
||||||
|
PROC main()
|
||||||
|
allocate()
|
||||||
|
ENDPROC
|
||||||
|
|
||||||
|
PROC allocate() HANDLE
|
||||||
|
DEF mem=NIL
|
||||||
|
mem:=AllocMem(SMALL, 0)
|
||||||
|
morealloc()
|
||||||
|
FreeMem(mem, SMALL)
|
||||||
|
EXCEPT
|
||||||
|
IF mem THEN FreeMem(mem, SMALL)
|
||||||
|
WriteF('Handler: deallocating "allocate" local memory\\n')
|
||||||
|
ENDPROC
|
||||||
|
|
||||||
|
PROC morealloc() HANDLE
|
||||||
|
DEF more=NIL, andmore=NIL
|
||||||
|
more:=AllocMem(SMALL, 0)
|
||||||
|
andmore:=AllocMem(BIG, 0)
|
||||||
|
WriteF('Allocated all the memory!\\n')
|
||||||
|
FreeMem(andmore, BIG)
|
||||||
|
FreeMem(more, SMALL)
|
||||||
|
EXCEPT
|
||||||
|
IF andmore THEN FreeMem(andmore, BIG)
|
||||||
|
IF more THEN FreeMem(more, SMALL)
|
||||||
|
WriteF('Handler: deallocating "morealloc" local memory\\n')
|
||||||
|
Raise(ERR_MEM)
|
||||||
|
ENDPROC
|
||||||
|
|
||||||
|
The calls to @{b }AllocMem@{ub } are automatically checked, and if @{b }NIL@{ub } is returned
|
||||||
|
the exception @{b }ERR_MEM@{ub } is raised. The handler in the @{b }allocate@{ub } procedure
|
||||||
|
checks to see if it needs to free the memory pointed to by @{b }mem@{ub }, and the
|
||||||
|
handler in the @{b }morealloc@{ub } checks @{b }andmore@{ub } and @{b }more@{ub }. At the end of the
|
||||||
|
@{b }morealloc@{ub } handler is the call @{b }Raise(ERR_MEM)@{ub }. This passes control to the
|
||||||
|
exception handler of the @{b }allocate@{ub } procedure, since @{b }allocate@{ub } called
|
||||||
|
@{b }morealloc@{ub }.
|
||||||
|
|
||||||
|
There's a couple of subtle points to notice about this example.
|
||||||
|
Firstly, the memory variables are all initialised to @{b }NIL@{ub }. This is because
|
||||||
|
the automatic exception raising on @{b }AllocMem@{ub } will result in the variables
|
||||||
|
not being assigned if the call returns @{b }NIL@{ub } (i.e., the exception is raised
|
||||||
|
before the assignment takes place), and the handler needs them to be @{b }NIL@{ub }
|
||||||
|
if @{b }AllocMem@{ub } fails. Of course, if @{b }AllocMem@{ub } does not return @{b }NIL@{ub } the
|
||||||
|
assignments work as normal.
|
||||||
|
|
||||||
|
Secondly, the @{b }IF@{ub } statements in the handlers check the memory pointer
|
||||||
|
variables do not contain @{b }NIL@{ub } by using their values as truth values. Since
|
||||||
|
@{b }NIL@{ub } is actually zero, a non-@{b }NIL@{ub } pointer will be non-zero, i.e., true in
|
||||||
|
the @{b }IF@{ub } check. This shorthand is often used, and so you should be aware of
|
||||||
|
it.
|
||||||
|
|
||||||
|
It is quite common that an exception handler will want to raise the
|
||||||
|
same exception after it has done its processing. The function @{b }ReThrow@{ub }
|
||||||
|
(which has no arguments) can be used for this purpose. It will re-raise
|
||||||
|
the exception, but only if the exception is not zero (since this special
|
||||||
|
value means that no error occurred). If the exception is zero then this
|
||||||
|
function has no effect. In fact, the following code fragments (within a
|
||||||
|
handler) are equivalent:
|
||||||
|
|
||||||
|
ReThrow()
|
||||||
|
|
||||||
|
IF exception THEN Throw(exception, exceptioninfo)
|
||||||
|
|
||||||
|
There are two examples, in Part Three, of how to use an exception
|
||||||
|
handler to make a program more readable: one deals with using data files
|
||||||
|
(see @{"String Handling and I-O" Link "Examples.guide/String Handling and I-O" }) and the other deals with opening screens and
|
||||||
|
windows (see @{"Screens" Link "Examples.guide/Screens" }).
|
||||||
|
|
||||||
|
|
||||||
|
@ENDNODE
|
||||||
|
|
|
@ -0,0 +1,358 @@
|
||||||
|
@database beginner.guide
|
||||||
|
|
||||||
|
@Master beginner
|
||||||
|
|
||||||
|
@Width 75
|
||||||
|
|
||||||
|
|
||||||
|
This is the AmigaGuide® file beginner.guide, produced by Makeinfo-1.55 from
|
||||||
|
the input file beginner.
|
||||||
|
|
||||||
|
|
||||||
|
@NODE "main" "Floating-Point Numbers"
|
||||||
|
@Next "Recursion.guide/main"
|
||||||
|
@Prev "Memory.guide/main"
|
||||||
|
@Toc "Contents.guide/main"
|
||||||
|
|
||||||
|
Floating-Point Numbers
|
||||||
|
**********************
|
||||||
|
|
||||||
|
@{fg shine }Floating-point@{fg text } or @{fg shine }real@{fg text } numbers can be used to represent both very
|
||||||
|
small fractions and very large numbers. However, unlike a @{b }LONG@{ub } which can
|
||||||
|
hold every integer in a certain range (see @{"Variable types" Link "Introduction.guide/Variable types" }), floating-point
|
||||||
|
numbers have limited @{fg shine }accuracy@{fg text }. Be warned, though: using floating-point
|
||||||
|
arithmetic in E is quite complicated and most problems can be solved
|
||||||
|
without using floating-point numbers, so you may wish to skip this chapter
|
||||||
|
until you really need to use them.
|
||||||
|
|
||||||
|
|
||||||
|
@{" Floating-Point Values " Link "Floating-Point Values" }
|
||||||
|
@{" Floating-Point Calculations " Link "Floating-Point Calculations" }
|
||||||
|
@{" Floating-Point Functions " Link "Floating-Point Functions" }
|
||||||
|
@{" Accuracy and Range " Link "Accuracy and Range" }
|
||||||
|
|
||||||
|
|
||||||
|
@ENDNODE
|
||||||
|
|
||||||
|
@NODE "Floating-Point Values" "Floating-Point Values"
|
||||||
|
@Next "Floating-Point Calculations"
|
||||||
|
@Toc "main"
|
||||||
|
|
||||||
|
Floating-Point Values
|
||||||
|
=====================
|
||||||
|
|
||||||
|
Floating-point values in E are written just like you might expect and
|
||||||
|
are stored in @{b }LONG@{ub } variables:
|
||||||
|
|
||||||
|
DEF x
|
||||||
|
x:=3.75
|
||||||
|
x:=-0.0000367
|
||||||
|
x:=275.0
|
||||||
|
|
||||||
|
You must remember to use a decimal point (without any spaces around it) in
|
||||||
|
the number if you want it to be considered a floating-point number, and
|
||||||
|
this is why a trailing @{b }.0@{ub } was used on the number in the last assignment.
|
||||||
|
At present you can't express every floating-point value in this way; the
|
||||||
|
compiler may complain that the value does not fit in 32-bits if you try to
|
||||||
|
use more than about nine digits in a single number. You can, however, use
|
||||||
|
the various floating-point maths functions to calculate any value you want
|
||||||
|
(see @{"Floating-Point Functions" Link "Floating-Point Functions" }).
|
||||||
|
|
||||||
|
|
||||||
|
@ENDNODE
|
||||||
|
|
||||||
|
@NODE "Floating-Point Calculations" "Floating-Point Calculations"
|
||||||
|
@Next "Floating-Point Functions"
|
||||||
|
@Prev "Floating-Point Values"
|
||||||
|
@Toc "main"
|
||||||
|
|
||||||
|
Floating-Point Calculations
|
||||||
|
===========================
|
||||||
|
|
||||||
|
Since a floating-point number is stored in a @{b }LONG@{ub } variable it would
|
||||||
|
normally be interpreted as an integer, and this interpretation will
|
||||||
|
generally not give a number anything like the intended floating-point
|
||||||
|
number. To use floating-point numbers in expressions you must use the
|
||||||
|
(rather complicated) floating-point conversion operator, which is the @{b }!@{ub }
|
||||||
|
character. This converts expressions and the normal maths and comparison
|
||||||
|
operators to and from floating-point.
|
||||||
|
|
||||||
|
All expressions are, by default, integer expressions. That is, they
|
||||||
|
represent @{b }LONG@{ub } integer values, rather than floating-point values. The
|
||||||
|
first time a @{b }!@{ub } occurs in an expression the value of the expression so far
|
||||||
|
is converted to floating-point and all the operators and variables after
|
||||||
|
this point are considered floating-point. The next time it occurs the
|
||||||
|
(floating-point) value of the expression so far is converted to an
|
||||||
|
integer, and the following operators and variables are considered integer
|
||||||
|
again. You can use @{b }!@{ub } as often as necessary within an expression. Parts
|
||||||
|
of an expression in parentheses are treated as separate expressions, so
|
||||||
|
are, by default, integer expressions (this, includes function call
|
||||||
|
arguments).
|
||||||
|
|
||||||
|
The integer/floating-point conversions performed by @{b }!@{ub } are not simple.
|
||||||
|
They involve rounding and also bounding. Conversion, for example, from
|
||||||
|
integer to floating-point and back again will generally not result in the
|
||||||
|
original integer value.
|
||||||
|
|
||||||
|
Here's a few commented examples, where @{b }f@{ub } always holds a floating-point
|
||||||
|
number, and @{b }i@{ub } and @{b }j@{ub } always hold integers:
|
||||||
|
|
||||||
|
DEF f, i, j
|
||||||
|
i:=1
|
||||||
|
f:=1.0
|
||||||
|
f:=i! -> i converted to floating-point (1.0)
|
||||||
|
f:=6.2
|
||||||
|
i:=!f! -> the expression f is floating-point,
|
||||||
|
-> then converted to integer (6)
|
||||||
|
|
||||||
|
In the first assignment, the integer value one is assigned to @{b }i@{ub }. In the
|
||||||
|
second, the floating-point value 1.0 is assigned to @{b }f@{ub }. The expression on
|
||||||
|
the right-hand side of third assignment is considered to be an integer
|
||||||
|
until the @{b }!@{ub } is met, at which point it is converted to the nearest
|
||||||
|
floating-point value. So, @{b }f@{ub } is assigned the floating-point value of one
|
||||||
|
(i.e., 1.0), just like it is by the second assignment. The expression in
|
||||||
|
the final assignment needs to start off as floating-point in order to
|
||||||
|
interpret the value stored in @{b }f@{ub } as floating-point. The expression
|
||||||
|
finishes by converting back to integer. The overall result is to turn the
|
||||||
|
floating-point value of @{b }f@{ub } into the nearest integer (in this case, six).
|
||||||
|
|
||||||
|
The assignments below are more complicated, but should be
|
||||||
|
straight-forward to follow. Again, @{b }f@{ub } always holds a floating-point
|
||||||
|
number, and @{b }i@{ub } and @{b }j@{ub } always hold integers.
|
||||||
|
|
||||||
|
f:=!f*f -> the whole expression is floating-point,
|
||||||
|
-> and f is squared (6.2*6.2)
|
||||||
|
f:=!f*(i!) -> the whole expression is floating-point,
|
||||||
|
-> i is converted to floating-point and
|
||||||
|
-> multiplied by f
|
||||||
|
j:=!f/(i!)! -> the whole division is floating-point,
|
||||||
|
-> with the result converted to integer
|
||||||
|
j:=!f!/i -> floating-point f is converted to integer
|
||||||
|
-> and is (integer) divided by i
|
||||||
|
IF !f<230.0 THEN RETURN 0 -> floating-point comparison <
|
||||||
|
IF !f>(i!) THEN RETURN 0 -> i converted to floating-point,
|
||||||
|
-> then compared to f
|
||||||
|
|
||||||
|
If the @{b }!@{ub } were omitted from the first assignment, then not only would the
|
||||||
|
value in @{b }f@{ub } be interpreted (incorrectly) as integer, but the multiplication
|
||||||
|
performed would be integer multiplication, rather than floating-point. In
|
||||||
|
the second assignment, the parentheses around the expression involving @{b }i@{ub }
|
||||||
|
are crucial. Without the parentheses the value stored in @{b }i@{ub } would be
|
||||||
|
interpreted as floating-point. This would be wrong because @{b }i@{ub } actually
|
||||||
|
stores an integer value, so parentheses are used to start a new expression
|
||||||
|
(which defaults to being integer). The value of @{b }i@{ub } is then interpreted
|
||||||
|
correctly, and finally converted to floating-point (by the @{b }!@{ub } just before
|
||||||
|
the closing parenthesis). The (floating-point) multiplication then takes
|
||||||
|
place with two floating-point values, and the result is stored in @{b }f@{ub }. In
|
||||||
|
the last two assignments (using division), @{b }j@{ub } is assigned roughly the same
|
||||||
|
value. However, the expression in the first assignment allows for greater
|
||||||
|
accuracy, since it uses floating-point division. This means the result
|
||||||
|
will be rounded, whereas it is truncated when integer division is used.
|
||||||
|
|
||||||
|
One important thing to know about floating-point numbers in E is that
|
||||||
|
the following assignments store the same value in @{b }g@{ub } (again, @{b }f@{ub } stores a
|
||||||
|
floating-point number). This is because no computation is performed and
|
||||||
|
no conversion happens: the value in @{b }f@{ub } is simply copied to @{b }g@{ub }. This is
|
||||||
|
especially important for function calls, as we shall see in the next
|
||||||
|
section. Strictly speaking, however, the second version is better, since
|
||||||
|
it shows (to the reader of the code) that the value in @{b }f@{ub } is meant to be
|
||||||
|
floating-point.
|
||||||
|
|
||||||
|
g:=f
|
||||||
|
g:=!f
|
||||||
|
|
||||||
|
|
||||||
|
@ENDNODE
|
||||||
|
|
||||||
|
@NODE "Floating-Point Functions" "Floating-Point Functions"
|
||||||
|
@Next "Accuracy and Range"
|
||||||
|
@Prev "Floating-Point Calculations"
|
||||||
|
@Toc "main"
|
||||||
|
|
||||||
|
Floating-Point Functions
|
||||||
|
========================
|
||||||
|
|
||||||
|
There are functions for formatting floating-point numbers to E-strings
|
||||||
|
(so that they can be printed) and for decoding floating-point numbers from
|
||||||
|
strings. There are also a number of built-in, floating-point functions
|
||||||
|
which compute some of the less common mathematical functions, such as the
|
||||||
|
various trigonometric functions.
|
||||||
|
|
||||||
|
@{b }RealVal(@{ub }@{fg shine }string@{fg text }@{b })@{ub }
|
||||||
|
This works in a similar way to @{b }Val@{ub } for extracting integers from a
|
||||||
|
string. The decoded floating-point value is returned as the regular
|
||||||
|
return value, and the number of characters of @{fg shine }string@{fg text } that were read
|
||||||
|
to make the number is returned as the first optional return value.
|
||||||
|
If a floating-point value could not be decoded from the string then
|
||||||
|
zero is returned as the optional return value and the regular return
|
||||||
|
value will be zero (i.e., 0.0).
|
||||||
|
|
||||||
|
@{b }RealF(@{ub }@{fg shine }e-string@{fg text }@{b },@{ub }@{fg shine }float@{fg text }@{b },@{ub }@{fg shine }digits@{fg text }@{b })@{ub }
|
||||||
|
Converts the floating-point value @{b }float@{ub } into a string which is stored
|
||||||
|
in @{fg shine }e-string@{fg text }. The number of digits to use after the decimal point
|
||||||
|
is specified by @{fg shine }digits@{fg text }, which can be zero to eight. The
|
||||||
|
floating-point value is rounded to the specified number of digits. A
|
||||||
|
value of zero for @{fg shine }digits@{fg text } gives a result with no fractional part and
|
||||||
|
no decimal point. The @{fg shine }e-string@{fg text } is returned by this function, and
|
||||||
|
this makes it easy to use with @{b }WriteF@{ub }.
|
||||||
|
|
||||||
|
PROC main()
|
||||||
|
DEF s[20]:STRING, f, i
|
||||||
|
f:=21.60539
|
||||||
|
FOR i:=0 TO 8
|
||||||
|
WriteF('f is \\s (using digits=\\d)\\n', RealF(s, f, i), i)
|
||||||
|
ENDFOR
|
||||||
|
ENDPROC
|
||||||
|
|
||||||
|
Notice that the floating-point argument, @{b }f@{ub }, to @{b }RealF@{ub } does not need a
|
||||||
|
leading @{b }!@{ub } because we are simply passing its value and not performing
|
||||||
|
a computation with it. The program should generate the following
|
||||||
|
output:
|
||||||
|
|
||||||
|
f is 22 (using digits=0)
|
||||||
|
f is 21.6 (using digits=1)
|
||||||
|
f is 21.61 (using digits=2)
|
||||||
|
f is 21.605 (using digits=3)
|
||||||
|
f is 21.6054 (using digits=4)
|
||||||
|
f is 21.60539 (using digits=5)
|
||||||
|
f is 21.605390 (using digits=6)
|
||||||
|
f is 21.6053900 (using digits=7)
|
||||||
|
f is 21.60539000 (using digits=8)
|
||||||
|
|
||||||
|
@{b }Fsin(@{ub }@{fg shine }float@{fg text }@{b })@{ub }, @{b }Fcos(@{ub }@{fg shine }float@{fg text }@{b })@{ub }, @{b }Ftan(@{ub }@{fg shine }float@{fg text }@{b })@{ub }
|
||||||
|
These compute the sine, cosine and tangent (respectively) of the
|
||||||
|
supplied @{fg shine }float@{fg text } angle, which is specified in radians.
|
||||||
|
|
||||||
|
@{b }Fabs(@{ub }@{fg shine }float@{fg text }@{b })@{ub }
|
||||||
|
Returns the absolute value of @{fg shine }float@{fg text }, much like @{b }Abs@{ub } does for
|
||||||
|
integers.
|
||||||
|
|
||||||
|
@{b }Ffloor(@{ub }@{fg shine }float@{fg text }@{b })@{ub }, @{b }Fceil(@{ub }@{fg shine }float@{fg text }@{b })@{ub }
|
||||||
|
The @{b }Ffloor@{ub } function rounds a floating-point value down to the
|
||||||
|
nearest, whole floating-point value. The @{b }Fceil@{ub } function rounds it up.
|
||||||
|
|
||||||
|
@{b }Fsqrt(@{ub }@{fg shine }float@{fg text }@{b })@{ub }
|
||||||
|
Returns the square root of @{fg shine }float@{fg text }.
|
||||||
|
|
||||||
|
@{b }Fpow(@{ub }@{fg shine }x@{fg text }@{b },@{ub }@{fg shine }y@{fg text }@{b })@{ub }, @{b }Fexp(@{ub }@{fg shine }float@{fg text }@{b })@{ub }
|
||||||
|
The @{b }Fpow@{ub } function returns the value of @{fg shine }x@{fg text } raised to the power of @{fg shine }y@{fg text }
|
||||||
|
(which are both floating-point values). The @{b }Fexp@{ub } function returns
|
||||||
|
the value of e raised to the power of @{fg shine }float@{fg text }, where e is the
|
||||||
|
mathematically special value (roughly 2.718282). `Raising to a
|
||||||
|
power' is known as @{fg shine }exponentiation@{fg text }.
|
||||||
|
|
||||||
|
@{b }Flog10(@{ub }@{fg shine }float@{fg text }@{b })@{ub }, @{b }Flog(@{ub }@{fg shine }float@{fg text }@{b })@{ub }
|
||||||
|
The @{b }Flog10@{ub } function returns the log to base ten of @{fg shine }float@{fg text } (the
|
||||||
|
@{fg shine }common logarithm@{fg text }). The @{b }Flog@{ub } function returns the log to base e of
|
||||||
|
@{fg shine }float@{fg text } (the @{fg shine }natural logarithm@{fg text }). @{b }Flog10@{ub } and @{b }Fpow@{ub } are linked in the
|
||||||
|
following way (ignoring floating-point inaccuracies):
|
||||||
|
|
||||||
|
x = Fpow(10.0, Flog10(x))
|
||||||
|
|
||||||
|
@{b }Flog@{ub } and @{b }Fexp@{ub } are similarly related (@{b }Fexp@{ub } could be used again, using
|
||||||
|
2.718282 as the first argument in place of 10.0).
|
||||||
|
|
||||||
|
x = Fexp(Flog(x))
|
||||||
|
|
||||||
|
Here's a small program which uses a few of the above functions, and
|
||||||
|
shows how to define functions which use and/or return floating-point
|
||||||
|
values.
|
||||||
|
|
||||||
|
DEF f, i, s[20]:STRING
|
||||||
|
|
||||||
|
PROC print_float()
|
||||||
|
WriteF('\\tf is \\s\\n', RealF(s, !f, 8))
|
||||||
|
ENDPROC
|
||||||
|
|
||||||
|
PROC print_both()
|
||||||
|
WriteF('\\ti is \\d, ', i)
|
||||||
|
print_float()
|
||||||
|
ENDPROC
|
||||||
|
|
||||||
|
/* Square a float */
|
||||||
|
PROC square_float(f) IS !f*f
|
||||||
|
|
||||||
|
/* Square an integer */
|
||||||
|
PROC square_integer(i) IS i*i
|
||||||
|
|
||||||
|
/* Converts a float to an integer */
|
||||||
|
PROC convert_to_integer(f) IS Val(RealF(s, !f, 0))
|
||||||
|
|
||||||
|
/* Converts an integer to a float */
|
||||||
|
PROC convert_to_float(i) IS RealVal(StringF(s, '\\d', i))
|
||||||
|
|
||||||
|
/* This should be the same as Ftan */
|
||||||
|
PROC my_tan(f) IS !Fsin(!f)/Fcos(!f)
|
||||||
|
|
||||||
|
/* This should show float inaccuracies */
|
||||||
|
PROC inaccurate(f) IS Fexp(Flog(!f))
|
||||||
|
|
||||||
|
PROC main()
|
||||||
|
WriteF('Next 2 lines should be the same\\n')
|
||||||
|
f:=2.7; i:=!f!
|
||||||
|
print_both()
|
||||||
|
f:=2.7; i:=convert_to_integer(!f)
|
||||||
|
print_both()
|
||||||
|
|
||||||
|
WriteF('Next 2 lines should be the same\\n')
|
||||||
|
i:=10; f:=i!
|
||||||
|
print_both()
|
||||||
|
i:=10; f:=convert_to_float(i)
|
||||||
|
print_both()
|
||||||
|
|
||||||
|
WriteF('f and i should be the same\\n')
|
||||||
|
i:=square_integer(i)
|
||||||
|
f:=square_float(f)
|
||||||
|
print_both()
|
||||||
|
|
||||||
|
WriteF('Next 2 lines should be the same\\n')
|
||||||
|
f:=Ftan(.8)
|
||||||
|
print_float()
|
||||||
|
f:=my_tan(.8)
|
||||||
|
print_float()
|
||||||
|
|
||||||
|
WriteF('Next 2 lines should be the same\\n')
|
||||||
|
f:=.35
|
||||||
|
print_float()
|
||||||
|
f:=inaccurate(f)
|
||||||
|
print_float()
|
||||||
|
ENDPROC
|
||||||
|
|
||||||
|
The @{b }convert_to_integer@{ub } and @{b }convert_to_float@{ub } functions perform similar
|
||||||
|
conversions to those done by @{b }!@{ub } when it occurs in an expression. To make
|
||||||
|
things more explicit, there are a lot of unnecessary uses of @{b }!@{ub }, and these
|
||||||
|
are when @{b }f@{ub } is passed directly as a parameter to a function (in these
|
||||||
|
cases, the @{b }!@{ub } could safely be omitted). All of the examples have the
|
||||||
|
potential to give different results where they ought to give the same, and
|
||||||
|
this is due to the inaccuracy of floating-point numbers. The last example
|
||||||
|
has been carefully chosen to show this.
|
||||||
|
|
||||||
|
|
||||||
|
@ENDNODE
|
||||||
|
|
||||||
|
@NODE "Accuracy and Range" "Accuracy and Range"
|
||||||
|
@Prev "Floating-Point Functions"
|
||||||
|
@Toc "main"
|
||||||
|
|
||||||
|
Accuracy and Range
|
||||||
|
==================
|
||||||
|
|
||||||
|
A floating-point number is just another 32-bit value, so can be stored
|
||||||
|
in @{b }LONG@{ub } variables. It's just the interpretation of the 32-bits which
|
||||||
|
makes them different. A floating-point number can range from numbers as
|
||||||
|
small as 1.3E-38 to numbers as large as 3.4E+38 (that's very small and
|
||||||
|
very large if you don't understand the scientific notation!). However,
|
||||||
|
not every number in this range can @{fg shine }accurately@{fg text } be represented, since the
|
||||||
|
number of significant digits is roughly eight.
|
||||||
|
|
||||||
|
Accuracy is an important consideration when trying to compare two
|
||||||
|
floating-point numbers and when combining floating-point values after
|
||||||
|
dividing them. It is usually best to check that a floating-point value is
|
||||||
|
in a small range of values, rather than just a particular value. And when
|
||||||
|
combining values, allow for a small amount of error due to rounding etc.
|
||||||
|
See the `Reference Manual' for more details about the implementation of
|
||||||
|
floating-point numbers.
|
||||||
|
|
||||||
|
|
||||||
|
@ENDNODE
|
||||||
|
|
|
@ -0,0 +1,208 @@
|
||||||
|
@database beginner.guide
|
||||||
|
|
||||||
|
@Master beginner
|
||||||
|
|
||||||
|
@Width 75
|
||||||
|
|
||||||
|
|
||||||
|
This is the AmigaGuide® file beginner.guide, produced by Makeinfo-1.55 from
|
||||||
|
the input file beginner.
|
||||||
|
|
||||||
|
|
||||||
|
@NODE "main" "Format and Layout"
|
||||||
|
@Next "Procedures.guide/main"
|
||||||
|
@Prev "Introduction.guide/Summary"
|
||||||
|
@Toc "Contents.guide/main"
|
||||||
|
|
||||||
|
Format and Layout
|
||||||
|
*****************
|
||||||
|
|
||||||
|
In this chapter we'll look at the rules which govern the format and
|
||||||
|
layout of E code. In the previous Part we saw examples of E code that
|
||||||
|
were quite nicely indented and the structure of the program was easily
|
||||||
|
visible. This was just a convention and the E language does not constrain
|
||||||
|
you to write code in this way. However, there are certain rules that must
|
||||||
|
be followed. (This chapter refers to some concepts and parts of the E
|
||||||
|
language which were not covered in Part One. Don't let this put you
|
||||||
|
off--those things will be dealt with in later chapters, and it's maybe a
|
||||||
|
good idea to read this chapter again when they have been.)
|
||||||
|
|
||||||
|
|
||||||
|
@{" Identifiers " Link "Identifiers" }
|
||||||
|
@{" Statements " Link "Statements" }
|
||||||
|
@{" Spacing and Separators " Link "Spacing and Separators" }
|
||||||
|
@{" Comments " Link "Comments" }
|
||||||
|
|
||||||
|
|
||||||
|
@ENDNODE
|
||||||
|
|
||||||
|
@NODE "Identifiers" "Identifiers"
|
||||||
|
@Next "Statements"
|
||||||
|
@Toc "main"
|
||||||
|
|
||||||
|
Identifiers
|
||||||
|
===========
|
||||||
|
|
||||||
|
An @{fg shine }identifier@{fg text } is a word which the compiler must interpret rather than
|
||||||
|
treating literally. For instance, a variable is an identifier, as is a
|
||||||
|
keyword (e.g., @{b }IF@{ub }), but anything in a string is not (e.g., @{b }fred@{ub } in @{b }'fred
|
||||||
|
and wilma'@{ub } is not an identifier). Identifiers can be made up of upper- or
|
||||||
|
lower-case letters, numbers and underscores (the @{b }_@{ub } character). There are
|
||||||
|
only two constraints:
|
||||||
|
|
||||||
|
1. The first character cannot be a number (this would cause confusion
|
||||||
|
with numeric constants).
|
||||||
|
|
||||||
|
2. The case of the first few characters of identifiers is significant.
|
||||||
|
|
||||||
|
For keywords (e.g., @{b }ENDPROC@{ub }), constants (e.g., @{b }TRUE@{ub }) and assembly
|
||||||
|
mnemonics (e.g., @{b }MOVE.L@{ub }) the first two characters must both be uppercase.
|
||||||
|
For E built-in or Amiga system procedures/functions the first character
|
||||||
|
must be uppercase and the second must be lowercase. For all other
|
||||||
|
identifiers (i.e., local, global and procedure parameter variables, object
|
||||||
|
names and element names, procedure names and code labels) the first
|
||||||
|
character must be lowercase.
|
||||||
|
|
||||||
|
Apart from these constraints you are free to write identifiers how you
|
||||||
|
like, although it's arguably more tasteful to use all lowercase for
|
||||||
|
variables and all uppercase for keywords and constants.
|
||||||
|
|
||||||
|
|
||||||
|
@ENDNODE
|
||||||
|
|
||||||
|
@NODE "Statements" "Statements"
|
||||||
|
@Next "Spacing and Separators"
|
||||||
|
@Prev "Identifiers"
|
||||||
|
@Toc "main"
|
||||||
|
|
||||||
|
Statements
|
||||||
|
==========
|
||||||
|
|
||||||
|
A @{fg shine }statement@{fg text } is normally a single instruction to the computer, and
|
||||||
|
each statement normally occupies a single line. If you think of a
|
||||||
|
procedure as a paragraph then a statement is a sentence. Using the same
|
||||||
|
analogy, variables, expressions and keywords are the words which make up
|
||||||
|
the sentence.
|
||||||
|
|
||||||
|
So far in our examples we have met only two kinds of statement: the
|
||||||
|
single line statement and the multi-line statement. The assignments we
|
||||||
|
have seen were single line statements, and the vertical form of the @{b }IF@{ub }
|
||||||
|
block is a multi-line statement. The horizontal form of the @{b }IF@{ub } block was
|
||||||
|
actually the single line statement form of the @{b }IF@{ub } block. Notice that
|
||||||
|
statements can be built up from other statements, as is the case for @{b }IF@{ub }
|
||||||
|
blocks. The code parts between the @{b }IF@{ub }, @{b }ELSEIF@{ub }, @{b }ELSE@{ub } and @{b }ENDIF@{ub } lines are
|
||||||
|
sequences of statements.
|
||||||
|
|
||||||
|
Single line statements can often be very short, and you may be able to
|
||||||
|
fit several of them onto an single line without the line getting too long.
|
||||||
|
To do this in E you use a semi-colon (the @{b };@{ub } character) to separate each
|
||||||
|
statement on the line. For example, the following code fragments are
|
||||||
|
equivalent:
|
||||||
|
|
||||||
|
fred(y,z)
|
||||||
|
y:=x
|
||||||
|
x:=z+1
|
||||||
|
|
||||||
|
fred(y,z); y:=x; x:=z+1
|
||||||
|
|
||||||
|
On the other hand you may want to split a long statement over several
|
||||||
|
lines. This is a bit more tricky because the compiler needs to see that
|
||||||
|
you haven't finished the statement when it gets to the end of a line.
|
||||||
|
Therefore you can only break a statement at certain places. The most
|
||||||
|
common place is after a comma that is part of the statement (like in a
|
||||||
|
procedure call with more than one parameter), but you can also split a
|
||||||
|
line after binary operators and anywhere between opening and closing
|
||||||
|
brackets. The following examples are rather silly but show some allowable
|
||||||
|
line breaking places.
|
||||||
|
|
||||||
|
fred(a, b, c,
|
||||||
|
d, e, f) /* After a comma */
|
||||||
|
|
||||||
|
x:=x+
|
||||||
|
y+
|
||||||
|
z /* After a binary operator */
|
||||||
|
|
||||||
|
x:=(1+2
|
||||||
|
+3) /* Between open...close brackets */
|
||||||
|
|
||||||
|
list:= [ 1,2,
|
||||||
|
[3,4]
|
||||||
|
] /* Between open...close brackets */
|
||||||
|
|
||||||
|
The simple rule is this: if a complete line can be interpreted as a
|
||||||
|
statement then it will be, otherwise it will be interpreted as part of a
|
||||||
|
statement which continues on the following lines.
|
||||||
|
|
||||||
|
Strings may also get a bit long. You can split them over several lines
|
||||||
|
by breaking them into several separate strings and using @{b }+@{ub } between them.
|
||||||
|
If a line ends with a @{b }+@{ub } and the previous thing on the line was a string
|
||||||
|
then the E compiler takes the next string to be a continuation. The
|
||||||
|
following calls to @{b }WriteF@{ub } print the same thing:
|
||||||
|
|
||||||
|
WriteF('This long string can be broken over several lines.\\n')
|
||||||
|
|
||||||
|
WriteF('This long string ' +
|
||||||
|
'can be broken over several lines.\\n')
|
||||||
|
|
||||||
|
WriteF('This long' +
|
||||||
|
' string can be ' +
|
||||||
|
'broken over several ' +
|
||||||
|
'lines.\\n')
|
||||||
|
|
||||||
|
|
||||||
|
@ENDNODE
|
||||||
|
|
||||||
|
@NODE "Spacing and Separators" "Spacing and Separators"
|
||||||
|
@Next "Comments"
|
||||||
|
@Prev "Statements"
|
||||||
|
@Toc "main"
|
||||||
|
|
||||||
|
Spacing and Separators
|
||||||
|
======================
|
||||||
|
|
||||||
|
The examples we've seen so far used a rigid indentation convention
|
||||||
|
which was intended to illuminate the structure of the program. This was
|
||||||
|
just a convention, and the E language places no constraints on the amount
|
||||||
|
of @{fg shine }whitespace@{fg text } (spaces, tabs and linefeeds) you place between statements.
|
||||||
|
However, within statements you must supply enough spacing to make the
|
||||||
|
statement readable. This generally means that you must put whitespace
|
||||||
|
between adjacent identifiers which start or end with a letter, number or
|
||||||
|
underscore (so that the compiler does not think it's one big identifier!).
|
||||||
|
So, in practice, you should put a space after a keyword if it might run
|
||||||
|
into a variable or procedure name. Most other times (like in expressions)
|
||||||
|
identifiers are separated by non-identifier characters (a comma,
|
||||||
|
parenthesis or other symbol).
|
||||||
|
|
||||||
|
|
||||||
|
@ENDNODE
|
||||||
|
|
||||||
|
@NODE "Comments" "Comments"
|
||||||
|
@Prev "Spacing and Separators"
|
||||||
|
@Toc "main"
|
||||||
|
|
||||||
|
Comments
|
||||||
|
========
|
||||||
|
|
||||||
|
A @{fg shine }comment@{fg text } is something that the E compiler ignores and is only there
|
||||||
|
to help the reader. Remember that one day in the future you may be the
|
||||||
|
reader, and it may be quite hard to decipher your own code without a few
|
||||||
|
decent comments! Comments are therefore pretty important.
|
||||||
|
|
||||||
|
You can write comments anywhere you can write whitespace that isn't
|
||||||
|
part of a string. There are two kinds of comment: one uses @{b }/*@{ub } to mark the
|
||||||
|
start of the comment text and @{b }*/@{ub } to mark the end, and the other uses @{b }->@{ub } to
|
||||||
|
mark the start, with the comment text continuing to the end of the line.
|
||||||
|
You must be careful not to write @{b }/*@{ub }, @{b }*/@{ub } or @{b }->@{ub } as part of the comment text,
|
||||||
|
unless part of a nested comment. In practice a comment is best put on a
|
||||||
|
line by itself or after the end of the code on a line.
|
||||||
|
|
||||||
|
/* This line is a comment */
|
||||||
|
x:=1 /* This line contains an assignment then a comment */
|
||||||
|
/* y:=2 /* This whole line is a comment with a nested comment */*/
|
||||||
|
|
||||||
|
x:=1 -> Assignment then a comment
|
||||||
|
-> y:=2 /* A nested comment comment */
|
||||||
|
|
||||||
|
|
||||||
|
@ENDNODE
|
||||||
|
|
File diff suppressed because it is too large
Load Diff
Binary file not shown.
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,557 @@
|
||||||
|
@database beginner.guide
|
||||||
|
|
||||||
|
@Master beginner
|
||||||
|
|
||||||
|
@Width 75
|
||||||
|
|
||||||
|
|
||||||
|
This is the AmigaGuide® file beginner.guide, produced by Makeinfo-1.55 from
|
||||||
|
the input file beginner.
|
||||||
|
|
||||||
|
|
||||||
|
@NODE "main" "Memory Allocation"
|
||||||
|
@Next "FloatingPoint.guide/main"
|
||||||
|
@Prev "Exceptions.guide/main"
|
||||||
|
@Toc "Contents.guide/main"
|
||||||
|
|
||||||
|
Memory Allocation
|
||||||
|
*****************
|
||||||
|
|
||||||
|
When a program is running memory is being used in various different
|
||||||
|
ways. In order to use any memory it must first be @{fg shine }allocated@{fg text }, which is
|
||||||
|
simply a way of marking memory as being `in use'. This is to prevent the
|
||||||
|
same piece of memory being used for different data storage (e.g., by
|
||||||
|
different programs), and so helps prevent corruption of the data stored
|
||||||
|
there. There are two general ways in which memory can be allocated:
|
||||||
|
dynamically and statically.
|
||||||
|
|
||||||
|
|
||||||
|
@{" Static Allocation " Link "Static Allocation" }
|
||||||
|
@{" Deallocation of Memory " Link "Deallocation of Memory" }
|
||||||
|
@{" Dynamic Allocation " Link "Dynamic Allocation" }
|
||||||
|
@{" NEW and END Operators " Link "NEW and END Operators" }
|
||||||
|
|
||||||
|
|
||||||
|
@ENDNODE
|
||||||
|
|
||||||
|
@NODE "Static Allocation" "Static Allocation"
|
||||||
|
@Next "Deallocation of Memory"
|
||||||
|
@Toc "main"
|
||||||
|
|
||||||
|
Static Allocation
|
||||||
|
=================
|
||||||
|
|
||||||
|
@{fg shine }Statically@{fg text } allocated memory is memory allocated by the program for
|
||||||
|
variables and static data like string constants, lists and typed lists
|
||||||
|
(see @{"Static data" Link "Types.guide/Static data" }). Every variable in a program requires some memory in
|
||||||
|
which to store its value. Variables declared to be of type @{b }ARRAY@{ub }, @{b }LIST@{ub },
|
||||||
|
@{b }STRING@{ub } or any object require two lots of memory: one to hold the value of
|
||||||
|
the pointer and one to hold the large amount of data (e.g., the elements
|
||||||
|
in an @{b }ARRAY@{ub }). In fact, such declarations are merely @{b }PTR TO @{ub }@{fg shine }type@{fg text }@{b }@{ub }
|
||||||
|
declarations together with an initialisation of the pointer to the address
|
||||||
|
of some (statically) allocated memory to hold the data. The following
|
||||||
|
example shows very similar declarations, with the difference being that in
|
||||||
|
the second case (using @{b }PTR@{ub }) only memory to hold the pointer values is
|
||||||
|
allocated. The first case also allocates memory to hold the appropriate
|
||||||
|
size of array, object and E-string.
|
||||||
|
|
||||||
|
DEF a[20]:ARRAY, m:myobj, s[10]:STRING
|
||||||
|
|
||||||
|
DEF a:PTR TO CHAR, m:PTR TO myobj, s:PTR TO CHAR
|
||||||
|
|
||||||
|
The pointers in the second case are not initialised by the declaration
|
||||||
|
and, therefore, they are not valid pointers. This means that they should
|
||||||
|
not be dereferenced in any way, until they have been initialised to the
|
||||||
|
address of some allocated memory. This usually involves dynamic
|
||||||
|
allocation of memory (see @{"Dynamic Allocation" Link "Dynamic Allocation" }).
|
||||||
|
|
||||||
|
|
||||||
|
@ENDNODE
|
||||||
|
|
||||||
|
@NODE "Deallocation of Memory" "Deallocation of Memory"
|
||||||
|
@Next "Dynamic Allocation"
|
||||||
|
@Prev "Static Allocation"
|
||||||
|
@Toc "main"
|
||||||
|
|
||||||
|
Deallocation of Memory
|
||||||
|
======================
|
||||||
|
|
||||||
|
When memory is allocated it is, conceptually, marked as being `in use'.
|
||||||
|
This means that this piece of memory cannot be allocated again, so a
|
||||||
|
different piece will be allocated (if any is available) when the program
|
||||||
|
wants to allocate some more. In this way, variables are allocated
|
||||||
|
different pieces of memory, and so their values can be distinct. But
|
||||||
|
there is only a certain amount of memory available, and if it could not be
|
||||||
|
marked as `not in use' again it would soon run out (and the program would
|
||||||
|
come to a nasty end). This is what @{fg shine }deallocation@{fg text } does: it marks
|
||||||
|
previously allocated memory as being `not in use' and so makes it
|
||||||
|
available for allocation again. However, memory should be deallocated
|
||||||
|
only when it is actually no longer in use, and this is where things get a
|
||||||
|
bit complicated.
|
||||||
|
|
||||||
|
Memory is such a vital resource in every computer that it is important
|
||||||
|
to use as little of it as necessary and to deallocate it whenever possible.
|
||||||
|
This is why a programming language like E handles most of the memory
|
||||||
|
allocation for variables. The memory allocated for variables can be
|
||||||
|
automatically deallocated when it is no longer possible for the program to
|
||||||
|
use that variable. However, this automatic deallocation is not useful for
|
||||||
|
global variables, since they can be used from any procedure and so can be
|
||||||
|
deallocated only when the program terminates. A procedure's local
|
||||||
|
variables, on the other hand, are allocated when the procedure is called
|
||||||
|
but cannot be used after the procedure returns. They can, therefore, be
|
||||||
|
deallocated when the procedure returns.
|
||||||
|
|
||||||
|
Pointers, as always, can cause big problems. The following example
|
||||||
|
shows why you need to be careful when using pointers as the return value
|
||||||
|
of a procedure.
|
||||||
|
|
||||||
|
/* This is an example of what *NOT* to do */
|
||||||
|
PROC fullname(first, last)
|
||||||
|
DEF full[40]:STRING
|
||||||
|
StrCopy(full, first)
|
||||||
|
StrAdd(full, ' ')
|
||||||
|
StrAdd(full, last)
|
||||||
|
ENDPROC full
|
||||||
|
|
||||||
|
PROC main()
|
||||||
|
WriteF('Name is \\s\\n', fullname('Fred', 'Flintstone'))
|
||||||
|
ENDPROC
|
||||||
|
|
||||||
|
On first sight this seems fine, and, in fact, it may even work correctly
|
||||||
|
if you run it once or twice (but be careful: it could crash your machine).
|
||||||
|
The problem is that the procedure @{b }fullname@{ub } returns the value of the local
|
||||||
|
variable @{b }full@{ub }, which is a pointer to some statically allocated memory for
|
||||||
|
the E-string and this memory will be deallocated when the procedure
|
||||||
|
returns. This means that the return value of any call to @{b }fullname@{ub } is the
|
||||||
|
address of recently deallocated memory, so it is invalid to dereference it.
|
||||||
|
But the call to @{b }WriteF@{ub } does just that: it dereferences the result of
|
||||||
|
@{b }fullname@{ub } in order to print the E-string it points to. This is a very
|
||||||
|
common problem, because it is such an easy thing to do. The fact that it
|
||||||
|
may, on many occasions, appear to work makes it much harder to find, too.
|
||||||
|
The solution, in this case, is to use dynamic allocation (see
|
||||||
|
@{"Dynamic Allocation" Link "Dynamic Allocation" }).
|
||||||
|
|
||||||
|
If you're still a bit sceptical that this really is a problem, try the
|
||||||
|
above @{b }fullname@{ub } procedure definition with either of these replacement @{b }main@{ub }
|
||||||
|
procedures, but be aware, again, that each one has the potential to crash
|
||||||
|
your machine.
|
||||||
|
|
||||||
|
/* This might not print the correct string */
|
||||||
|
PROC main()
|
||||||
|
DEF f
|
||||||
|
f:=fullname('Fred', 'Flintstone')
|
||||||
|
WriteF('Name is \\s\\n', f)
|
||||||
|
ENDPROC
|
||||||
|
|
||||||
|
/* This will definitely print g instead of f */
|
||||||
|
PROC main()
|
||||||
|
DEF f, g
|
||||||
|
f:=fullname('Fred', 'Flintstone')
|
||||||
|
g:=fullname('Barney', 'Rubble')
|
||||||
|
WriteF('Name is \\s\\n', f)
|
||||||
|
ENDPROC
|
||||||
|
|
||||||
|
(The reason why things go wrong is outlined above, but the reasons why
|
||||||
|
each prints what it does is beyond the scope of this Guide.)
|
||||||
|
|
||||||
|
|
||||||
|
@ENDNODE
|
||||||
|
|
||||||
|
@NODE "Dynamic Allocation" "Dynamic Allocation"
|
||||||
|
@Next "NEW and END Operators"
|
||||||
|
@Prev "Deallocation of Memory"
|
||||||
|
@Toc "main"
|
||||||
|
|
||||||
|
Dynamic Allocation
|
||||||
|
==================
|
||||||
|
|
||||||
|
@{fg shine }Dynamically@{fg text } allocated memory is any memory that is not statically
|
||||||
|
allocated. To allocate memory dynamically you can use the @{b }List@{ub } and @{b }String@{ub }
|
||||||
|
functions, all flavours of @{b }New@{ub }, and the versatile @{b }NEW@{ub } operator. But
|
||||||
|
because the memory is dynamically allocated it must be explicitly
|
||||||
|
deallocated when no longer needed. In all the above cases, though, any
|
||||||
|
memory that is still allocated when the program terminates will be
|
||||||
|
deallocated automatically.
|
||||||
|
|
||||||
|
Another way to allocate memory dynamically is to use the Amiga system
|
||||||
|
functions based on @{b }AllocMem@{ub }. However, these functions require that the
|
||||||
|
memory allocated using them be deallocated (using functions like @{b }FreeMem@{ub })
|
||||||
|
before the program terminates, or else it will never be deallocated (not
|
||||||
|
until your machine is rebooted, anyway). It is safer, therefore, to try
|
||||||
|
to use the E functions for dynamic allocation whenever possible.
|
||||||
|
|
||||||
|
There are many reasons why you might want to use dynamic allocation,
|
||||||
|
and most of them involve initialisation of pointers. For example, the
|
||||||
|
declarations in the section about static allocation can be extended to
|
||||||
|
give initialisations for the pointers declared in the second @{b }DEF@{ub } line (see
|
||||||
|
@{"Static Allocation" Link "Static Allocation" }).
|
||||||
|
|
||||||
|
DEF a[20]:ARRAY, m:myobj, s[10]:STRING
|
||||||
|
|
||||||
|
DEF a:PTR TO CHAR, m:PTR TO myobj, s:PTR TO CHAR
|
||||||
|
a:=New(20)
|
||||||
|
m:=New(SIZEOF myobj)
|
||||||
|
s:=String(20)
|
||||||
|
|
||||||
|
These are initialisations to dynamically allocated memory, whereas the
|
||||||
|
first line of declarations initialise similar pointers to statically
|
||||||
|
allocated memory. If these sections of code were part of a procedure
|
||||||
|
then, since they would now be local variables, there would be one other,
|
||||||
|
significant difference: the dynamically allocated memory would not
|
||||||
|
automatically be deallocated when the procedure returns, whereas the
|
||||||
|
statically allocated memory would. This means that we can solve the
|
||||||
|
deallocation problem (see @{"Deallocation of Memory" Link "Deallocation of Memory" }).
|
||||||
|
|
||||||
|
/* This is the correct way of doing it */
|
||||||
|
PROC fullname(first, last)
|
||||||
|
DEF full
|
||||||
|
full:=String(40)
|
||||||
|
StrCopy(full, first)
|
||||||
|
StrAdd(full, ' ')
|
||||||
|
StrAdd(full, last)
|
||||||
|
ENDPROC full
|
||||||
|
|
||||||
|
PROC main()
|
||||||
|
DEF f, g
|
||||||
|
WriteF('Name is \\s\\n', fullname('Fred', 'Flintstone'))
|
||||||
|
f:=fullname('Fred', 'Flintstone')
|
||||||
|
g:=fullname('Barney', 'Rubble')
|
||||||
|
WriteF('Name is \\s\\n', f)
|
||||||
|
ENDPROC
|
||||||
|
|
||||||
|
The memory for the E-string pointed to by @{b }full@{ub } is now allocated
|
||||||
|
dynamically, using @{b }String@{ub }, and is not deallocated until the end of the
|
||||||
|
program. This means that it is quite valid to pass the value of @{b }full@{ub } as
|
||||||
|
the result of the procedure @{b }fullname@{ub }, and it is quite valid to dereference
|
||||||
|
the result by printing it using @{b }WriteF@{ub }. However, this has caused one last
|
||||||
|
problem: the memory is not deallocated until the end of the program, so is
|
||||||
|
potentially wasted since it could be used, for example, to hold the
|
||||||
|
results of subsequent calls. Of course, the memory can be deallocated
|
||||||
|
only when the data it stores is no longer required. The following
|
||||||
|
replacement @{b }main@{ub } procedure shows when you might want to deallocate the
|
||||||
|
E-string (using @{b }DisposeLink@{ub }).
|
||||||
|
|
||||||
|
PROC main()
|
||||||
|
DEF f, g
|
||||||
|
f:=fullname('Fred', 'Flintstone')
|
||||||
|
WriteF('Name is \\s, f points to $\\h\\n', f, f)
|
||||||
|
/* Try this with and without the next DisposeLink line */
|
||||||
|
DisposeLink(f)
|
||||||
|
g:=fullname('Barney', 'Rubble')
|
||||||
|
WriteF('Name is \\s, g points to $\\h\\n', g, g)
|
||||||
|
DisposeLink(g)
|
||||||
|
ENDPROC
|
||||||
|
|
||||||
|
If you run this with the @{b }DisposeLink(f)@{ub } line you'll probably find that
|
||||||
|
@{b }g@{ub } will be a pointer to the same memory as @{b }f@{ub }. This is because the call
|
||||||
|
to @{b }DisposeLink@{ub } has deallocated the memory pointed to by @{b }f@{ub }, so it can be
|
||||||
|
reused to store the E-string pointed to by @{b }g@{ub }. If you comment out (or
|
||||||
|
delete) the @{b }DisposeLink@{ub } line, then you will find that @{b }f@{ub } and @{b }g@{ub } always point
|
||||||
|
to different memory.
|
||||||
|
|
||||||
|
In some ways it is best to never do any deallocation, because of the
|
||||||
|
problems you can get into if you deallocate memory too early (i.e., before
|
||||||
|
you've finished with the data it contains). Of course, it is safe (but
|
||||||
|
temporarily wasteful) to do this with the E dynamic allocation functions,
|
||||||
|
but it is very wasteful (and wrong) to do this with the Amiga system
|
||||||
|
functions like @{b }AllocMem@{ub }.
|
||||||
|
|
||||||
|
Another benefit of using dynamic allocation is that the size of the
|
||||||
|
arrays, E-lists and E-strings that can be created can be the result of any
|
||||||
|
expression, so is not restricted to constant values. (Remember that the
|
||||||
|
size given on @{b }ARRAY@{ub }, @{b }LIST@{ub } and @{b }STRING@{ub } declarations must be a constant.)
|
||||||
|
This means that the @{b }fullname@{ub } procedure can be made more efficient and
|
||||||
|
allocate only the amount of memory it needs for the E-string it creates.
|
||||||
|
|
||||||
|
PROC fullname(first, last)
|
||||||
|
DEF full
|
||||||
|
/* The extra +1 is for the added space */
|
||||||
|
full:=String(StrLen(first)+StrLen(last)+1)
|
||||||
|
StrCopy(full, first)
|
||||||
|
StrAdd(full, ' ')
|
||||||
|
StrAdd(full, last)
|
||||||
|
ENDPROC full
|
||||||
|
|
||||||
|
However, it may be very complicated or inefficient to calculate the
|
||||||
|
correct size. In these cases, a quick, constant estimate might be better,
|
||||||
|
overall.
|
||||||
|
|
||||||
|
The various functions for allocating memory dynamically have
|
||||||
|
corresponding functions for deallocating that memory. The following table
|
||||||
|
shows some of the more common pairings.
|
||||||
|
|
||||||
|
Allocation Deallocation
|
||||||
|
------------------------------
|
||||||
|
New Dispose
|
||||||
|
NewR Dispose
|
||||||
|
List DisposeLink
|
||||||
|
String DisposeLink
|
||||||
|
NEW END
|
||||||
|
FastNew FastDispose
|
||||||
|
AllocMem FreeMem
|
||||||
|
AllocVec FreeVec
|
||||||
|
AllocDosObject FreeDosObject
|
||||||
|
|
||||||
|
@{b }NEW@{ub } and @{b }END@{ub } are versatile and powerful operators, discussed in the
|
||||||
|
following section. The functions beginning with @{b }Alloc-@{ub } are Amiga system
|
||||||
|
functions and are paired with similarly suffixed functions with a @{b }Free-@{ub }
|
||||||
|
prefix. See the `Rom Kernel Reference Manual' for more details.
|
||||||
|
|
||||||
|
|
||||||
|
@ENDNODE
|
||||||
|
|
||||||
|
@NODE "NEW and END Operators" "NEW and END Operators"
|
||||||
|
@Prev "Dynamic Allocation"
|
||||||
|
@Toc "main"
|
||||||
|
|
||||||
|
@{b }NEW@{ub } and @{b }END@{ub } Operators
|
||||||
|
=====================
|
||||||
|
|
||||||
|
To help deal with dynamic allocation and deallocation of memory there
|
||||||
|
are two, powerful operators, @{b }NEW@{ub } and @{b }END@{ub }. The @{b }NEW@{ub } operator is very
|
||||||
|
versatile, and similar in operation to the @{b }New@{ub } family of built-in
|
||||||
|
functions (see @{"System support functions" Link "BuiltIns.guide/System support functions" }). The @{b }END@{ub } operator is the
|
||||||
|
deallocating complement of @{b }NEW@{ub } (so it is similar to the @{b }Dispose@{ub } family of
|
||||||
|
built-in functions). The major difference between @{b }NEW@{ub } and the various
|
||||||
|
flavours of @{b }New@{ub } is that @{b }NEW@{ub } allocates memory based on the types of its
|
||||||
|
arguments.
|
||||||
|
|
||||||
|
|
||||||
|
@{" Object and simple typed allocation " Link "Object and simple typed allocation" }
|
||||||
|
@{" Array allocation " Link "Array allocation" }
|
||||||
|
@{" List and typed list allocation " Link "List and typed list allocation" }
|
||||||
|
@{" OOP object allocation " Link "OOP object allocation" }
|
||||||
|
|
||||||
|
|
||||||
|
@ENDNODE
|
||||||
|
|
||||||
|
@NODE "Object and simple typed allocation" "Object and simple typed allocation"
|
||||||
|
@Next "Array allocation"
|
||||||
|
@Toc "NEW and END Operators"
|
||||||
|
|
||||||
|
Object and simple typed allocation
|
||||||
|
----------------------------------
|
||||||
|
|
||||||
|
The following sections of code are roughly equivalent and serve to show
|
||||||
|
the function of @{b }NEW@{ub }, and how it is closely related to @{b }NewR@{ub }. (The @{fg shine }type@{fg text }
|
||||||
|
can be any object or simple type.)
|
||||||
|
|
||||||
|
DEF p:PTR TO @{fg shine }type@{fg text }
|
||||||
|
NEW p
|
||||||
|
|
||||||
|
DEF p:PTR TO @{fg shine }type@{fg text }
|
||||||
|
p:=NewR(SIZEOF @{fg shine }type@{fg text })
|
||||||
|
|
||||||
|
Notice that the use of @{b }NEW@{ub } is not like a function call, as there are no
|
||||||
|
parentheses around the parameter @{b }p@{ub }. This is because @{b }NEW@{ub } is an operator
|
||||||
|
rather than a function. It works differently from a function, since it
|
||||||
|
also needs to know the types of its arguments. This means that the
|
||||||
|
declaration of @{b }p@{ub } is very important, since it governs how much memory is
|
||||||
|
allocated by @{b }NEW@{ub }. The version using @{b }NewR@{ub } explicitly gives the amount of
|
||||||
|
memory to be allocated (using the @{b }SIZEOF@{ub } operator), so in this case the
|
||||||
|
declared type of @{b }p@{ub } is not so important for correct allocation.
|
||||||
|
|
||||||
|
The next example shows how @{b }NEW@{ub } can be used to initialise several
|
||||||
|
pointers at once. The second section of code is roughly equivalent, but
|
||||||
|
uses @{b }NewR@{ub }. (Remember that the default type of a variable is @{b }LONG@{ub }, which
|
||||||
|
is actually @{b }PTR TO CHAR@{ub }.)
|
||||||
|
|
||||||
|
DEF p:PTR TO LONG, q:PTR TO myobj, r
|
||||||
|
NEW p, q, r
|
||||||
|
|
||||||
|
DEF p:PTR TO LONG, q:PTR TO myobj, r
|
||||||
|
p:=NewR(SIZEOF LONG)
|
||||||
|
q:=NewR(SIZEOF myobj)
|
||||||
|
r:=NewR(SIZEOF CHAR)
|
||||||
|
|
||||||
|
These first two examples have shown the statement form of @{b }NEW@{ub }. There
|
||||||
|
is also an expression form, which has one parameter and returns the
|
||||||
|
address of the newly allocated memory as well as initialising the argument
|
||||||
|
pointer to this address.
|
||||||
|
|
||||||
|
DEF p:PTR TO myobj, q:PTR TO myobj
|
||||||
|
q:=NEW p
|
||||||
|
|
||||||
|
DEF p:PTR TO myobj, q:PTR TO myobj
|
||||||
|
q:=(p:=NewR(SIZEOF @{fg shine }type@{fg text }))
|
||||||
|
|
||||||
|
This may not seem desperately useful, but it's also the way that @{b }NEW@{ub } is
|
||||||
|
used to allocate copies of lists and typed lists (see
|
||||||
|
@{"List and typed list allocation" Link "List and typed list allocation" }).
|
||||||
|
|
||||||
|
To deallocate memory allocated using @{b }NEW@{ub } you use the @{b }END@{ub } statement with
|
||||||
|
the pointers that you want to deallocate. To work properly, @{b }END@{ub } requires
|
||||||
|
that the type of each pointer matches the type used when it was allocated
|
||||||
|
with @{b }NEW@{ub }. Failure to do this will result in an incorrect amount of memory
|
||||||
|
being deallocated, and this can cause many subtle problems in a program.
|
||||||
|
You must also be careful not to deallocate the same memory twice, and to
|
||||||
|
this end the pointers given to @{b }END@{ub } are re-initialised to @{b }NIL@{ub } after the
|
||||||
|
memory they point to is deallocated (it is quite safe to use @{b }END@{ub } with a
|
||||||
|
pointer which is @{b }NIL@{ub }). This does not catch all problems, however, since
|
||||||
|
more than one pointer can point to the same piece of memory, as shown in
|
||||||
|
the example below.
|
||||||
|
|
||||||
|
DEF p:PTR TO LONG, q:PTR TO LONG
|
||||||
|
q:=NEW p
|
||||||
|
p[]:=-24
|
||||||
|
q[]:=613
|
||||||
|
END p
|
||||||
|
/* p is now NIL, but q is now invalid but not NIL */
|
||||||
|
|
||||||
|
The first assignment initialises @{b }q@{ub } to be the same as @{b }p@{ub } (which is
|
||||||
|
initialised by @{b }NEW@{ub }). @{i }Both@{ui } the next two assignments change the value
|
||||||
|
pointed to by @{i }both@{ui } @{b }p@{ub } and @{b }q@{ub }. The memory allocated to store this value is
|
||||||
|
then deallocated, using @{b }END@{ub }, and this also sets @{b }p@{ub } to @{b }NIL@{ub }. However, the
|
||||||
|
address stored in @{b }q@{ub } is not altered, and still points to the memory that
|
||||||
|
has just been deallocated. This means that @{b }q@{ub } now has a plausible, but
|
||||||
|
invalid, pointer value. The only thing that can safely be done with @{b }q@{ub } is
|
||||||
|
re-initialise it. One of the @{i }worst@{ui } things that could be done is to use it
|
||||||
|
with @{b }END@{ub }, which would deallocate the same memory again, and potentially
|
||||||
|
crash your machine. So, in summary, don't deallocate the same pointer
|
||||||
|
value more than once, and keep track of which variables point to the same
|
||||||
|
memory as others.
|
||||||
|
|
||||||
|
Just as a use of @{b }NEW@{ub } has a simple (but rough) equivalent using @{b }NewR@{ub },
|
||||||
|
@{b }END@{ub } has an equivalent using @{b }Dispose@{ub }, as shown by the following
|
||||||
|
sections of code.
|
||||||
|
|
||||||
|
END p
|
||||||
|
|
||||||
|
IF p
|
||||||
|
Dispose(p)
|
||||||
|
p:=NIL
|
||||||
|
ENDIF
|
||||||
|
|
||||||
|
In fact, it's a tiny bit more complicated than that, since OOP objects are
|
||||||
|
allocated and deallocated using @{b }NEW@{ub } and @{b }END@{ub } (see @{"Object Oriented E" Link "OOE.guide/main" }).
|
||||||
|
|
||||||
|
|
||||||
|
@ENDNODE
|
||||||
|
|
||||||
|
@NODE "Array allocation" "Array allocation"
|
||||||
|
@Next "List and typed list allocation"
|
||||||
|
@Prev "Object and simple typed allocation"
|
||||||
|
@Toc "NEW and END Operators"
|
||||||
|
|
||||||
|
Array allocation
|
||||||
|
----------------
|
||||||
|
|
||||||
|
Arrays can also be allocated using @{b }NEW@{ub }, and this works in a very
|
||||||
|
similar way to that outlined in the previous section. The difference is
|
||||||
|
that the size of the array must also be supplied, in both the use of @{b }NEW@{ub }
|
||||||
|
and @{b }END@{ub }. Of course, the size supplied to @{b }END@{ub } must be the same as the size
|
||||||
|
supplied to the appropriate use of @{b }NEW@{ub }. All this extra effort also gains
|
||||||
|
you the ability to create an array of a size which is not a constant
|
||||||
|
(unlike variables of type @{b }ARRAY@{ub }). This means that the size supplied to
|
||||||
|
@{b }NEW@{ub } and @{b }END@{ub } can be the result of an arbitrary expression.
|
||||||
|
|
||||||
|
DEF a:PTR TO LONG, b:PTR TO myobj, s
|
||||||
|
NEW a[10] /* A dynamic array of LONG */
|
||||||
|
s:=my_random(20)
|
||||||
|
NEW b[s] /* A dynamic array of myobj */
|
||||||
|
/* ...some other code... */
|
||||||
|
END a[10], b[s]
|
||||||
|
|
||||||
|
The @{b }my_random@{ub } function stands for some arbitrary calculation, to show that
|
||||||
|
@{b }s@{ub } does not have to be a constant. This form of @{b }NEW@{ub } can also be used as an
|
||||||
|
expression, as before.
|
||||||
|
|
||||||
|
|
||||||
|
@ENDNODE
|
||||||
|
|
||||||
|
@NODE "List and typed list allocation" "List and typed list allocation"
|
||||||
|
@Next "OOP object allocation"
|
||||||
|
@Prev "Array allocation"
|
||||||
|
@Toc "NEW and END Operators"
|
||||||
|
|
||||||
|
List and typed list allocation
|
||||||
|
------------------------------
|
||||||
|
|
||||||
|
Lists and typed lists are usually static data, but @{b }NEW@{ub } can be used to
|
||||||
|
create dynamically allocated versions. This form of @{b }NEW@{ub } can be used only
|
||||||
|
as an expression, and it takes the list (or typed list) as its argument
|
||||||
|
and returns the address of the dynamically allocated copy of the list.
|
||||||
|
Deallocation of the memory allocated in this way is a bit more complicated
|
||||||
|
than before, but you can, of course, let it be deallocated automatically
|
||||||
|
at the end of the program.
|
||||||
|
|
||||||
|
The following example shows how simple it is to use @{b }NEW@{ub } to cure the
|
||||||
|
static data problem described previously (see @{"Static data" Link "Types.guide/Static data" }). The
|
||||||
|
difference from the original, incorrect program is very subtle.
|
||||||
|
|
||||||
|
PROC main()
|
||||||
|
DEF i, a[10]:ARRAY OF LONG, p:PTR TO LONG
|
||||||
|
FOR i:=0 TO 9
|
||||||
|
a[i]:=NEW [1, i, i*i]
|
||||||
|
/* a[i] is now dynamically allocated */
|
||||||
|
ENDFOR
|
||||||
|
FOR i:=0 TO 9
|
||||||
|
p:=a[i]
|
||||||
|
WriteF('a[\\d] is an array at address \\d\\n', i, p)
|
||||||
|
WriteF(' and the second element is \\d\\n', p[1])
|
||||||
|
ENDFOR
|
||||||
|
ENDPROC
|
||||||
|
|
||||||
|
The minor alteration is to prefix the list with @{b }NEW@{ub }, thereby making the
|
||||||
|
list dynamic. This means that each @{b }a[i]@{ub } is now a different list, rather
|
||||||
|
than the same, static list of the original version of the program.
|
||||||
|
|
||||||
|
Typed lists are allocated in a similar way, and the following example
|
||||||
|
also shows how to deallocate this memory. Basically, you need to know how
|
||||||
|
long the new array is (i.e., how many elements there are), since a typed
|
||||||
|
list is really just an initialised array. You can then deallocate it like
|
||||||
|
a normal array, remembering to use an appropriately typed pointer.
|
||||||
|
Object-typed lists are restricted (when used with @{b }NEW@{ub }) to an array of at
|
||||||
|
most one object, so is useful only for allocating an initialised object
|
||||||
|
(not really an array). Notice how, in the following code, the pointer @{b }q@{ub }
|
||||||
|
can be treated both as an object and as an array of one object (see
|
||||||
|
@{"Element selection and element types" Link "Types.guide/Element selection and element types" }).
|
||||||
|
|
||||||
|
OBJECT myobj
|
||||||
|
x:INT, y:LONG, z:INT
|
||||||
|
ENDOBJECT
|
||||||
|
|
||||||
|
PROC main()
|
||||||
|
DEF p:PTR TO INT, q:PTR TO myobj
|
||||||
|
p:=NEW [1, 9, 3, 7, 6]:INT
|
||||||
|
q:=NEW [1, 2]:myobj
|
||||||
|
WriteF('Last element in array p is \\d\\n', p[4])
|
||||||
|
WriteF('Object q is x=\\d, y=\\d, z=\\d\\n',
|
||||||
|
q.x, q.y, q.z)
|
||||||
|
WriteF('Array q is q[0].x=\\d, q[0].y=\\d, q[0].z=\\d\\n',
|
||||||
|
q[].x, q[].y, q[].z)
|
||||||
|
END p[5], q
|
||||||
|
ENDPROC
|
||||||
|
|
||||||
|
The dynamically allocated version of an object-typed list differs from the
|
||||||
|
static version in another way: it always has memory allocated for a whole
|
||||||
|
number of objects, so a partially initialised object is padded with zero
|
||||||
|
elements. The static version does not allocate this extra padding, so you
|
||||||
|
must be careful not to access any element beyond those mentioned in the
|
||||||
|
list.
|
||||||
|
|
||||||
|
The deallocation of @{b }NEW@{ub } copies of normal lists can, as ever, be left to
|
||||||
|
be done automatically at the end of the program. If you want to
|
||||||
|
deallocate them before this time you must use the function
|
||||||
|
@{b }FastDisposeList@{ub }, passing the address of the list as the only argument.
|
||||||
|
You @{i }must@{ui } not use @{b }END@{ub } or any other method of deallocation. @{b }FastDisposeList@{ub }
|
||||||
|
is the only safe way of deallocating lists allocated using @{b }NEW@{ub }.
|
||||||
|
|
||||||
|
|
||||||
|
@ENDNODE
|
||||||
|
|
||||||
|
@NODE "OOP object allocation" "OOP object allocation"
|
||||||
|
@Prev "List and typed list allocation"
|
||||||
|
@Toc "NEW and END Operators"
|
||||||
|
|
||||||
|
OOP object allocation
|
||||||
|
---------------------
|
||||||
|
|
||||||
|
Currently, the only way to create OOP objects in E is to use @{b }NEW@{ub } and
|
||||||
|
the only safe way to destroy them is to use @{b }END@{ub }. This is probably the
|
||||||
|
most common use of @{b }NEW@{ub } and @{b }END@{ub } and is described in detail later (see
|
||||||
|
@{"Objects in E" Link "OOE.guide/Objects in E" }).
|
||||||
|
|
||||||
|
|
||||||
|
@ENDNODE
|
||||||
|
|
|
@ -0,0 +1,260 @@
|
||||||
|
@database beginner.guide
|
||||||
|
|
||||||
|
@Master beginner
|
||||||
|
|
||||||
|
@Width 75
|
||||||
|
|
||||||
|
|
||||||
|
This is the AmigaGuide® file beginner.guide, produced by Makeinfo-1.55 from
|
||||||
|
the input file beginner.
|
||||||
|
|
||||||
|
|
||||||
|
@NODE "main" "Modules"
|
||||||
|
@Next "Exceptions.guide/main"
|
||||||
|
@Prev "BuiltIns.guide/main"
|
||||||
|
@Toc "Contents.guide/main"
|
||||||
|
|
||||||
|
Modules
|
||||||
|
*******
|
||||||
|
|
||||||
|
A @{fg shine }module@{fg text } is the E equivalent of a C header file and an Assembly
|
||||||
|
include file. It can contain various object and constant definitions, and
|
||||||
|
also library function offsets and library base variables. This information is
|
||||||
|
necessary for the correct use of a library.
|
||||||
|
|
||||||
|
A @{fg shine }code module@{fg text } is an extension of the module idea. As well as object
|
||||||
|
and constant definitions, a code module can contain procedures and global
|
||||||
|
variables. Code modules are like C and Assembly object files.
|
||||||
|
|
||||||
|
|
||||||
|
@{" Using Modules " Link "Using Modules" }
|
||||||
|
@{" Amiga System Modules " Link "Amiga System Modules" }
|
||||||
|
@{" Non-Standard Modules " Link "Non-Standard Modules" }
|
||||||
|
@{" Example Module Use " Link "Example Module Use" }
|
||||||
|
@{" Code Modules " Link "Code Modules" }
|
||||||
|
|
||||||
|
|
||||||
|
@ENDNODE
|
||||||
|
|
||||||
|
@NODE "Using Modules" "Using Modules"
|
||||||
|
@Next "Amiga System Modules"
|
||||||
|
@Toc "main"
|
||||||
|
|
||||||
|
Using Modules
|
||||||
|
=============
|
||||||
|
|
||||||
|
To use the definitions in a particular module you use the @{b }MODULE@{ub }
|
||||||
|
statement at the beginning of your program (before the first procedure
|
||||||
|
definition). You follow the @{b }MODULE@{ub } keyword by a comma-separated list of
|
||||||
|
strings, each of which is the filename (with path if necessary) of a
|
||||||
|
module (without the @{b }.m@{ub } extension--since every module file name must end
|
||||||
|
with @{b }.m@{ub }). The filenames (and paths) are all relative to the logical
|
||||||
|
volume @{b }Emodules:@{ub } (which can be set-up using an @{b }assign@{ub } as described in the
|
||||||
|
`Reference Manual'), unless the first character of the string is @{b }*@{ub }. In
|
||||||
|
this case the files are relative to the directory of the current source
|
||||||
|
file. For instance, the statement:
|
||||||
|
|
||||||
|
MODULE 'fred', 'dir/barney', '*mymod'
|
||||||
|
|
||||||
|
will try to load the files @{b }Emodules:fred.m@{ub }, @{b }Emodules:dir/barney.m@{ub } and
|
||||||
|
@{b }mymod.m@{ub }. If it can't find these files or they aren't proper modules the E
|
||||||
|
compiler will complain.
|
||||||
|
|
||||||
|
All the definitions in the modules included in this way are available
|
||||||
|
to every procedure in the program. To see what a module contains you can
|
||||||
|
use the @{b }showmodule@{ub } program that comes with the Amiga E distribution.
|
||||||
|
|
||||||
|
|
||||||
|
@ENDNODE
|
||||||
|
|
||||||
|
@NODE "Amiga System Modules" "Amiga System Modules"
|
||||||
|
@Next "Non-Standard Modules"
|
||||||
|
@Prev "Using Modules"
|
||||||
|
@Toc "main"
|
||||||
|
|
||||||
|
Amiga System Modules
|
||||||
|
====================
|
||||||
|
|
||||||
|
Amiga E comes with the standard Amiga system include files as E modules.
|
||||||
|
The AmigaDOS 2.04 modules are supplied with E version 2.1, and the
|
||||||
|
AmigaDOS 3.0 modules are supplied with E version 3.0. However, modules
|
||||||
|
are much more useful in E version 3.0 (see @{"Code Modules" Link "Code Modules" }). If you want to
|
||||||
|
use any of the standard Amiga libraries properly you will need to
|
||||||
|
investigate the modules for that library. The top-level @{b }.m@{ub } files in
|
||||||
|
@{b }Emodules:@{ub } contain the library function offsets, and those in directories
|
||||||
|
in @{b }Emodules:@{ub } contain constant and object definitions for the appropriate
|
||||||
|
library. For instance, the module @{b }asl@{ub } (i.e., the file @{b }Emodules:asl.m@{ub })
|
||||||
|
contains the ASL library function offsets and @{b }libraries/asl@{ub } contains the
|
||||||
|
ASL library constants and objects.
|
||||||
|
|
||||||
|
If you are going to use, say, the ASL library then you need to open the
|
||||||
|
library using the @{b }OpenLibrary@{ub } function (an Amiga system function) before
|
||||||
|
you can use any of the library functions. You also need to define the
|
||||||
|
library function offsets by using the @{b }MODULE@{ub } statement. However, the DOS,
|
||||||
|
Exec, Graphics and Intuition libraries don't need to be opened and their
|
||||||
|
function offsets are built in to E. That's why you won't find, for
|
||||||
|
example, a @{b }dos.m@{ub } file in @{b }Emodules:@{ub }. The constants and objects for these
|
||||||
|
libraries still need to be included via modules (they are not built in to
|
||||||
|
E).
|
||||||
|
|
||||||
|
|
||||||
|
@ENDNODE
|
||||||
|
|
||||||
|
@NODE "Non-Standard Modules" "Non-Standard Modules"
|
||||||
|
@Next "Example Module Use"
|
||||||
|
@Prev "Amiga System Modules"
|
||||||
|
@Toc "main"
|
||||||
|
|
||||||
|
Non-Standard Modules
|
||||||
|
====================
|
||||||
|
|
||||||
|
Several non-standard library modules are also supplied with Amiga E. To
|
||||||
|
make your own modules you need the @{b }pragma2module@{ub } and @{b }iconvert@{ub } programs.
|
||||||
|
These convert standard format C header files and Assembly include files to
|
||||||
|
modules. The C header file should contain pragmas for function offsets,
|
||||||
|
and the Assembly include file should contain constant and structure
|
||||||
|
definitions (the Assembly structures will be converted to objects).
|
||||||
|
However, unless you're trying to do really advanced things you probably
|
||||||
|
don't need to worry about any of this!
|
||||||
|
|
||||||
|
|
||||||
|
@ENDNODE
|
||||||
|
|
||||||
|
@NODE "Example Module Use" "Example Module Use"
|
||||||
|
@Next "Code Modules"
|
||||||
|
@Prev "Non-Standard Modules"
|
||||||
|
@Toc "main"
|
||||||
|
|
||||||
|
Example Module Use
|
||||||
|
==================
|
||||||
|
|
||||||
|
The gadget example program in Part Three shows how to use constants
|
||||||
|
from the module @{b }intuition/intuition@{ub } (see @{"Gadgets" Link "Examples.guide/Gadgets" }), and the IDCMP example
|
||||||
|
program shows the object @{b }gadget@{ub } from that module being used (see
|
||||||
|
@{"IDCMP Messages" Link "Examples.guide/IDCMP Messages" }). The following program uses the modules for the Reqtools
|
||||||
|
library, which is not a standard Amiga system library but a commonly used
|
||||||
|
one, and the appropriate modules are supplied with Amiga E. To run this
|
||||||
|
program, you will, of course, need the @{b }reqtools.library@{ub } in @{b }Libs:@{ub }.
|
||||||
|
|
||||||
|
MODULE 'reqtools'
|
||||||
|
|
||||||
|
PROC main()
|
||||||
|
DEF col
|
||||||
|
IF (reqtoolsbase:=OpenLibrary('reqtools.library',37))<>NIL
|
||||||
|
IF (col:=RtPaletteRequestA('Select a colour', 0,0))<>-1
|
||||||
|
RtEZRequestA('You picked colour \\d',
|
||||||
|
'I did|I can\\at remember',0,[col],0)
|
||||||
|
ENDIF
|
||||||
|
CloseLibrary(reqtoolsbase)
|
||||||
|
ELSE
|
||||||
|
WriteF('Could not open reqtools.library, version 37+\\n')
|
||||||
|
ENDIF
|
||||||
|
ENDPROC
|
||||||
|
|
||||||
|
The @{b }reqtoolsbase@{ub } variable is the library base variable for the Reqtools
|
||||||
|
library. This is defined in the module @{b }reqtools@{ub } and you @{i }must@{ui } store the
|
||||||
|
result of the @{b }OpenLibrary@{ub } call in this variable if you are going to use
|
||||||
|
any of the functions from the Reqtools library. (You can find out which
|
||||||
|
variable to use for other libraries by running the @{b }showmodule@{ub } program on
|
||||||
|
the library module.) The two functions the program uses are
|
||||||
|
@{b }RtPaletteRequestA@{ub } and @{b }RtEZRequestA@{ub }. Without the inclusion of the @{b }reqtools@{ub }
|
||||||
|
module and the setting up of the @{b }reqtoolsbase@{ub } variable you would not be
|
||||||
|
able to use these functions. In fact, if you didn't have the @{b }MODULE@{ub } line
|
||||||
|
you wouldn't even be able to compile the program because the compiler
|
||||||
|
wouldn't know where the functions came from and would complain bitterly.
|
||||||
|
|
||||||
|
Notice that the Reqtools library is closed before the program
|
||||||
|
terminates (if it had been successfully opened). This is always
|
||||||
|
necessary: if you succeed in opening a library you @{i }must@{ui } close it when
|
||||||
|
you're finished with it.
|
||||||
|
|
||||||
|
|
||||||
|
@ENDNODE
|
||||||
|
|
||||||
|
@NODE "Code Modules" "Code Modules"
|
||||||
|
@Prev "Example Module Use"
|
||||||
|
@Toc "main"
|
||||||
|
|
||||||
|
Code Modules
|
||||||
|
============
|
||||||
|
|
||||||
|
You can also make modules containing procedure definitions and some
|
||||||
|
global variables. These are called @{fg shine }code modules@{fg text } and can be extremely
|
||||||
|
useful. This section briefly outlines their construction and use. For
|
||||||
|
in-depth details see the `Reference Manual'.
|
||||||
|
|
||||||
|
Code modules can be made by using the E compiler as you would to make
|
||||||
|
an executable, except you put the statement @{b }OPT MODULE@{ub } at the start of the
|
||||||
|
code. Also, any definitions that are to be accessed from outside the
|
||||||
|
module need to be marked with the @{b }EXPORT@{ub } keyword. Alternatively, all
|
||||||
|
definitions can be exported using @{b }OPT EXPORT@{ub } at the start of the code.
|
||||||
|
You include the definitions from this module in your program using @{b }MODULE@{ub }
|
||||||
|
in the normal way.
|
||||||
|
|
||||||
|
The following code is an example of a small module:
|
||||||
|
|
||||||
|
OPT MODULE
|
||||||
|
|
||||||
|
EXPORT CONST MAX_LEN=20
|
||||||
|
|
||||||
|
EXPORT OBJECT fullname
|
||||||
|
firstname, surname
|
||||||
|
ENDOBJECT
|
||||||
|
|
||||||
|
EXPORT PROC printname(p:PTR TO fullname)
|
||||||
|
IF short(p.surname)
|
||||||
|
WriteF('Hello, \\s \\s\\n', p.firstname, p.surname)
|
||||||
|
ELSE
|
||||||
|
WriteF('Gosh, you have a long name\\n')
|
||||||
|
ENDIF
|
||||||
|
ENDPROC
|
||||||
|
|
||||||
|
PROC short(s)
|
||||||
|
RETURN StrLen(s)<MAX_LEN
|
||||||
|
ENDPROC
|
||||||
|
|
||||||
|
Everything is exported except the @{b }short@{ub } procedure. Therefore, this can be
|
||||||
|
accessed only in the module. In fact, the @{b }printname@{ub } procedure uses it
|
||||||
|
(rather artificially) to check the length of the @{b }surname@{ub }. It's not of
|
||||||
|
much use or interest apart from in the module, so that's why it isn't
|
||||||
|
exported. In effect, we've hidden the fact that @{b }printname@{ub } uses @{b }short@{ub } from
|
||||||
|
the user of the module.
|
||||||
|
|
||||||
|
Assuming the above code was compiled to module @{b }mymods/name@{ub }, here's how
|
||||||
|
it could be used:
|
||||||
|
|
||||||
|
MODULE 'mymods/name'
|
||||||
|
|
||||||
|
PROC main()
|
||||||
|
DEF fred:PTR TO fullname, bigname
|
||||||
|
fred.firstname:='Fred'
|
||||||
|
fred.surname:='Flintstone'
|
||||||
|
printname(fred)
|
||||||
|
bigname:=['Peter', 'Extremelybiglongprehistoricname']:fullname
|
||||||
|
printname(bigname)
|
||||||
|
ENDPROC
|
||||||
|
|
||||||
|
Global variables in a module are a bit more problematic than the other
|
||||||
|
kinds of definitions. You cannot initialise them in the declaration or
|
||||||
|
make them reserve chunks memory. So you can't have @{b }ARRAY@{ub }, @{b }OBJECT@{ub }, @{b }STRING@{ub }
|
||||||
|
or @{b }LIST@{ub } declarations. However, you can have pointers so this isn't a big
|
||||||
|
problem. The reason for this limitation is that exported global variables
|
||||||
|
with the same name in a module and the main program are taken to be the
|
||||||
|
same variable, and the values are shared. So you can have an array
|
||||||
|
declaration in the main program:
|
||||||
|
|
||||||
|
DEF a[80]:ARRAY OF INT
|
||||||
|
|
||||||
|
and the appropriate pointer declaration in the module:
|
||||||
|
|
||||||
|
EXPORT DEF a:PTR TO INT
|
||||||
|
|
||||||
|
The array from the main program can then be accessed in the module! For
|
||||||
|
this reason you also need to be pretty careful about the names of your
|
||||||
|
exported variables so you don't get unwanted sharing. Global variables
|
||||||
|
which are not exported are private to the module, so will not clash with
|
||||||
|
variables in the main program or other modules.
|
||||||
|
|
||||||
|
|
||||||
|
@ENDNODE
|
||||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,929 @@
|
||||||
|
@database beginner.guide
|
||||||
|
|
||||||
|
@Master beginner
|
||||||
|
|
||||||
|
@Width 75
|
||||||
|
|
||||||
|
|
||||||
|
This is the AmigaGuide® file beginner.guide, produced by Makeinfo-1.55 from
|
||||||
|
the input file beginner.
|
||||||
|
|
||||||
|
|
||||||
|
@NODE "main" "Object Oriented E"
|
||||||
|
@Next "Examples.guide/main"
|
||||||
|
@Prev "Recursion.guide/main"
|
||||||
|
@Toc "Contents.guide/main"
|
||||||
|
|
||||||
|
Object Oriented E
|
||||||
|
*****************
|
||||||
|
|
||||||
|
The Object Oriented Programming (OOP) aspects of E are covered in this
|
||||||
|
chapter. Don't worry if you don't know the OOP buzz words like `object',
|
||||||
|
`method' and `inheritance': these terms are explained in the OOP
|
||||||
|
introduction, below. (For some reason, computer science uses strange
|
||||||
|
words to cloak simple concepts in secrecy.)
|
||||||
|
|
||||||
|
|
||||||
|
@{" OOP Introduction " Link "OOP Introduction" }
|
||||||
|
@{" Objects in E " Link "Objects in E" }
|
||||||
|
@{" Methods in E " Link "Methods in E" }
|
||||||
|
@{" Inheritance in E " Link "Inheritance in E" }
|
||||||
|
@{" Data-Hiding in E " Link "Data-Hiding in E" }
|
||||||
|
|
||||||
|
|
||||||
|
@ENDNODE
|
||||||
|
|
||||||
|
@NODE "OOP Introduction" "OOP Introduction"
|
||||||
|
@Next "Objects in E"
|
||||||
|
@Toc "main"
|
||||||
|
|
||||||
|
OOP Introduction
|
||||||
|
================
|
||||||
|
|
||||||
|
`Object Oriented Programming' is the name given to a collection of
|
||||||
|
programming techniques that are meant to speed up development and ease
|
||||||
|
maintenance of large programs. These techniques have been around for a
|
||||||
|
long time, but it is only recently that languages that explicitly support
|
||||||
|
them have become popular. You do not @{i }need@{ui } to use a language that supports
|
||||||
|
OOP to program in an Object Oriented way; it's just a bit simpler if you
|
||||||
|
do!
|
||||||
|
|
||||||
|
|
||||||
|
@{" Classes and methods " Link "Classes and methods" }
|
||||||
|
@{" Example class " Link "Example class" }
|
||||||
|
@{" Inheritance " Link "Inheritance" }
|
||||||
|
|
||||||
|
|
||||||
|
@ENDNODE
|
||||||
|
|
||||||
|
@NODE "Classes and methods" "Classes and methods"
|
||||||
|
@Next "Example class"
|
||||||
|
@Toc "OOP Introduction"
|
||||||
|
|
||||||
|
Classes and methods
|
||||||
|
-------------------
|
||||||
|
|
||||||
|
The heart of OOP is the `Black Box' approach to programming. The kind
|
||||||
|
of black box in question is one where the contents are unknown but there
|
||||||
|
is a number of wires on the outside which give you some way of interacting
|
||||||
|
with the stuff on the inside. The black boxes of OOP are actually
|
||||||
|
collections of data (just like the idea of variables that we've already
|
||||||
|
met) and they are called @{fg shine }objects@{fg text } (this is the general term, which is,
|
||||||
|
coincidentally, connected with the @{b }OBJECT@{ub } type in E). Objects can be
|
||||||
|
grouped together in @{fg shine }classes@{fg text }, like the types for variables, except that a
|
||||||
|
class also defines what different kinds of wires protrude from the black
|
||||||
|
box. This extra bit (the wires) is known as the @{fg shine }interface@{fg text } to the
|
||||||
|
object, and is made up of a number of @{fg shine }methods@{fg text } (so a method is analogous
|
||||||
|
to a wire). Each method is actually just like a procedure. With a real
|
||||||
|
black box, the wires are the only way of interacting with the box, so the
|
||||||
|
methods of an object ought to be the @{i }only@{ui } way of creating and using the
|
||||||
|
object. Of course, the methods themselves normally need to know the
|
||||||
|
internal workings of the object, just like the way the wires are normally
|
||||||
|
connected to something inside the black box.
|
||||||
|
|
||||||
|
There are two special kinds of methods: @{fg shine }constructors@{fg text } and
|
||||||
|
@{fg shine }destructors@{fg text }. A @{fg shine }constructor@{fg text } is a method which is used to
|
||||||
|
initialise the data in an object, and a class may have several different
|
||||||
|
constructors (allowing for different kinds of initialisation) or it may
|
||||||
|
have none if no special initialisation is necessary. Constructors are
|
||||||
|
normally used to allocate the resources (such as memory) that an object
|
||||||
|
needs. The deallocation of such resources is done by the @{fg shine }destructor@{fg text }, of
|
||||||
|
which there is at most one for each class.
|
||||||
|
|
||||||
|
Protecting the contents of an object in the `black box' way is known as
|
||||||
|
@{fg shine }data-hiding@{fg text } (the data in the object is visible only to its methods), and
|
||||||
|
only allowing the contents of an object to be manipulated via its
|
||||||
|
interface is known as @{fg shine }data abstraction@{fg text }. By using this approach, only
|
||||||
|
the methods know the structure of the data in an object and so this
|
||||||
|
structure can be changed without affecting the whole of a program: only
|
||||||
|
the methods would potentially need recoding. As you might be able to
|
||||||
|
tell, this simplifies maintenance quite considerably.
|
||||||
|
|
||||||
|
|
||||||
|
@ENDNODE
|
||||||
|
|
||||||
|
@NODE "Example class" "Example class"
|
||||||
|
@Next "Inheritance"
|
||||||
|
@Prev "Classes and methods"
|
||||||
|
@Toc "OOP Introduction"
|
||||||
|
|
||||||
|
Example class
|
||||||
|
-------------
|
||||||
|
|
||||||
|
A good example of a class is the mathematical notion of a set (of
|
||||||
|
integers). A particular object from this class would represent a
|
||||||
|
particular set of integers. The interface for the class would probably
|
||||||
|
include the following methods:
|
||||||
|
|
||||||
|
1. @{b }Add@{ub } -- adds an integer to a set object.
|
||||||
|
|
||||||
|
2. @{b }Member@{ub } -- tests for membership of an integer in a set object.
|
||||||
|
|
||||||
|
3. @{b }Empty@{ub } -- tests for emptiness of a set object.
|
||||||
|
|
||||||
|
4. @{b }Union@{ub } -- unions a set object with a set object.
|
||||||
|
|
||||||
|
A more complete class would also contain methods for removing elements,
|
||||||
|
intersecting sets etc. The important thing to notice is that to use this
|
||||||
|
class you need to know only how to use the methods. The black box
|
||||||
|
approach means that we don't (and shouldn't) know how the set class is
|
||||||
|
actually implemented, i.e., how data is structured within a set object.
|
||||||
|
Only the methods themselves need to know how to manipulate the data that
|
||||||
|
represents a set object.
|
||||||
|
|
||||||
|
The benefit of OOP comes when you actually use the classes, so suppose
|
||||||
|
you implement this set class and then use it in your code for some
|
||||||
|
database program. If you found that the set implementation was a bit
|
||||||
|
inefficient (in terms of memory or speed), then, since you programmed in
|
||||||
|
this OOP way, you wouldn't have to recode the whole database program, just
|
||||||
|
the set class! You can change the way the set data is structured in an
|
||||||
|
object as much and as often as you like, so long as each implementation
|
||||||
|
has the same interface (and gives the same results!).
|
||||||
|
|
||||||
|
|
||||||
|
@ENDNODE
|
||||||
|
|
||||||
|
@NODE "Inheritance" "Inheritance"
|
||||||
|
@Prev "Example class"
|
||||||
|
@Toc "OOP Introduction"
|
||||||
|
|
||||||
|
Inheritance
|
||||||
|
-----------
|
||||||
|
|
||||||
|
The remaining OOP concept of interest is @{fg shine }inheritance@{fg text }. This is a
|
||||||
|
grand name for a way of building on classes that enables the @{fg shine }derived@{fg text }
|
||||||
|
(i.e., bigger) class to be used as if its objects were really members of
|
||||||
|
the inherited, or @{fg shine }base@{fg text }, class. For example, suppose class @{b }D@{ub } were
|
||||||
|
derived from class @{b }B@{ub }, so @{b }D@{ub } is the derived class and @{b }B@{ub } is the base class.
|
||||||
|
In this case, class @{b }D@{ub } inherits the data structure of class @{b }B@{ub }, and may add
|
||||||
|
extra data to it. It also inherits all the methods of class @{b }B@{ub }, and
|
||||||
|
objects of class @{b }D@{ub } may be treated as if they were really objects of class
|
||||||
|
@{b }B@{ub }.
|
||||||
|
|
||||||
|
Of course, an inherited method cannot affect the extra data in class @{b }D@{ub },
|
||||||
|
only the inherited data. To affect the extra data, class @{b }D@{ub } can have extra
|
||||||
|
methods defined, or it can make new definitions for the inherited methods. The
|
||||||
|
latter approach is only really useful if the new definition of an
|
||||||
|
inherited method is pretty similar to the inherited method, differing only
|
||||||
|
in how it affects the extra data in class @{b }D@{ub }. This overriding of methods
|
||||||
|
does not affect the methods in class @{b }B@{ub } (nor those of other classes derived
|
||||||
|
from @{b }B@{ub }), but only those in class @{b }D@{ub } and the classes derived from @{b }D@{ub }.
|
||||||
|
|
||||||
|
|
||||||
|
@ENDNODE
|
||||||
|
|
||||||
|
@NODE "Objects in E" "Objects in E"
|
||||||
|
@Next "Methods in E"
|
||||||
|
@Prev "OOP Introduction"
|
||||||
|
@Toc "main"
|
||||||
|
|
||||||
|
Objects in E
|
||||||
|
============
|
||||||
|
|
||||||
|
Classes are defined using @{b }OBJECT@{ub } in the same way that we've seen before
|
||||||
|
(see @{"OBJECT Type" Link "Types.guide/OBJECT Type" }). So, in E, the terms `object declaration' and `class'
|
||||||
|
may be used interchangeably. However, referring to an @{b }OBJECT@{ub } type as a
|
||||||
|
`class' signals the presence of methods in an object.
|
||||||
|
|
||||||
|
The following example @{b }OBJECT@{ub } is the basis of a set class, as described
|
||||||
|
above (see @{"Example class" Link "Example class" }). This set implementation is going to be quite
|
||||||
|
simple and it will be limited to a maximum of 100 elements.
|
||||||
|
|
||||||
|
OBJECT set
|
||||||
|
elts[100]:ARRAY OF LONG
|
||||||
|
size
|
||||||
|
ENDOBJECT
|
||||||
|
|
||||||
|
Currently, the only way to allocate an OOP object is to use @{b }NEW@{ub } with an
|
||||||
|
appropriately typed pointer. The following sections of code all allocate
|
||||||
|
memory for the data of @{b }set@{ub }, but only the last one allocates an OOP @{b }set@{ub }
|
||||||
|
object. Each one may use and access the @{b }set@{ub } data, but only the last one
|
||||||
|
may call the methods of @{b }set@{ub }.
|
||||||
|
|
||||||
|
DEF s:set
|
||||||
|
|
||||||
|
DEF s:PTR TO set
|
||||||
|
s:=NewR(SIZEOF set)
|
||||||
|
|
||||||
|
DEF s:PTR TO set
|
||||||
|
s:=NEW s
|
||||||
|
|
||||||
|
OOP objects can, of course, be deallocated using @{b }END@{ub }, in which case the
|
||||||
|
destructor for the corresponding class is also called. Leaving an OOP
|
||||||
|
object to be deallocated automatically at the end of the program is not
|
||||||
|
quite as safe as normal, since in this case the destructor will not be
|
||||||
|
called. Also, when using @{b }END@{ub } to deallocate an object you do not need to
|
||||||
|
use a pointer of exactly the same type as the object (like you would for
|
||||||
|
normal @{b }NEW@{ub } allocations). Instead you can use a pointer of any of the base
|
||||||
|
classes' types. Constructors and destructors are described in more detail
|
||||||
|
below.
|
||||||
|
|
||||||
|
|
||||||
|
@ENDNODE
|
||||||
|
|
||||||
|
@NODE "Methods in E" "Methods in E"
|
||||||
|
@Next "Inheritance in E"
|
||||||
|
@Prev "Objects in E"
|
||||||
|
@Toc "main"
|
||||||
|
|
||||||
|
Methods in E
|
||||||
|
============
|
||||||
|
|
||||||
|
The @{fg shine }methods@{fg text } of E are very similar to normal procedures, but there is
|
||||||
|
one, big difference: a method is part of a class, so must somehow be
|
||||||
|
identified with the other parts of the class. In E this identification is
|
||||||
|
done by relating all methods to the corresponding @{b }OBJECT@{ub } type for the
|
||||||
|
class, using the @{b }OF@{ub } keyword after the description of the method's
|
||||||
|
parameters. So, the methods of the simple set class would be defined as
|
||||||
|
outlined below (of course, these examples have omitted the code of
|
||||||
|
methods).
|
||||||
|
|
||||||
|
PROC add(x) OF set
|
||||||
|
/* code for add method */
|
||||||
|
ENDPROC
|
||||||
|
|
||||||
|
PROC member(x) OF set
|
||||||
|
/* code for member method */
|
||||||
|
ENDPROC
|
||||||
|
|
||||||
|
PROC empty() OF set
|
||||||
|
/* code for empty method */
|
||||||
|
ENDPROC
|
||||||
|
|
||||||
|
PROC union(s:PTR TO set) OF set
|
||||||
|
/* code for union method */
|
||||||
|
ENDPROC
|
||||||
|
|
||||||
|
At first sight it might seem that the particular @{b }set@{ub } object which would
|
||||||
|
be manipulated by these methods is missing from the parameters. For
|
||||||
|
instance, it appears that the @{b }empty@{ub } method should need an extra @{b }PTR TO set@{ub }
|
||||||
|
parameter, and that would be the @{b }set@{ub } object it tested for emptiness.
|
||||||
|
However, methods are called in a slightly different way to normal
|
||||||
|
procedures. A method is a part of a class, and is called in a similar way
|
||||||
|
to accessing the data elements of the class. That is, the method is
|
||||||
|
selected using @{b }.@{ub } and acts (implicitly) on the object from which it was
|
||||||
|
selected. The following example shows the allocation of a set object and
|
||||||
|
the use of some of the above methods.
|
||||||
|
|
||||||
|
DEF s:PTR TO set
|
||||||
|
NEW s -> Allocate an OOP object
|
||||||
|
s.add(17)
|
||||||
|
s.add(-34)
|
||||||
|
IF s.empty()
|
||||||
|
WriteF('Error: the set s should not be empty!\\n')
|
||||||
|
ELSE
|
||||||
|
WriteF('OK: not empty\\n')
|
||||||
|
ENDIF
|
||||||
|
IF s.member(0)
|
||||||
|
WriteF('Error: how did 0 get in there?\\n')
|
||||||
|
ELSE
|
||||||
|
WriteF('OK: 0 is not a member\\n')
|
||||||
|
ENDIF
|
||||||
|
IF s.member(-34)
|
||||||
|
WriteF('OK: -34 is a member\\n')
|
||||||
|
ELSE
|
||||||
|
WriteF('Error: where has -34 gone?\\n')
|
||||||
|
ENDIF
|
||||||
|
END s -> Finished with s now
|
||||||
|
|
||||||
|
This is why the methods do not take that extra @{b }PTR TO set@{ub } argument. If a
|
||||||
|
method is called then it has been selected from an appropriate object, and
|
||||||
|
so this must be the object which it affects. The slightly complicated
|
||||||
|
method is @{b }union@{ub } which adds another @{b }set@{ub } object by unioning it. In this
|
||||||
|
case, the argument to the method is a @{b }PTR TO set@{ub }, but this is the set to
|
||||||
|
be added, not the set which is being expanded.
|
||||||
|
|
||||||
|
So, how do you refer to the object which is being affected? In other
|
||||||
|
words, how do you affect it? Well, this is the remaining difference from
|
||||||
|
normal procedures: every method has a special local variable, @{b }self@{ub }, which
|
||||||
|
is of type @{b }PTR TO @{ub }@{fg shine }class@{fg text }@{b }@{ub } and is initialised to point to the object from
|
||||||
|
which the method was selected. Using this variable, the data and methods
|
||||||
|
of object can be accessed and used as normal. For instance, the @{b }empty@{ub }
|
||||||
|
method has a @{b }self@{ub } local variable of type @{b }PTR TO set@{ub }, and can be defined as
|
||||||
|
below:
|
||||||
|
|
||||||
|
PROC empty() OF set IS self.size=0
|
||||||
|
|
||||||
|
@{fg shine }Constructors@{fg text } are simply methods which initialise the data of an
|
||||||
|
object. For this reason they should normally be called only when the
|
||||||
|
object is allocated. The @{b }NEW@{ub } operator allows OOP objects to call a
|
||||||
|
constructor at the point at which they are allocated, to make this easier
|
||||||
|
and more explicit. The constructor will be called after @{b }NEW@{ub } has allocated
|
||||||
|
the memory for the object. It is wise to give constructors suggestive
|
||||||
|
names like @{b }create@{ub } and @{b }copy@{ub }, or the same name as the class. The following
|
||||||
|
constructors might be defined for the set class:
|
||||||
|
|
||||||
|
/* Create empty set */
|
||||||
|
PROC create() OF set
|
||||||
|
self.size=0
|
||||||
|
ENDPROC
|
||||||
|
|
||||||
|
/* Copy existing set */
|
||||||
|
PROC copy(oldset:PTR TO set) OF set
|
||||||
|
DEF i
|
||||||
|
FOR i:=0 TO oldset.size-1
|
||||||
|
self.elements[i]:=oldset.elements[i]
|
||||||
|
ENDFOR
|
||||||
|
self.size:=oldset.size
|
||||||
|
ENDPROC
|
||||||
|
|
||||||
|
They would be used as in the code below. Notice that the @{b }create@{ub }
|
||||||
|
constructor is, in this case, redundant since @{b }NEW@{ub } will initialise the data
|
||||||
|
elements to zero. If @{b }NEW@{ub } does sufficient initialisation then you do not
|
||||||
|
have to define any constructors, and even if you do have constructors you
|
||||||
|
don't @{i }have@{ui } to use them when allocating objects.
|
||||||
|
|
||||||
|
DEF s:PTR TO set, t:PTR TO set, u:PTR TO set
|
||||||
|
NEW s.create()
|
||||||
|
IF s.empty THEN WriteF('s is empty\\n')
|
||||||
|
END s
|
||||||
|
NEW t /* This happens to be the same as using create */
|
||||||
|
IF t.empty THEN WriteF('t is empty\\n')
|
||||||
|
t.add(10)
|
||||||
|
NEW u.copy(t)
|
||||||
|
IF u.member(10) THEN WriteF('10 is in u\\n')
|
||||||
|
END t, u
|
||||||
|
|
||||||
|
For each class there is at most one @{fg shine }destructor@{fg text }, and this is
|
||||||
|
responsible for clearing up and deallocating resources. If one is needed
|
||||||
|
then it must be called @{b }end@{ub }, and (as this might suggest) it is called
|
||||||
|
automatically when an OOP object is deallocated using @{b }END@{ub }. So, for OOP
|
||||||
|
objects with a destructor, the (roughly) equivalent code to @{b }END@{ub } using
|
||||||
|
@{b }Dispose@{ub } is a bit different. Take care to note that the destructor is @{i }not@{ui }
|
||||||
|
called if @{b }END@{ub } is not used to deallocate an OOP object (i.e., if
|
||||||
|
deallocation is left to be done automatically at the end of the program).
|
||||||
|
|
||||||
|
END p
|
||||||
|
|
||||||
|
IF p
|
||||||
|
p.end() -> Call destructor
|
||||||
|
Dispose(p)
|
||||||
|
p:=NIL
|
||||||
|
ENDIF
|
||||||
|
|
||||||
|
The simple implementation of the set class needs no destructor. If,
|
||||||
|
however, the @{b }elements@{ub } data were a pointer (to @{b }LONG@{ub }), and the array were
|
||||||
|
allocated based on some size parameter to a constructor, then a destructor
|
||||||
|
would be useful. In this case the set class would also need a @{b }maxsize@{ub }
|
||||||
|
data element, which records the maximum, allocated size of the @{b }elements@{ub }
|
||||||
|
array.
|
||||||
|
|
||||||
|
OBJECT set
|
||||||
|
elements:PTR TO LONG
|
||||||
|
size
|
||||||
|
maxsize
|
||||||
|
ENDOBJECT
|
||||||
|
|
||||||
|
PROC create(sz=100) OF set -> Default to 100
|
||||||
|
DEF p:PTR TO LONG
|
||||||
|
self.maxsize:=IF (sz>0) AND (sz<100000) THEN sz ELSE 100
|
||||||
|
self.elements:=NEW p[self.maxsize]
|
||||||
|
ENDPROC
|
||||||
|
|
||||||
|
PROC end() OF set
|
||||||
|
DEF p:PTR TO LONG
|
||||||
|
IF self.maxsize=0
|
||||||
|
WriteF('Error: did not create() the set\\n')
|
||||||
|
ELSE
|
||||||
|
p:=self.elements
|
||||||
|
END p[self.maxsize]
|
||||||
|
ENDIF
|
||||||
|
ENDPROC
|
||||||
|
|
||||||
|
Without the destructor @{b }end@{ub }, the memory allocated for @{b }elements@{ub } would not be
|
||||||
|
deallocated when @{b }END@{ub } is used, although it would get deallocated at the end
|
||||||
|
of the program (in this case). However, if @{b }AllocMem@{ub } were used instead of
|
||||||
|
@{b }NEW@{ub } to allocate the array, then the memory would have to be deallocated
|
||||||
|
using @{b }FreeMem@{ub }, and this would best be done in the destructor, as above.
|
||||||
|
(The memory would not be deallocated automatically at the end of the
|
||||||
|
program if @{b }AllocMem@{ub } is used.) Another solution to this kind of problem
|
||||||
|
would be to have a special method which called @{b }FreeMem@{ub }, and to remember to
|
||||||
|
call this method just before deallocating one of these objects, so you can
|
||||||
|
see that the interaction of @{b }END@{ub } with destructors is quite useful.
|
||||||
|
|
||||||
|
Already, the above re-definition of @{b }set@{ub } begins to show the power of OOP.
|
||||||
|
The actual implementation of the set class is very different, but the
|
||||||
|
interface can remain the same. The code for the methods would need to
|
||||||
|
change to take into account the new @{b }maxsize@{ub } element (where before the
|
||||||
|
fixed size of 100 was used), and also to deal with the possibility the
|
||||||
|
@{b }create@{ub } constructor had not been used (in which case @{b }elements@{ub } would be @{b }NIL@{ub }
|
||||||
|
and @{b }maxsize@{ub } zero). But the code which used the set class would not need
|
||||||
|
to change, except maybe to allocate more sensibly sized sets!
|
||||||
|
|
||||||
|
Yet another, different implementation of a set was outlined above (see
|
||||||
|
@{"Binary Trees" Link "Recursion.guide/Binary Trees" }). In fact, remarkably few changes would be needed to convert
|
||||||
|
the code from that section into another implementation of the set class.
|
||||||
|
The @{b }new_set@{ub } procedure is like a set constructor which initialises the set
|
||||||
|
to be a singleton (i.e., to contain one element), and the @{b }add@{ub } procedure is
|
||||||
|
just like the @{b }add@{ub } method of the set class. The only slight problem is
|
||||||
|
that empty sets are not modelled by the binary tree implementation, so it
|
||||||
|
wouldn't, as it stands, be a complete implementation. It would be
|
||||||
|
straight-forward (but unduly complicated at this point) to add support for
|
||||||
|
empty sets to this particular implementation.
|
||||||
|
|
||||||
|
|
||||||
|
@ENDNODE
|
||||||
|
|
||||||
|
@NODE "Inheritance in E" "Inheritance in E"
|
||||||
|
@Next "Data-Hiding in E"
|
||||||
|
@Prev "Methods in E"
|
||||||
|
@Toc "main"
|
||||||
|
|
||||||
|
Inheritance in E
|
||||||
|
================
|
||||||
|
|
||||||
|
One class is @{fg shine }derived@{fg text } from another using the @{b }OF@{ub } keyword in the
|
||||||
|
definition of the derived class @{b }OBJECT@{ub }, in a similar way that @{b }OF@{ub } is used
|
||||||
|
with methods. For instance, the following code shows how to define the
|
||||||
|
class @{b }d@{ub } to be derived from class @{b }b@{ub }. The class @{b }b@{ub } is then said to be
|
||||||
|
@{fg shine }inherited@{fg text } by the class @{b }d@{ub }.
|
||||||
|
|
||||||
|
OBJECT b
|
||||||
|
b_data
|
||||||
|
ENDOBJECT
|
||||||
|
|
||||||
|
OBJECT d OF b
|
||||||
|
extra_d_data
|
||||||
|
ENDOBJECT
|
||||||
|
|
||||||
|
The names @{b }b@{ub } and @{b }d@{ub } have been chosen to be somewhat suggestive, since the
|
||||||
|
class which is inherited (i.e., @{b }b@{ub }) is known as the @{fg shine }base@{fg text } class, whilst
|
||||||
|
the inheriting class (i.e., @{b }d@{ub }) is known as the @{fg shine }derived@{fg text } class.
|
||||||
|
|
||||||
|
The definition of @{b }d@{ub } is the same as the following definition of @{b }duff@{ub },
|
||||||
|
except for one major difference: with the above derivation the methods of
|
||||||
|
@{b }b@{ub } are also inherited by @{b }d@{ub } and they become methods of class @{b }d@{ub }. The
|
||||||
|
definition of @{b }duff@{ub } relates it in no way to @{b }b@{ub }, except at best accidentally
|
||||||
|
(since any changes to @{b }b@{ub } do not affect @{b }duff@{ub }, whereas they would affect @{b }d@{ub }).
|
||||||
|
|
||||||
|
OBJECT duff
|
||||||
|
b_data
|
||||||
|
extra_d_data
|
||||||
|
ENDOBJECT
|
||||||
|
|
||||||
|
One property of this derivation applies to the data records built by
|
||||||
|
@{b }OBJECT@{ub } as well as the OOP classes. The data records of type @{b }d@{ub } or @{b }duff@{ub } may
|
||||||
|
be used wherever a data record of type @{b }b@{ub } were required (e.g., the argument
|
||||||
|
to some procedure), and they are, in fact, indistinguishable from records
|
||||||
|
of type @{b }b@{ub }. Although, if the definition of @{b }b@{ub } were changed (e.g., by
|
||||||
|
changing the name of the @{b }b_data@{ub } element) then data records of type @{b }duff@{ub }
|
||||||
|
would not be usable in this way, but those of type @{b }d@{ub } still would.
|
||||||
|
Therefore, it is wise to use inheritance to show the relationships between
|
||||||
|
classes or data of @{b }OBJECT@{ub } types. The following example shows how
|
||||||
|
procedure @{b }print_b_data@{ub } can validly be called in several ways, given the
|
||||||
|
definitions of @{b }b@{ub }, @{b }d@{ub } and @{b }duff@{ub } above.
|
||||||
|
|
||||||
|
PROC print_b_data(p:PTR TO b)
|
||||||
|
WriteF('b_data = \\d\\n', p.b_data)
|
||||||
|
ENDPROC
|
||||||
|
|
||||||
|
PROC main()
|
||||||
|
DEF p_b:PTR TO b, p_d:PTR TO d, p_duff:PTR TO duff
|
||||||
|
NEW p_b, p_d, p_duff
|
||||||
|
p_b.b_data:=11
|
||||||
|
p_d.b_data:=-3
|
||||||
|
p_duff.b_data:=27
|
||||||
|
WriteF('Printing p_b: ')
|
||||||
|
print_b_data(p_b)
|
||||||
|
WriteF('Printing p_d: ')
|
||||||
|
print_b_data(p_d)
|
||||||
|
WriteF('Printing p_duff: ')
|
||||||
|
print_b_data(p_duff)
|
||||||
|
ENDPROC
|
||||||
|
|
||||||
|
So far, no methods have been defined for @{b }b@{ub }, which means that it is just
|
||||||
|
an @{b }OBJECT@{ub } type. The procedure @{b }print_b_data@{ub } suggests a useful method of @{b }b@{ub },
|
||||||
|
which will be called @{b }print@{ub }.
|
||||||
|
|
||||||
|
PROC print() OF b
|
||||||
|
WriteF('b_data = \\d\\n', self.b_data)
|
||||||
|
ENDPROC
|
||||||
|
|
||||||
|
This definition would also define a @{b }print@{ub } method for @{b }d@{ub }, since @{b }d@{ub } is derived
|
||||||
|
from @{b }b@{ub } and it inherits all the methods of @{b }b@{ub }. However, @{b }duff@{ub } would, of
|
||||||
|
course, still be just an @{b }OBJECT@{ub } type, although it could have a similar
|
||||||
|
@{b }print@{ub } method explicitly defined for it. If @{b }b@{ub } has any methods defined for
|
||||||
|
it (i.e., if it is a class) then data records of type @{b }duff@{ub } cannot be used
|
||||||
|
as if they were @{i }objects@{ui } of the class @{b }b@{ub }, and it is not safe to try! In
|
||||||
|
this case, only objects of derived class @{b }d@{ub } can be used in this manner.
|
||||||
|
(If @{b }b@{ub } is a class then @{b }d@{ub } is a class, due to inheritance.)
|
||||||
|
|
||||||
|
PROC main()
|
||||||
|
DEF p_b:PTR TO b, p_d:PTR TO d, p_duff:PTR TO duff
|
||||||
|
NEW p_b, p_d, p_duff
|
||||||
|
p_b.b_data:=11
|
||||||
|
p_d.b_data:=-3; p_d.extra_d_data:=3
|
||||||
|
p_duff.b_data:=7; p_duff.extra_d_data:=-7
|
||||||
|
WriteF('Printing p_b: ')
|
||||||
|
/* b explicitly has print method */
|
||||||
|
p_b.print()
|
||||||
|
WriteF('Printing p_d: ')
|
||||||
|
/* d inherits print method from b */
|
||||||
|
p_d.print()
|
||||||
|
WriteF('No print method for p_duff\\n')
|
||||||
|
/* Do not try to print p_duff in this way */
|
||||||
|
/* p_duff.print() */
|
||||||
|
ENDPROC
|
||||||
|
|
||||||
|
Unfortunately, the @{b }print@{ub } method inherited by @{b }d@{ub } only prints the @{b }b_data@{ub }
|
||||||
|
element (since it is really a method of @{b }b@{ub }, so cannot access the extra data
|
||||||
|
added in @{b }d@{ub }). However, any inherited method can be overridden by defining
|
||||||
|
it again, this time for the derived class.
|
||||||
|
|
||||||
|
PROC print() OF d
|
||||||
|
WriteF('extra_d_data = \\d, ', self.extra_d_data)
|
||||||
|
WriteF('b_data = \\d\\n', self.b_data)
|
||||||
|
ENDPROC
|
||||||
|
|
||||||
|
With this extra definition, the same @{b }main@{ub } procedure above would now print
|
||||||
|
all the data of @{b }d@{ub }, but only the @{b }b_data@{ub } element of @{b }b@{ub }. This is because the
|
||||||
|
new definition of @{b }print@{ub } affects only class @{b }d@{ub } (and classes derived from @{b }d@{ub }).
|
||||||
|
|
||||||
|
Inherited methods are often overridden just to add extra functionality,
|
||||||
|
as in the case above where we wanted the extra data to be printed as well
|
||||||
|
as the data derived from @{b }b@{ub }. For this purpose, the @{b }SUPER@{ub } operator can be
|
||||||
|
used on a method call to force the base class method to be used, where
|
||||||
|
normally the derived class method would be used. So, the definition of
|
||||||
|
the @{b }print@{ub } method for class @{b }d@{ub } could call the @{b }print@{ub } method of class @{b }b@{ub }.
|
||||||
|
|
||||||
|
PROC print() OF d
|
||||||
|
WriteF('extra_d_data = \\d, ', self.extra_d_data)
|
||||||
|
SUPER self.print()
|
||||||
|
ENDPROC
|
||||||
|
|
||||||
|
Be careful, though, because without the @{b }SUPER@{ub } operator this would involve
|
||||||
|
a recursive call to the @{b }print@{ub } method of class @{b }d@{ub }, rather than a call to the
|
||||||
|
base class method.
|
||||||
|
|
||||||
|
Just as data records of type @{b }d@{ub } can be used wherever data records of
|
||||||
|
type @{b }b@{ub } were required, objects of class @{b }d@{ub } can used in place of objects of
|
||||||
|
class @{b }b@{ub }. The following procedure prints a message and the object data,
|
||||||
|
using the @{b }print@{ub } method of @{b }b@{ub }. (Of course, only the methods named by class
|
||||||
|
@{b }b@{ub } can be used in such a procedure, since the pointer @{b }p@{ub } is of type @{b }PTR
|
||||||
|
TO b@{ub }.)
|
||||||
|
|
||||||
|
PROC msg_print(msg, p:PTR TO b)
|
||||||
|
WriteF('Printing \\s: ', msg)
|
||||||
|
p.print()
|
||||||
|
ENDPROC
|
||||||
|
|
||||||
|
PROC main()
|
||||||
|
DEF p_b:PTR TO b, p_d:PTR TO d
|
||||||
|
NEW p_b, p_d
|
||||||
|
p_b.b_data:=11
|
||||||
|
p_d.b_data:=-3; p_d.extra_d_data:=3
|
||||||
|
msg_print('p_b', p_b)
|
||||||
|
msg_print('p_d', p_d)
|
||||||
|
ENDPROC
|
||||||
|
|
||||||
|
You can't use @{b }duff@{ub } now, since it is not a class and @{b }b@{ub } is, and @{b }msg_print@{ub }
|
||||||
|
expects a pointer to class @{b }b@{ub }. The only other objects that can be passed
|
||||||
|
to @{b }msg_print@{ub } are objects from classes derived from @{b }b@{ub }, and this is why @{b }p_d@{ub }
|
||||||
|
can be printed using @{b }msg_print@{ub }. If you collect together the code and run
|
||||||
|
the example you will see that the call to @{b }print@{ub } in @{b }msg_print@{ub } uses the
|
||||||
|
overridden @{b }print@{ub } method when @{b }msg_print@{ub } is called with @{b }p_d@{ub } as a parameter.
|
||||||
|
That is, the correct method is called even though the pointer @{b }p@{ub } is not of
|
||||||
|
type @{b }PTR TO d@{ub }. This is called @{fg shine }polymorphism@{fg text }: different implementations
|
||||||
|
of @{b }print@{ub } may be called depending on the real, @{fg shine }dynamic@{fg text } type of @{b }p@{ub }. Here's
|
||||||
|
what should be printed:
|
||||||
|
|
||||||
|
Printing p_b: b_data = 11
|
||||||
|
Printing p_d: extra_d_data = 3, b_data = -3
|
||||||
|
|
||||||
|
Inheritance is not limited to a single layer: you can derive other
|
||||||
|
classes from @{b }b@{ub }, you can derive classes from @{b }d@{ub }, and so on. For instance,
|
||||||
|
if class @{b }e@{ub } is derived from class @{b }d@{ub } then it would inherit all the data of @{b }d@{ub }
|
||||||
|
and all the methods of @{b }d@{ub }. This means that @{b }e@{ub } would inherit the richer
|
||||||
|
version of @{b }print@{ub }, and may even override it yet again. In this case, class
|
||||||
|
@{b }e@{ub } would have two base classes, @{b }b@{ub } and @{b }d@{ub }, but would be derived directly from
|
||||||
|
@{b }d@{ub } (and indirectly from @{b }b@{ub }, via @{b }d@{ub }). Class @{b }d@{ub } would therefore be known as the
|
||||||
|
@{fg shine }super@{fg text } class of @{b }e@{ub }, since @{b }e@{ub } is derived directly from @{b }d@{ub }. (The super class
|
||||||
|
of @{b }d@{ub } is its only base class, @{b }b@{ub }.) So, the @{b }SUPER@{ub } operator is actually used
|
||||||
|
to call the methods in the super class. In this example, the @{b }SUPER@{ub }
|
||||||
|
operator can be used in the methods of @{b }e@{ub } to call methods of @{b }d@{ub }.
|
||||||
|
|
||||||
|
The binary tree implementation above (see @{"Binary Trees" Link "Recursion.guide/Binary Trees" }) suggests a good
|
||||||
|
example for a @{fg shine }class hierarchy@{fg text } (a collection of classes related by
|
||||||
|
inheritance). A basic tree structure can be encapsulated in a base class
|
||||||
|
definition, and then specific kinds of tree (with different data at the
|
||||||
|
nodes) can be derived from this. In fact, the base class @{b }tree@{ub } defined
|
||||||
|
below is only useful for inheriting, since a tree is pretty useless
|
||||||
|
without some data attached to the nodes. Since it is very likely that
|
||||||
|
objects of class @{b }tree@{ub } will never be useful (but objects of classes derived
|
||||||
|
from @{b }tree@{ub } would be), the @{b }tree@{ub } class is called an @{fg shine }abstract@{fg text } class.
|
||||||
|
|
||||||
|
OBJECT tree
|
||||||
|
left:PTR TO tree, right:PTR TO tree
|
||||||
|
ENDOBJECT
|
||||||
|
|
||||||
|
PROC nodes() OF tree
|
||||||
|
DEF tot=1
|
||||||
|
IF self.left THEN tot:=tot+self.left.nodes()
|
||||||
|
IF self.right THEN tot:=tot+self.right.nodes()
|
||||||
|
ENDPROC tot
|
||||||
|
|
||||||
|
PROC leaves(show=FALSE) OF tree
|
||||||
|
DEF tot=0
|
||||||
|
IF self.left
|
||||||
|
tot:=tot+self.left.leaves(show)
|
||||||
|
ENDIF
|
||||||
|
IF self.right
|
||||||
|
tot:=tot+self.right.leaves(show)
|
||||||
|
ELSEIF self.left=NIL
|
||||||
|
IF show THEN self.print_node()
|
||||||
|
tot++
|
||||||
|
ENDIF
|
||||||
|
ENDPROC tot
|
||||||
|
|
||||||
|
PROC print_node() OF tree
|
||||||
|
WriteF('<NULL> ')
|
||||||
|
ENDPROC
|
||||||
|
|
||||||
|
PROC print() OF tree
|
||||||
|
IF self.left THEN self.left.print()
|
||||||
|
self.print_node()
|
||||||
|
IF self.right THEN self.right.print()
|
||||||
|
ENDPROC
|
||||||
|
|
||||||
|
The @{b }nodes@{ub } and @{b }leaves@{ub } methods return the number of nodes and leaves of the
|
||||||
|
tree, respectively, with the @{b }leaves@{ub } method taking a flag to specify
|
||||||
|
whether the leaves should also be printed. These methods should never
|
||||||
|
need overriding in a class derived from @{b }tree@{ub }, and neither should @{b }print@{ub },
|
||||||
|
which traverses the tree, printing the nodes from left to right. However,
|
||||||
|
the @{b }print_node@{ub } method probably should be overridden, as is the case in the
|
||||||
|
integer tree defined below.
|
||||||
|
|
||||||
|
OBJECT integer_tree OF tree
|
||||||
|
int
|
||||||
|
ENDOBJECT
|
||||||
|
|
||||||
|
PROC create(i) OF integer_tree
|
||||||
|
self.int:=i
|
||||||
|
ENDPROC
|
||||||
|
|
||||||
|
PROC add(i) OF integer_tree
|
||||||
|
DEF p:PTR TO integer_tree
|
||||||
|
IF i < self.int
|
||||||
|
IF self.left
|
||||||
|
p:=self.left
|
||||||
|
p.add(i)
|
||||||
|
ELSE
|
||||||
|
self.left:=NEW p.create(i)
|
||||||
|
ENDIF
|
||||||
|
ELSEIF i > self.int
|
||||||
|
IF self.right
|
||||||
|
p:=self.right
|
||||||
|
p.add(i)
|
||||||
|
ELSE
|
||||||
|
self.right:=NEW p.create(i)
|
||||||
|
ENDIF
|
||||||
|
ENDIF
|
||||||
|
ENDPROC
|
||||||
|
|
||||||
|
PROC print_node() OF integer_tree
|
||||||
|
WriteF('\\d ', self.int)
|
||||||
|
ENDPROC
|
||||||
|
|
||||||
|
This is a nice example of polymorphism at work: we can implement a @{b }tree@{ub }
|
||||||
|
which works with integers simply by defining the appropriate methods. The
|
||||||
|
@{b }leaves@{ub } method (of the @{b }tree@{ub } class) will then automatically call the
|
||||||
|
@{b }integer_tree@{ub } version of @{b }print_node@{ub } whenever we pass it an @{b }integer_tree@{ub }
|
||||||
|
object. The definitions of @{b }tree@{ub } and @{b }integer_tree@{ub } can even be in different
|
||||||
|
modules (see @{"Data-Hiding in E" Link "Data-Hiding in E" }), and, using these OOP techniques, the
|
||||||
|
module containing @{b }tree@{ub } would not need to be recompiled even if a class
|
||||||
|
like @{b }integer_tree@{ub } is added or changed. This shows why OOP is good for
|
||||||
|
code-reuse and extensibility: with traditional programming techniques we
|
||||||
|
would have to adapt the binary tree functions to account for integers, and
|
||||||
|
again for each new datatype.
|
||||||
|
|
||||||
|
Notice that the recursive use of the new method @{b }add@{ub } must be called via
|
||||||
|
an auxiliary pointer, @{b }p@{ub }, of the derived class. This is because the @{b }left@{ub }
|
||||||
|
and @{b }right@{ub } elements of @{b }tree@{ub } are pointers to @{b }tree@{ub } objects and @{b }add@{ub } is not a
|
||||||
|
method of @{b }tree@{ub } (the compiler would reject the code as a syntax error if
|
||||||
|
you tried to directly access @{b }add@{ub } under these circumstances). Of course,
|
||||||
|
if the @{b }tree@{ub } class had an @{b }add@{ub } method there would not be this problem, but
|
||||||
|
what would the code be for such a method?
|
||||||
|
|
||||||
|
An @{b }add@{ub } method does not really make sense for @{b }tree@{ub }, but if almost all
|
||||||
|
classes derived from @{b }tree@{ub } are going to need such a method it might be nice
|
||||||
|
to include it in the @{b }tree@{ub } base class. This is the purpose of @{fg shine }abstract@{fg text }
|
||||||
|
methods. An @{fg shine }abstract@{fg text } method is one which exists in a base class solely
|
||||||
|
so that it can be overridden in some derived class. Normally, such
|
||||||
|
methods have no sensible definition in the base class, so there is a
|
||||||
|
special keyword, @{b }EMPTY@{ub }, which can be used to define them. For example,
|
||||||
|
the @{b }add@{ub } method in @{b }tree@{ub } would be defined as below.
|
||||||
|
|
||||||
|
PROC add(x) OF tree IS EMPTY
|
||||||
|
|
||||||
|
With this definition, the code for the @{b }add@{ub } method for the @{b }integer_tree@{ub }
|
||||||
|
class could be simplified. (The auxiliary pointer, @{b }p@{ub }, is still needed for
|
||||||
|
use with @{b }NEW@{ub }, since an expression like @{b }self.left@{ub } is not a pointer
|
||||||
|
variable.)
|
||||||
|
|
||||||
|
PROC add(i) OF integer_tree
|
||||||
|
DEF p:PTR TO integer_tree
|
||||||
|
IF i < self.int
|
||||||
|
IF self.left
|
||||||
|
self.left.add(i)
|
||||||
|
ELSE
|
||||||
|
self.left:=NEW p.create(i)
|
||||||
|
ENDIF
|
||||||
|
ELSEIF i > self.int
|
||||||
|
IF self.right
|
||||||
|
self.right.add(i)
|
||||||
|
ELSE
|
||||||
|
self.right:=NEW p.create(i)
|
||||||
|
ENDIF
|
||||||
|
ENDIF
|
||||||
|
ENDPROC
|
||||||
|
|
||||||
|
This, however, is not the best example of an abstract method, since the
|
||||||
|
@{b }add@{ub } method in every class derived from @{b }tree@{ub } must now take a single @{b }LONG@{ub }
|
||||||
|
value as an parameter, in order to be compatible. In general, though, a
|
||||||
|
class representing a tree with node data of type @{fg shine }t@{fg text } would really want an
|
||||||
|
@{b }add@{ub } method to take a single parameter of type @{fg shine }t@{fg text }. The fact that a @{b }LONG@{ub }
|
||||||
|
value can represent a pointer to any type is helpful, here. This means
|
||||||
|
that the definition of @{b }add@{ub } may not be so limiting, after all.
|
||||||
|
|
||||||
|
The @{b }print_node@{ub } method is much more obviously suited to being an
|
||||||
|
abstract method. The above definition prints something silly, because at
|
||||||
|
that point we didn't know about abstract methods and we needed the method
|
||||||
|
to be defined in the base class. A much better definition would make
|
||||||
|
@{b }print_node@{ub } abstract.
|
||||||
|
|
||||||
|
PROC print_node() OF tree IS EMPTY
|
||||||
|
|
||||||
|
It is quite safe to call these abstract methods, even for @{b }tree@{ub } class
|
||||||
|
objects. If a method is still abstract in any class (i.e., it has not
|
||||||
|
been overridden), then calling it on objects of that class has the same
|
||||||
|
effect as calling a function which just returns zero (i.e., it does very
|
||||||
|
little!).
|
||||||
|
|
||||||
|
The @{b }integer_tree@{ub } class could be used like this:
|
||||||
|
|
||||||
|
PROC main()
|
||||||
|
DEF t:PTR TO integer_tree
|
||||||
|
NEW t.create(10)
|
||||||
|
t.add(-10)
|
||||||
|
t.add(3)
|
||||||
|
t.add(5)
|
||||||
|
t.add(-1)
|
||||||
|
t.add(1)
|
||||||
|
WriteF('t has \\d nodes, with \\d leaves: ',
|
||||||
|
t.nodes(), t.leaves())
|
||||||
|
t.leaves(TRUE)
|
||||||
|
WriteF('\\n')
|
||||||
|
WriteF('Contents of t: ')
|
||||||
|
t.print()
|
||||||
|
WriteF('\\n')
|
||||||
|
END t
|
||||||
|
ENDPROC
|
||||||
|
|
||||||
|
|
||||||
|
@ENDNODE
|
||||||
|
|
||||||
|
@NODE "Data-Hiding in E" "Data-Hiding in E"
|
||||||
|
@Prev "Inheritance in E"
|
||||||
|
@Toc "main"
|
||||||
|
|
||||||
|
Data-Hiding in E
|
||||||
|
================
|
||||||
|
|
||||||
|
@{fg shine }Data-hiding@{fg text } is accomplished in E at the module level. This means,
|
||||||
|
effectively, that it is wise to define classes in separate modules (or at
|
||||||
|
least only closely related classes together in a module), taking care to
|
||||||
|
@{b }EXPORT@{ub } only the definitions that you need to. You can also use the
|
||||||
|
@{b }PRIVATE@{ub } keyword in the definition of any @{b }OBJECT@{ub } to hide all the
|
||||||
|
elements following it from code which uses the module (although this does
|
||||||
|
not affect the code within the module). The @{b }PUBLIC@{ub } keyword can be used in
|
||||||
|
a similar way to make the elements which follow visible (i.e., accessible)
|
||||||
|
again, as they are by default. For instance, the following @{b }OBJECT@{ub }
|
||||||
|
definition makes @{b }x@{ub }, @{b }y@{ub }, @{b }a@{ub } and @{b }b@{ub } private (so only visible to the code within
|
||||||
|
the same module), and @{b }p@{ub }, @{b }q@{ub } and @{b }r@{ub } public (so visible to code external to
|
||||||
|
the module, too).
|
||||||
|
|
||||||
|
OBJECT rec
|
||||||
|
p:INT
|
||||||
|
PRIVATE
|
||||||
|
x:INT
|
||||||
|
y
|
||||||
|
PUBLIC
|
||||||
|
q
|
||||||
|
r:PTR TO LONG
|
||||||
|
PRIVATE
|
||||||
|
a:PTR TO LONG, b
|
||||||
|
ENDOBJECT
|
||||||
|
|
||||||
|
For the set class you would probably want to make all the data private
|
||||||
|
and all the methods public. In this way you force programs which use this
|
||||||
|
module to use the supplied interface, rather than fiddling with the set
|
||||||
|
data structures themselves. The following example is the complete code
|
||||||
|
for a simple, inefficient set class, and can be compiled to a module.
|
||||||
|
|
||||||
|
OPT MODULE -> Define class 'set' in a module
|
||||||
|
OPT EXPORT -> Export everything
|
||||||
|
|
||||||
|
/* The data for the class */
|
||||||
|
OBJECT set PRIVATE -> Make all the data private
|
||||||
|
elements:PTR TO LONG
|
||||||
|
maxsize, size
|
||||||
|
ENDOBJECT
|
||||||
|
|
||||||
|
/* Creation constructor */
|
||||||
|
/* Minimum size of 1, maximum 100000, default 100 */
|
||||||
|
PROC create(sz=100) OF set
|
||||||
|
DEF p:PTR TO LONG
|
||||||
|
self.maxsize:=IF (sz>0) AND (sz<100000) THEN sz ELSE 100 -> Check size
|
||||||
|
self.elements:=NEW p[self.maxsize]
|
||||||
|
ENDPROC
|
||||||
|
|
||||||
|
/* Copy constructor */
|
||||||
|
PROC copy(oldset:PTR TO set) OF set
|
||||||
|
DEF i
|
||||||
|
self.create(oldset.maxsize) -> Call create method!
|
||||||
|
FOR i:=0 TO oldset.size-1 -> Copy elements
|
||||||
|
self.elements[i]:=oldset.elements[i]
|
||||||
|
ENDFOR
|
||||||
|
self.size:=oldset.size
|
||||||
|
ENDPROC
|
||||||
|
|
||||||
|
/* Destructor */
|
||||||
|
PROC end() OF set
|
||||||
|
DEF p:PTR TO LONG
|
||||||
|
IF self.maxsize<>0 -> Check that it was allocated
|
||||||
|
p:=self.elements
|
||||||
|
END p[self.maxsize]
|
||||||
|
ENDIF
|
||||||
|
ENDPROC
|
||||||
|
|
||||||
|
/* Add an element */
|
||||||
|
PROC add(x) OF set
|
||||||
|
IF self.member(x)=FALSE -> Is it new? (Call member method!)
|
||||||
|
IF self.size=self.maxsize
|
||||||
|
Raise("full") -> The set is already full
|
||||||
|
ELSE
|
||||||
|
self.elements[self.size]:=x
|
||||||
|
self.size:=self.size+1
|
||||||
|
ENDIF
|
||||||
|
ENDIF
|
||||||
|
ENDPROC
|
||||||
|
|
||||||
|
/* Test for membership */
|
||||||
|
PROC member(x) OF set
|
||||||
|
DEF i
|
||||||
|
FOR i:=0 TO self.size-1
|
||||||
|
IF self.elements[i]=x THEN RETURN TRUE
|
||||||
|
ENDFOR
|
||||||
|
ENDPROC FALSE
|
||||||
|
|
||||||
|
/* Test for emptiness */
|
||||||
|
PROC empty() OF set IS self.size=0
|
||||||
|
|
||||||
|
/* Union (add) another set */
|
||||||
|
PROC union(other:PTR TO set) OF set
|
||||||
|
DEF i
|
||||||
|
FOR i:=0 TO other.size-1
|
||||||
|
self.add(other.elements[i]) -> Call add method!
|
||||||
|
ENDFOR
|
||||||
|
ENDPROC
|
||||||
|
|
||||||
|
/* Print out the contents */
|
||||||
|
PROC print() OF set
|
||||||
|
DEF i
|
||||||
|
WriteF('{ ')
|
||||||
|
FOR i:=0 TO self.size-1
|
||||||
|
WriteF('\\d ', self.elements[i])
|
||||||
|
ENDFOR
|
||||||
|
WriteF('}')
|
||||||
|
ENDPROC
|
||||||
|
|
||||||
|
This class can be used in another module or program, as below:
|
||||||
|
|
||||||
|
MODULE '*set'
|
||||||
|
|
||||||
|
PROC main() HANDLE
|
||||||
|
DEF s=NIL:PTR TO set
|
||||||
|
NEW s.create(20)
|
||||||
|
s.add(1)
|
||||||
|
s.add(-13)
|
||||||
|
s.add(91)
|
||||||
|
s.add(42)
|
||||||
|
s.add(-76)
|
||||||
|
IF s.member(1) THEN WriteF('1 is a member\\n')
|
||||||
|
IF s.member(11) THEN WriteF('11 is a member\\n')
|
||||||
|
WriteF('s = ')
|
||||||
|
s.print()
|
||||||
|
WriteF('\\n')
|
||||||
|
EXCEPT DO
|
||||||
|
END s
|
||||||
|
SELECT exception
|
||||||
|
CASE "NEW"
|
||||||
|
WriteF('Out of memory\\n')
|
||||||
|
CASE "full"
|
||||||
|
WriteF('Set is full\\n')
|
||||||
|
ENDSELECT
|
||||||
|
ENDPROC
|
||||||
|
|
||||||
|
|
||||||
|
@ENDNODE
|
||||||
|
|
|
@ -0,0 +1,306 @@
|
||||||
|
@database beginner.guide
|
||||||
|
|
||||||
|
@Master beginner
|
||||||
|
|
||||||
|
@Width 75
|
||||||
|
|
||||||
|
|
||||||
|
This is the AmigaGuide® file beginner.guide, produced by Makeinfo-1.55 from
|
||||||
|
the input file beginner.
|
||||||
|
|
||||||
|
|
||||||
|
@NODE "main" "Procedures and Functions"
|
||||||
|
@Next "Constants.guide/main"
|
||||||
|
@Prev "Format.guide/main"
|
||||||
|
@Toc "Contents.guide/main"
|
||||||
|
|
||||||
|
Procedures and Functions
|
||||||
|
************************
|
||||||
|
|
||||||
|
A @{fg shine }function@{fg text } is a procedure which returns a value. This value can be
|
||||||
|
formed from any expression so it may depend on the parameters with which
|
||||||
|
the function was called. For instance, the addition operator @{b }+@{ub } can be
|
||||||
|
thought of as a function which returns the sum of its two parameters.
|
||||||
|
|
||||||
|
|
||||||
|
@{" Functions " Link "Functions" }
|
||||||
|
@{" One-Line Functions " Link "One-Line Functions" }
|
||||||
|
@{" Default Arguments " Link "Default Arguments" }
|
||||||
|
@{" Multiple Return Values " Link "Multiple Return Values" }
|
||||||
|
|
||||||
|
|
||||||
|
@ENDNODE
|
||||||
|
|
||||||
|
@NODE "Functions" "Functions"
|
||||||
|
@Next "One-Line Functions"
|
||||||
|
@Toc "main"
|
||||||
|
|
||||||
|
Functions
|
||||||
|
=========
|
||||||
|
|
||||||
|
We can define our own addition function, @{b }add@{ub }, in a very similar way to
|
||||||
|
the definition of a procedure.
|
||||||
|
|
||||||
|
PROC main()
|
||||||
|
DEF sum
|
||||||
|
sum:=12+79
|
||||||
|
WriteF('Using +, sum is \\d\\n', sum)
|
||||||
|
sum:=add(12,79)
|
||||||
|
WriteF('Using add, sum is \\d\\n', sum)
|
||||||
|
ENDPROC
|
||||||
|
|
||||||
|
PROC add(x, y)
|
||||||
|
DEF s
|
||||||
|
s:=x+y
|
||||||
|
ENDPROC s
|
||||||
|
|
||||||
|
This should generate the following output:
|
||||||
|
|
||||||
|
Using +, sum is 91
|
||||||
|
Using add, sum is 91
|
||||||
|
|
||||||
|
In the procedure @{b }add@{ub } the value @{b }s@{ub } is returned using the @{b }ENDPROC@{ub } label. The
|
||||||
|
value returned from @{b }add@{ub } can be used in expressions, just like any other
|
||||||
|
value. You do this by writing the procedure call where you want the value
|
||||||
|
to be. In the above example we wanted the value to be assigned to @{b }sum@{ub } so
|
||||||
|
we wrote the call to @{b }add@{ub } on the right-hand side of the assignment. Notice
|
||||||
|
the similarities between the uses of @{b }+@{ub } and @{b }add@{ub }. In general, @{b }add(a,b)@{ub } can
|
||||||
|
be used in exactly the same places that @{b }a+b@{ub } can (more precisely, it can be
|
||||||
|
used anywhere @{b }(a+b)@{ub } can be used).
|
||||||
|
|
||||||
|
The @{b }RETURN@{ub } keyword can also be used to return values from a procedure.
|
||||||
|
If the @{b }ENDPROC@{ub } method is used then the value is returned when the
|
||||||
|
procedure reaches the end of its code. However, if the @{b }RETURN@{ub } method is
|
||||||
|
used the value is returned immediately at that point and no more of the
|
||||||
|
procedure's code is executed. Here's the same example using @{b }RETURN@{ub }:
|
||||||
|
|
||||||
|
PROC add(x, y)
|
||||||
|
DEF s
|
||||||
|
s:=x+y
|
||||||
|
RETURN s
|
||||||
|
ENDPROC
|
||||||
|
|
||||||
|
The only difference is that you can write @{b }RETURN@{ub } anywhere in the code part
|
||||||
|
of a procedure and it finishes the execution of the procedure at that
|
||||||
|
point (rather than execution finishing when it reaches the end of the
|
||||||
|
code). In fact, you can use @{b }RETURN@{ub } in the @{b }main@{ub } procedure to prematurely
|
||||||
|
finish the execution of a program.
|
||||||
|
|
||||||
|
Here's a slightly more complicated use of @{b }RETURN@{ub }:
|
||||||
|
|
||||||
|
PROC limitedadd(x,y)
|
||||||
|
IF x>10000
|
||||||
|
RETURN 10000
|
||||||
|
ELSEIF x<-10000
|
||||||
|
RETURN -10000
|
||||||
|
ELSE
|
||||||
|
RETURN x+y
|
||||||
|
ENDIF
|
||||||
|
/* The following code is redundant */
|
||||||
|
x:=1
|
||||||
|
IF x=1 THEN RETURN 9999 ELSE RETURN -9999
|
||||||
|
ENDPROC
|
||||||
|
|
||||||
|
This function checks to see if @{b }x@{ub } is greater than 10,000 or less than
|
||||||
|
-10,000, and if it is a limited value is returned (which is generally not
|
||||||
|
the correct sum!). If @{b }x@{ub } is between -10,000 and 10,000 the correct answer
|
||||||
|
is returned. The lines after the first @{b }IF@{ub } block will never get executed
|
||||||
|
because execution will have finished at one of the @{b }RETURN@{ub } lines. Those
|
||||||
|
lines are therefore just a waste of compiler time and can safely be
|
||||||
|
omitted (as the comment suggests).
|
||||||
|
|
||||||
|
If no value is given with the @{b }ENDPROC@{ub } or @{b }RETURN@{ub } keyword then zero is
|
||||||
|
returned. Therefore, all procedures are actually functions (and the terms
|
||||||
|
@{fg shine }procedure@{fg text } and @{fg shine }function@{fg text } will tend to be used interchangeably). So, what
|
||||||
|
happens to the value when you write a procedure call on a line by itself,
|
||||||
|
not in an expression? Well, as we will see, the value is simply discarded
|
||||||
|
(see @{"Turning an Expression into a Statement" Link "MoreExpressions.guide/Turning an Expression into a Statement" }). This is what happened in
|
||||||
|
the previous examples when we called the procedures @{b }fred@{ub } and @{b }WriteF@{ub }.
|
||||||
|
|
||||||
|
|
||||||
|
@ENDNODE
|
||||||
|
|
||||||
|
@NODE "One-Line Functions" "One-Line Functions"
|
||||||
|
@Next "Default Arguments"
|
||||||
|
@Prev "Functions"
|
||||||
|
@Toc "main"
|
||||||
|
|
||||||
|
One-Line Functions
|
||||||
|
==================
|
||||||
|
|
||||||
|
Just as the @{b }IF@{ub } block and @{b }FOR@{ub } loop have horizontal, single line forms,
|
||||||
|
so does a procedure definition. The general form is:
|
||||||
|
|
||||||
|
PROC @{fg shine }name@{fg text } (@{fg shine }arg1@{fg text }, @{fg shine }arg2@{fg text }, ...) IS @{fg shine }expression@{fg text }
|
||||||
|
|
||||||
|
Alternatively, the @{b }RETURN@{ub } keyword can be used:
|
||||||
|
|
||||||
|
PROC @{fg shine }name@{fg text } (@{fg shine }arg1@{fg text }, @{fg shine }arg2@{fg text }, ...) RETURN @{fg shine }expression@{fg text }
|
||||||
|
|
||||||
|
At first sight this might seem pretty unusable, but it is useful for very
|
||||||
|
simple functions and our @{b }add@{ub } function in the previous section is a good
|
||||||
|
example. If you look closely at the original definition you'll see that
|
||||||
|
the local variable @{b }s@{ub } wasn't really needed. Here's the one-line definition
|
||||||
|
of @{b }add@{ub }:
|
||||||
|
|
||||||
|
PROC add(x,y) IS x+y
|
||||||
|
|
||||||
|
|
||||||
|
@ENDNODE
|
||||||
|
|
||||||
|
@NODE "Default Arguments" "Default Arguments"
|
||||||
|
@Next "Multiple Return Values"
|
||||||
|
@Prev "One-Line Functions"
|
||||||
|
@Toc "main"
|
||||||
|
|
||||||
|
Default Arguments
|
||||||
|
=================
|
||||||
|
|
||||||
|
Sometimes a procedure (or function) will quite often be called with a
|
||||||
|
particular (constant) value for one of its parameters, and it might be
|
||||||
|
nice if you didn't have to fill this value in all the time. Luckily, E
|
||||||
|
allows you to define @{fg shine }default@{fg text } values for a procedure's parameters when
|
||||||
|
you define the procedure. You can then just leave out that parameter when
|
||||||
|
you call the procedure and it will default to the value you defined for it.
|
||||||
|
Here's a simple example:
|
||||||
|
|
||||||
|
PROC play(track=1)
|
||||||
|
WriteF('Starting to play track \\d\\n', track)
|
||||||
|
/* Rest of the code... */
|
||||||
|
ENDPROC
|
||||||
|
|
||||||
|
PROC main()
|
||||||
|
play(1) -> Start playing from track 1
|
||||||
|
play(6) -> Start playing from track 6
|
||||||
|
play() -> Start playing from track 1
|
||||||
|
ENDPROC
|
||||||
|
|
||||||
|
This is an outline of a program to control something like a CD player.
|
||||||
|
The @{b }play@{ub } procedure has one parameter, @{b }track@{ub }, which represents the first
|
||||||
|
track that should be played. Often, though, you just tell the CD player
|
||||||
|
to play, and don't specify a particular track. In this case, play starts
|
||||||
|
from the first track. This is exactly what happens in the example above:
|
||||||
|
the @{b }track@{ub } parameter has a default value of 1 defined for it (the @{b }=1@{ub } in the
|
||||||
|
definition of the @{b }play@{ub } procedure), and the third call to @{b }play@{ub } in @{b }main@{ub } does
|
||||||
|
not specify a value for @{b }track@{ub }, so the default value is used.
|
||||||
|
|
||||||
|
There are two constraints on the use of default arguments:
|
||||||
|
|
||||||
|
1. Any number of the parameters of a procedure may have default values
|
||||||
|
defined for them, although they may only be the right-most parameters.
|
||||||
|
This means that for a three parameter procedure, the second parameter
|
||||||
|
can have a default value only if the last parameter does as well, and
|
||||||
|
the first can have one only if both the others do. This should not
|
||||||
|
be a big problem because you can always reorder the parameters in the
|
||||||
|
procedure definition (and in all the places it has been called!).
|
||||||
|
|
||||||
|
The following examples show legal definitions of procedures with
|
||||||
|
default arguments:
|
||||||
|
|
||||||
|
PROC fred(x, y, z) IS x+y+z -> No defaults
|
||||||
|
|
||||||
|
PROC fred(x, y, z=1) IS x+y+z -> z defaults to 1
|
||||||
|
|
||||||
|
PROC fred(x, y=23, z=1) IS x+y+z -> y and z have defaults
|
||||||
|
|
||||||
|
PROC fred(x=9, y=23, z=1) IS x+y+z -> All have defaults
|
||||||
|
|
||||||
|
On the other hand, these definitions are all illegal:
|
||||||
|
|
||||||
|
PROC fred(x, y=23, z) IS x+y+z -> Illegal: no z default
|
||||||
|
|
||||||
|
PROC fred(x=9, y, z=1) IS x+y+z -> Illegal: no y default
|
||||||
|
|
||||||
|
2. When you call a procedure which has default arguments you can only
|
||||||
|
leave out the right-most parameters. This means that for a three
|
||||||
|
parameter procedure with all three parameters having default values,
|
||||||
|
you can leave out the second parameter in a call to this procedure
|
||||||
|
only if you also leave out the third parameter. The first parameter
|
||||||
|
may be left out only if both the others are, too.
|
||||||
|
|
||||||
|
The following example shows which parameters are considered defaults:
|
||||||
|
|
||||||
|
PROC fred(x, y=23, z=1)
|
||||||
|
WriteF('x is \\d, y is \\d, z is \\d\\n', x, y, z)
|
||||||
|
ENDPROC
|
||||||
|
|
||||||
|
PROC main()
|
||||||
|
fred(2, 3, 4) -> No defaults used
|
||||||
|
fred(2, 3) -> z defaults to 1
|
||||||
|
fred(2) -> y and z default
|
||||||
|
fred() -> Illegal: x has no default
|
||||||
|
ENDPROC
|
||||||
|
|
||||||
|
In this example, you cannot leave out the @{b }y@{ub } parameter in a call to
|
||||||
|
@{b }fred@{ub } without leaving out the @{b }z@{ub } parameter as well. To make @{b }y@{ub } have its
|
||||||
|
default value and @{b }z@{ub } some value other than its default you need to
|
||||||
|
supply the @{b }y@{ub } value explicitly in the call:
|
||||||
|
|
||||||
|
fred(2, 23, 9) -> Need to supply 23 for y
|
||||||
|
|
||||||
|
These constraints are necessary in order to make procedure calls
|
||||||
|
unambiguous. Consider a three-parameter procedure with default values for
|
||||||
|
two of the parameters. If it is called with only two parameters then,
|
||||||
|
without these constraints, it would not be clear which two parameters had
|
||||||
|
been supplied and which had not. If, however, the procedure were defined
|
||||||
|
and called according to these constraints, then it must be the third
|
||||||
|
parameter that needs to be defaulted (and the two parameters with default
|
||||||
|
values must be the last two).
|
||||||
|
|
||||||
|
|
||||||
|
@ENDNODE
|
||||||
|
|
||||||
|
@NODE "Multiple Return Values" "Multiple Return Values"
|
||||||
|
@Prev "Default Arguments"
|
||||||
|
@Toc "main"
|
||||||
|
|
||||||
|
Multiple Return Values
|
||||||
|
======================
|
||||||
|
|
||||||
|
So far we've only seen functions which return only one value, since
|
||||||
|
this is something common to most programming languages. However, E allows
|
||||||
|
you to return up to three values from a function. To do this you list the
|
||||||
|
values separated by commas after the @{b }ENDPROC@{ub }, @{b }RETURN@{ub } or @{b }IS@{ub } keyword, where
|
||||||
|
you would normally have specified only one value. A good example is a
|
||||||
|
function which manipulates a screen coordinate, which is a pair of values:
|
||||||
|
the x- and y-coordinates.
|
||||||
|
|
||||||
|
PROC movediag(x, y) IS x+8, y+4
|
||||||
|
|
||||||
|
All this function does is add 8 to the x-coordinate and 4 to the
|
||||||
|
y-coordinate. To get to the return values other than the first one you
|
||||||
|
must use a multiple-assignment statement:
|
||||||
|
|
||||||
|
PROC main()
|
||||||
|
DEF a, b
|
||||||
|
a, b:=movediag(10, 3)
|
||||||
|
/* Now a should be 10+8, and b should be 3+4 */
|
||||||
|
WriteF('a is \\d, b is \\d\\n', a, b)
|
||||||
|
ENDPROC
|
||||||
|
|
||||||
|
@{b }a@{ub } is assigned the first return value and @{b }b@{ub } is assigned the second. You
|
||||||
|
don't need to use all the return values from a function, so the assignment
|
||||||
|
in the example above could have assigned only to @{b }a@{ub } (in which case it would
|
||||||
|
not be a multiple-assignment anymore). A multiple-assignment makes sense
|
||||||
|
only if the right-hand side is a function call, so don't expect things
|
||||||
|
like the following example to set @{b }b@{ub } properly:
|
||||||
|
|
||||||
|
a,b:=6+movediag(10,3) -> No obvious value for b
|
||||||
|
|
||||||
|
If you use a function with more than one return value in any other
|
||||||
|
expression (i.e., something which is not the right-hand side of an
|
||||||
|
assignment), then only the first return value is used. For this reason
|
||||||
|
the return values of a function have special names: the first return value
|
||||||
|
is called the @{fg shine }regular@{fg text } value of the function, and the other values are
|
||||||
|
the @{fg shine }optional@{fg text } values.
|
||||||
|
|
||||||
|
PROC main()
|
||||||
|
DEF a, b
|
||||||
|
/* The next two lines ignore the second return value */
|
||||||
|
a:=movediag(10, 3)
|
||||||
|
WriteF('x-coord of movediag(21, 4) is \\d\\n', movediag(21,4))
|
||||||
|
ENDPROC
|
||||||
|
|
||||||
|
|
||||||
|
@ENDNODE
|
||||||
|
|
|
@ -0,0 +1,423 @@
|
||||||
|
@database beginner.guide
|
||||||
|
|
||||||
|
@Master beginner
|
||||||
|
|
||||||
|
@Width 75
|
||||||
|
|
||||||
|
|
||||||
|
This is the AmigaGuide® file beginner.guide, produced by Makeinfo-1.55 from
|
||||||
|
the input file beginner.
|
||||||
|
|
||||||
|
|
||||||
|
@NODE "main" "Recursion"
|
||||||
|
@Next "OOE.guide/main"
|
||||||
|
@Prev "FloatingPoint.guide/main"
|
||||||
|
@Toc "Contents.guide/main"
|
||||||
|
|
||||||
|
Recursion
|
||||||
|
*********
|
||||||
|
|
||||||
|
A @{fg shine }recursive@{fg text } function is very much like a function which uses a loop.
|
||||||
|
Basically, a @{fg shine }recursive@{fg text } function calls itself (usually after some
|
||||||
|
manipulation of data) rather than iterating a bit of code using a loop.
|
||||||
|
There are also recursive types, which are objects with elements which have
|
||||||
|
the object type (in E these would be pointers to objects). We've already
|
||||||
|
seen a recursive type: linked lists, where each element in the list
|
||||||
|
contains a pointer to the next element (see @{"Linked Lists" Link "Types.guide/Linked Lists" }).
|
||||||
|
|
||||||
|
Recursive definitions are normally much more understandable than an
|
||||||
|
equivalent iterative definition, and it's usually easier to use recursive
|
||||||
|
functions to manipulate this data from a recursive type. However,
|
||||||
|
recursion is by no means a simple topic. Read on at your own peril!
|
||||||
|
|
||||||
|
|
||||||
|
@{" Factorial Example " Link "Factorial Example" }
|
||||||
|
@{" Mutual Recursion " Link "Mutual Recursion" }
|
||||||
|
@{" Binary Trees " Link "Binary Trees" }
|
||||||
|
@{" Stack (and Crashing) " Link "Stack (and Crashing)" }
|
||||||
|
@{" Stack and Exceptions " Link "Stack and Exceptions" }
|
||||||
|
|
||||||
|
|
||||||
|
@ENDNODE
|
||||||
|
|
||||||
|
@NODE "Factorial Example" "Factorial Example"
|
||||||
|
@Next "Mutual Recursion"
|
||||||
|
@Toc "main"
|
||||||
|
|
||||||
|
Factorial Example
|
||||||
|
=================
|
||||||
|
|
||||||
|
The normal example for a recursive definition is the factorial
|
||||||
|
function, so let's not be different. In school mathematics the symbol @{b }!@{ub }
|
||||||
|
is used after a number to denote the factorial of that number (and only
|
||||||
|
positive integers have factorials). n! is n-factorial, which is defined
|
||||||
|
as follows:
|
||||||
|
|
||||||
|
n! = n * (n-1) * (n-2) * ... * 1 (for n >= 1)
|
||||||
|
|
||||||
|
So, 4! is 4*3*2*1, which is 24. And, 5! is 5*4*3*2*1, which is 120.
|
||||||
|
|
||||||
|
Here's the iterative definition of a factorial function (we'll @{b }Raise@{ub } an
|
||||||
|
exception is the number is not positive, but you can safely leave this
|
||||||
|
check out if you are sure the function will be called only with positive
|
||||||
|
numbers):
|
||||||
|
|
||||||
|
PROC fact_iter(n)
|
||||||
|
DEF i, result=1
|
||||||
|
IF n<=0 THEN Raise("FACT")
|
||||||
|
FOR i:=1 TO n
|
||||||
|
result:=result*i
|
||||||
|
ENDFOR
|
||||||
|
ENDPROC result
|
||||||
|
|
||||||
|
We've used a @{b }FOR@{ub } loop to generate the numbers one to @{b }n@{ub } (the parameter to
|
||||||
|
the @{b }fact_iter@{ub }), and @{b }result@{ub } holds the intermediate and final results. The
|
||||||
|
final result is returned, so check that @{b }fact_iter(4)@{ub } returns 24 and
|
||||||
|
@{b }fact_iter(5)@{ub } returns 120 using a @{b }main@{ub } procedure something like this:
|
||||||
|
|
||||||
|
PROC main()
|
||||||
|
WriteF('4! is \\d\\n5! is\\d\\n', fact_iter(4), fact_iter(5))
|
||||||
|
ENDPROC
|
||||||
|
|
||||||
|
If you're really observant you might have noticed that 5! is 5*4!, and,
|
||||||
|
in general, n! is n*(n-1)!. This is our first glimpse of a recursive
|
||||||
|
definition--we can define the factorial function in terms of itself. The
|
||||||
|
real definition of factorial is (the reason why this is the real
|
||||||
|
definition is because the `...' in the previous definition is not
|
||||||
|
sufficiently precise for a mathematical definition):
|
||||||
|
|
||||||
|
1! = 1
|
||||||
|
n! = n * (n-1)! (for n > 1)
|
||||||
|
|
||||||
|
Notice that there are now two cases to consider. The first case is called
|
||||||
|
the @{fg shine }base@{fg text } case and gives an easily calculated value (i.e., no recursion
|
||||||
|
is used). The second case is the @{fg shine }recursive@{fg text } case and gives a definition
|
||||||
|
in terms of a number nearer the base case (i.e., (n-1) is nearer 1 than n,
|
||||||
|
for n>1). The normal problem people get into when using recursion is they
|
||||||
|
forget the base case. Without the base case the definition is meaningless.
|
||||||
|
Without a base case in a recursive program the machine is likely to crash!
|
||||||
|
(See @{"Stack (and Crashing)" Link "Stack (and Crashing)" }.)
|
||||||
|
|
||||||
|
We can now define the recursive version of the @{b }fact_iter@{ub } function
|
||||||
|
(again, we'll use a @{b }Raise@{ub } if the number parameter is not positive):
|
||||||
|
|
||||||
|
PROC fact_rec(n)
|
||||||
|
IF n=1
|
||||||
|
RETURN 1
|
||||||
|
ELSEIF n>=2
|
||||||
|
RETURN n*fact_rec(n-1)
|
||||||
|
ELSE
|
||||||
|
Raise("FACT")
|
||||||
|
ENDIF
|
||||||
|
ENDPROC
|
||||||
|
|
||||||
|
Notice how this looks just like the mathematical definition, and is nice
|
||||||
|
and compact. We can even make a one-line function definition (if we omit
|
||||||
|
the check on the parameter being positive):
|
||||||
|
|
||||||
|
PROC fact_rec2(n) RETURN IF n=1 THEN 1 ELSE n*fact_rec2(n-1)
|
||||||
|
|
||||||
|
You might be tempted to omit the base case and write something like this:
|
||||||
|
|
||||||
|
/* Don't do this! */
|
||||||
|
PROC fact_bad(n) RETURN n*fact_bad(n-1)
|
||||||
|
|
||||||
|
The problem is the recursion will never end. The function @{b }fact_bad@{ub } will
|
||||||
|
be called with every number from @{b }n@{ub } to zero and then all the negative
|
||||||
|
integers. A value will never be returned, and the machine will crash
|
||||||
|
after a while. The precise reason why it will crash is given later (see
|
||||||
|
@{"Stack (and Crashing)" Link "Stack (and Crashing)" }).
|
||||||
|
|
||||||
|
|
||||||
|
@ENDNODE
|
||||||
|
|
||||||
|
@NODE "Mutual Recursion" "Mutual Recursion"
|
||||||
|
@Next "Binary Trees"
|
||||||
|
@Prev "Factorial Example"
|
||||||
|
@Toc "main"
|
||||||
|
|
||||||
|
Mutual Recursion
|
||||||
|
================
|
||||||
|
|
||||||
|
In the previous section we saw the function @{b }fact_rec@{ub } which called
|
||||||
|
itself. If you have two functions, @{b }fun1@{ub } and @{b }fun2@{ub }, and @{b }fun1@{ub } calls @{b }fun2@{ub },
|
||||||
|
and @{b }fun2@{ub } calls @{b }fun1@{ub }, then this pair of functions are @{fg shine }mutually@{fg text } recursive.
|
||||||
|
This extends to any amount of functions linked in this way.
|
||||||
|
|
||||||
|
This is a rather contrived example of a pair of mutually recursive
|
||||||
|
functions.
|
||||||
|
|
||||||
|
PROC f(n)
|
||||||
|
IF n=1
|
||||||
|
RETURN 1
|
||||||
|
ELSEIF n>=2
|
||||||
|
RETURN n*g(n-1)
|
||||||
|
ELSE
|
||||||
|
Raise("F")
|
||||||
|
ENDIF
|
||||||
|
ENDPROC
|
||||||
|
|
||||||
|
PROC g(n)
|
||||||
|
IF n=1
|
||||||
|
RETURN 2*1
|
||||||
|
ELSEIF n>=2
|
||||||
|
RETURN 2*n*f(n-1)
|
||||||
|
ELSE
|
||||||
|
Raise("G")
|
||||||
|
ENDIF
|
||||||
|
ENDPROC
|
||||||
|
|
||||||
|
Both functions are very similar to the @{b }fact_rec@{ub } function, but @{b }g@{ub } returns
|
||||||
|
double the normal values. The overall effect is that every other value in
|
||||||
|
long version of the multiplication is doubled. So, @{b }f(n)@{ub } computes
|
||||||
|
n*(2*(n-1))*(n-2)*(2*(n-3))*...*2 which probably isn't all that
|
||||||
|
interesting.
|
||||||
|
|
||||||
|
|
||||||
|
@ENDNODE
|
||||||
|
|
||||||
|
@NODE "Binary Trees" "Binary Trees"
|
||||||
|
@Next "Stack (and Crashing)"
|
||||||
|
@Prev "Mutual Recursion"
|
||||||
|
@Toc "main"
|
||||||
|
|
||||||
|
Binary Trees
|
||||||
|
============
|
||||||
|
|
||||||
|
This is an example of a recursive type and the effect it has on
|
||||||
|
functions which manipulate this type of data. A @{fg shine }binary tree@{fg text } is like a
|
||||||
|
linked list, but instead of each element containing only one link to
|
||||||
|
another element there are two links in each element of a binary tree
|
||||||
|
(which point to smaller trees called @{fg shine }branches@{fg text }). The first link points
|
||||||
|
to the @{i }left@{ui } branch and the second points to the @{i }right@{ui } branch. Each
|
||||||
|
element of the tree is called a @{fg shine }node@{fg text } and there are two kinds of special
|
||||||
|
node: the start point, called the @{fg shine }root@{fg text } of the tree (like the head of a
|
||||||
|
list), and the nodes which do not have left or right branches (i.e., @{b }NIL@{ub }
|
||||||
|
pointers for both links), called @{fg shine }leaves@{fg text }. Every node of the tree
|
||||||
|
contains some kind of data (just as the linked lists contained an E-string
|
||||||
|
or E-list in each element). The following diagram illustrates a small
|
||||||
|
tree.
|
||||||
|
|
||||||
|
+------+
|
||||||
|
| Root |
|
||||||
|
+--*---+
|
||||||
|
/ \\
|
||||||
|
Left / \\ Right
|
||||||
|
/ \\
|
||||||
|
+------* *------+
|
||||||
|
| Node | | Node |
|
||||||
|
+--*---+ +--*---+
|
||||||
|
/ / \\
|
||||||
|
Left / Left / \\ Right
|
||||||
|
/ / \\
|
||||||
|
+--*---+ +----*-+ +-*----+
|
||||||
|
| Leaf | | Leaf | | Leaf |
|
||||||
|
+------+ +------+ +------+
|
||||||
|
|
||||||
|
Notice that a node might have only one branch (it doesn't have to have
|
||||||
|
both the left and the right). Also, the leaves on the example were all at
|
||||||
|
the same level, but this doesn't have to be the case. Any of the leaves
|
||||||
|
could easily have been a node which had a lot of nodes branching off it.
|
||||||
|
|
||||||
|
So, how can a tree structure like this be written as an E object?
|
||||||
|
Well, the general outline is this:
|
||||||
|
|
||||||
|
OBJECT tree
|
||||||
|
data
|
||||||
|
left:PTR TO tree, right:PTR TO tree
|
||||||
|
ENDOBJECT
|
||||||
|
|
||||||
|
The @{b }left@{ub } and @{b }right@{ub } elements are pointers to the left and right branches
|
||||||
|
(which will be @{b }tree@{ub } objects, too). The @{b }data@{ub } element is some data for each
|
||||||
|
node. This could equally well be a pointer, an @{b }ARRAY@{ub } or a number of
|
||||||
|
different data elements.
|
||||||
|
|
||||||
|
So, what use can be made of such a tree? Well, a common use is for
|
||||||
|
holding a sorted collection of data that needs to be able to have elements
|
||||||
|
added quickly. As an example, the data at each node could be an integer,
|
||||||
|
so a tree of this kind could hold a sorted set of integers. To make the
|
||||||
|
tree sorted, constraints must be placed on the left and right branches of
|
||||||
|
a node. The left branch should contain only nodes with data that is @{i }less@{ui }
|
||||||
|
than the parent node's data, and, similarly, the right branch should
|
||||||
|
contain only nodes with data that is @{i }greater@{ui }. Nodes with the same data
|
||||||
|
could be included in one of the branches, but for our example we'll
|
||||||
|
disallow them. We are now ready to write some functions to manipulate our
|
||||||
|
tree.
|
||||||
|
|
||||||
|
The first function is one which starts off a new set of integers (i.e.,
|
||||||
|
begins a new tree). This should take an integer as a parameter and return
|
||||||
|
a pointer to the root node of new tree (with the integer as that node's
|
||||||
|
data).
|
||||||
|
|
||||||
|
PROC new_set(int)
|
||||||
|
DEF root:PTR TO tree
|
||||||
|
NEW root
|
||||||
|
root.data:=int
|
||||||
|
ENDPROC root
|
||||||
|
|
||||||
|
The memory for the new tree element must be allocated dynamically, so this
|
||||||
|
is a good example of a use of @{b }NEW@{ub }. Since @{b }NEW@{ub } clears the memory it
|
||||||
|
allocates all elements of the new object will be zero. In particular, the
|
||||||
|
@{b }left@{ub } and @{b }right@{ub } pointers will be @{b }NIL@{ub }, so the root node will also be a leaf.
|
||||||
|
If the @{b }NEW@{ub } fails a @{b }"MEM"@{ub } exception is raised; otherwise the data is set to
|
||||||
|
the supplied value and a pointer to the root node is returned.
|
||||||
|
|
||||||
|
To add a new integer to such a set we need to find the appropriate
|
||||||
|
position to insert it and set the left and right branches correctly. This
|
||||||
|
is because if the integer is new to the set it will be added as a new
|
||||||
|
leaf, and so one of the existing nodes will change its left or right
|
||||||
|
branch.
|
||||||
|
|
||||||
|
PROC add(i, set:PTR TO tree)
|
||||||
|
IF set=NIL
|
||||||
|
RETURN new_set(i)
|
||||||
|
ELSE
|
||||||
|
IF i<set.data
|
||||||
|
set.left:=add(i, set.left)
|
||||||
|
ELSEIF i>set.data
|
||||||
|
set.right:=add(i, set.right)
|
||||||
|
ENDIF
|
||||||
|
RETURN set
|
||||||
|
ENDIF
|
||||||
|
ENDPROC
|
||||||
|
|
||||||
|
This function returns a pointer to the set to which it added the integer.
|
||||||
|
If this set was initially empty a new set is created; otherwise the
|
||||||
|
original pointer is returned. The appropriate branches are corrected as
|
||||||
|
the search progresses. Only the last assignment to the left or right
|
||||||
|
branch is significant (all others do not change the value of the pointer),
|
||||||
|
since it is this assignment that adds the new leaf. Here's an iterative
|
||||||
|
version of this function:
|
||||||
|
|
||||||
|
PROC add_iter(i, set:PTR TO tree)
|
||||||
|
DEF node:PTR TO tree
|
||||||
|
IF set=NIL
|
||||||
|
RETURN new_set(i)
|
||||||
|
ELSE
|
||||||
|
node:=set
|
||||||
|
LOOP
|
||||||
|
IF i<node.data
|
||||||
|
IF node.left=NIL
|
||||||
|
node.left:=new_set(i)
|
||||||
|
RETURN set
|
||||||
|
ELSE
|
||||||
|
node:=node.left
|
||||||
|
ENDIF
|
||||||
|
ELSEIF i>node.data
|
||||||
|
IF node.right=NIL
|
||||||
|
node.right:=new_set(i)
|
||||||
|
RETURN set
|
||||||
|
ELSE
|
||||||
|
node:=node.right
|
||||||
|
ENDIF
|
||||||
|
ELSE
|
||||||
|
RETURN set
|
||||||
|
ENDIF
|
||||||
|
ENDLOOP
|
||||||
|
ENDIF
|
||||||
|
ENDPROC
|
||||||
|
|
||||||
|
As you can see, it's quite a bit messier. Recursive functions work well
|
||||||
|
with manipulation of recursive types.
|
||||||
|
|
||||||
|
Another really neat example is printing the contents of the set. It's
|
||||||
|
deceptively simple:
|
||||||
|
|
||||||
|
PROC show(set:PTR TO tree)
|
||||||
|
IF set<>NIL
|
||||||
|
show(set.left)
|
||||||
|
WriteF('\\d ', set.data)
|
||||||
|
show(set.right)
|
||||||
|
ENDIF
|
||||||
|
ENDPROC
|
||||||
|
|
||||||
|
The integers in the nodes will get printed in order (providing they were
|
||||||
|
added using the @{b }add@{ub } function). The left-hand nodes contain the smallest
|
||||||
|
elements so the data they contain is printed first, followed by the data
|
||||||
|
at the current node, and then that in the right-hand nodes. Try writing
|
||||||
|
an iterative version of this function if you fancy a really tough problem.
|
||||||
|
|
||||||
|
Putting everything together, here's a @{b }main@{ub } procedure which can be used
|
||||||
|
to test the above functions:
|
||||||
|
|
||||||
|
PROC main() HANDLE
|
||||||
|
DEF s, i, j
|
||||||
|
Rnd(-999999) /* Initialise seed */
|
||||||
|
s:=new_set(10) /* Initialise set s to contain the number 10 */
|
||||||
|
WriteF('Input:\\n')
|
||||||
|
FOR i:=1 TO 50 /* Generate 50 random numbers and add them to set s */
|
||||||
|
j:=Rnd(100)
|
||||||
|
add(j, s)
|
||||||
|
WriteF('\\d ',j)
|
||||||
|
ENDFOR
|
||||||
|
WriteF('\\nOutput:\\n')
|
||||||
|
show(s) /* Show the contents of the (sorted) set s */
|
||||||
|
WriteF('\\n')
|
||||||
|
EXCEPT
|
||||||
|
IF exception="NEW" THEN WriteF('Ran out of memory\\n')
|
||||||
|
ENDPROC
|
||||||
|
|
||||||
|
|
||||||
|
@ENDNODE
|
||||||
|
|
||||||
|
@NODE "Stack (and Crashing)" "Stack (and Crashing)"
|
||||||
|
@Next "Stack and Exceptions"
|
||||||
|
@Prev "Binary Trees"
|
||||||
|
@Toc "main"
|
||||||
|
|
||||||
|
Stack (and Crashing)
|
||||||
|
====================
|
||||||
|
|
||||||
|
When you call a procedure you use up a bit of the program's @{fg shine }stack@{fg text }.
|
||||||
|
The stack is used to keep track of procedures in a program which haven't
|
||||||
|
finished, and real problems can arise when the stack space runs out.
|
||||||
|
Normally, the amount of stack available to each program is sufficient,
|
||||||
|
since the E compiler handles all the fiddly bits quite well. However,
|
||||||
|
programs which use a lot of recursion can quite easily run out of stack.
|
||||||
|
|
||||||
|
For example, the @{b }fact_rec(10)@{ub } will need enough stack for ten calls of
|
||||||
|
@{b }fact_rec@{ub }, nine of which are recursively called. This is because each call
|
||||||
|
does not finish until the return value has been computed, so all recursive
|
||||||
|
calls up to @{b }fact_rec(1)@{ub } need to be kept on the stack until @{b }fact_rec(1)@{ub }
|
||||||
|
returns one. Then each procedure will be taken off the stack as they
|
||||||
|
finish. If you try to compute @{b }fact_rec(40000)@{ub }, not only will this take a
|
||||||
|
long time, but it will probably run out of stack space. When it does run
|
||||||
|
out of stack, the machine will probably crash or do other weird things.
|
||||||
|
The iterative version, @{b }fact_iter@{ub } does not have these problems, since it
|
||||||
|
only takes one procedure call to calculate a factorial using this function.
|
||||||
|
|
||||||
|
If there is the possibility of running out of stack space you can use
|
||||||
|
the @{b }FreeStack@{ub } (built-in) function call (see @{"System support functions" Link "BuiltIns.guide/System support functions" }).
|
||||||
|
This returns the amount of free stack space. If it drops below about 1KB
|
||||||
|
then you might like to stop the recursion or whatever else is using up the
|
||||||
|
stack. Also, you can specify amount of stack your program gets (and
|
||||||
|
override what the compiler might decide is appropriate) using the @{b }OPT
|
||||||
|
STACK@{ub } option. See the `Reference Manual' for more details on E's
|
||||||
|
stack organisation.
|
||||||
|
|
||||||
|
|
||||||
|
@ENDNODE
|
||||||
|
|
||||||
|
@NODE "Stack and Exceptions" "Stack and Exceptions"
|
||||||
|
@Prev "Stack (and Crashing)"
|
||||||
|
@Toc "main"
|
||||||
|
|
||||||
|
Stack and Exceptions
|
||||||
|
====================
|
||||||
|
|
||||||
|
The concept `recent' used earlier is connected with the stack (see
|
||||||
|
@{"Raising an Exception" Link "Exceptions.guide/Raising an Exception" }). A recent procedure is one which is on the stack,
|
||||||
|
the most recent being the current procedure. So, when @{b }Raise@{ub } is called it
|
||||||
|
looks through the stack until it finds a procedure with an exception
|
||||||
|
handler. That handler will then be used, and all procedures before the
|
||||||
|
selected one on the stack are taken off the stack.
|
||||||
|
|
||||||
|
Therefore, a recursive function with an exception handler can use @{b }Raise@{ub }
|
||||||
|
in the handler to call the handler in the previous (recursive) call of the
|
||||||
|
function. So anything that has been recursively allocated can be
|
||||||
|
`recursively' deallocated by exception handlers. This is a very powerful
|
||||||
|
and important feature of exception handlers.
|
||||||
|
|
||||||
|
|
||||||
|
@ENDNODE
|
||||||
|
|
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,7 @@
|
||||||
|
This directory contains 'E.guide', the original documentation written by me,
|
||||||
|
and 'beginner.guide', an excellent introduction to E by Jason Hulance. Both
|
||||||
|
are supplied in amigaguide format, if you wish to read them in plain ascii
|
||||||
|
instead, use Jason's own Ag2Txt utility that I included (in Bin/ and
|
||||||
|
Src/Utils/Ag2Txt/), for example:
|
||||||
|
|
||||||
|
1> bin/ag2txt >docs/e.txt docs/e.guide
|
Binary file not shown.
|
@ -0,0 +1,2 @@
|
||||||
|
This is the Amiga E v3.3a distribution.
|
||||||
|
Read docs/e.guide for details.
|
Binary file not shown.
Binary file not shown.
|
@ -0,0 +1,19 @@
|
||||||
|
Short: The Amiga E v3.3a compiler (ec)
|
||||||
|
Type: dev/e
|
||||||
|
Author: wvo96r@ecs.soton.ac.uk
|
||||||
|
|
||||||
|
This is the EC compiler, formerly shareware, now freeware. I've decided to
|
||||||
|
give away the E compiler for free. To read about the reasons why and other
|
||||||
|
details: http://www.ecs.soton.ac.uk/~wvo96r/e/efuture.txt
|
||||||
|
|
||||||
|
This archive contains only the compiler executable, and to do anything with
|
||||||
|
it, you need the distribution archive, dev/e/amigae33a.lha from any aminet
|
||||||
|
mirror. The source to the compiler is available as dev/e/ec33a_src.lha
|
||||||
|
|
||||||
|
EC is now freeware. You may distribute it in any way, i.e. via the internet
|
||||||
|
or on CDROMs. The only (obvious) exceptions are that you aren't allowed to
|
||||||
|
make money out of selling EC, and you're not allowed to change anything about
|
||||||
|
this archive. Thanks.
|
||||||
|
|
||||||
|
Wouter van Oortmerssen, march 1999
|
||||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,127 @@
|
||||||
|
Short: The Amiga E v3.3a compiler source
|
||||||
|
Type: dev/e
|
||||||
|
Author: wvo96r@ecs.soton.ac.uk
|
||||||
|
|
||||||
|
This archive contains the source code (in assembly) to the E compiler EC.
|
||||||
|
|
||||||
|
WARNING: This archive is for highly skilled assembly programmers with an
|
||||||
|
intimate knowledge of E and compilers, who are curious about EC's internals.
|
||||||
|
If this doesn't apply to you there's no point in even downloading this
|
||||||
|
archive. EC's source code redefines the term "hairy" and will make even
|
||||||
|
the best hackers faint.
|
||||||
|
|
||||||
|
This source code release is an addon to the freeware release of the E
|
||||||
|
compiler, EC v3.3a, which you can get from where you got this or any aminet
|
||||||
|
mirror as dev/e/ec33a.lha. You also need the distribution which was already
|
||||||
|
available: dev/e/amigae33a.lha. To read all about why I released this all
|
||||||
|
for free, read http://www.ecs.soton.ac.uk/~wvo96r/e/efuture.txt
|
||||||
|
|
||||||
|
As far as distributing the _unmodified_ archive goes, it is freeware, you can
|
||||||
|
distribute it in any way you see fit, i.e. via the internet or on CDROMs. The
|
||||||
|
only (obvious) exceptions are that you aren't allowed to make money out of
|
||||||
|
selling EC, and you're not allowed to change anything about this archive. Thanks.
|
||||||
|
|
||||||
|
|
||||||
|
I discourage people to distribute modified versions of EC, but if you feel you
|
||||||
|
really have to, a different set of rules applies:
|
||||||
|
|
||||||
|
As long as the modified compiler & source don't leave your system, you
|
||||||
|
can obviously do anything you please with it.
|
||||||
|
|
||||||
|
If you wish to re-distribute a modified version of EC you made, you have to
|
||||||
|
comply with the following rules:
|
||||||
|
|
||||||
|
* besided the new executable, you also distribute the new modified source
|
||||||
|
that generated it. EC's source code adheres to a similar scheme as "Open
|
||||||
|
Source" or the "GNU public license" with the only difference that you
|
||||||
|
don't need to include their licenses.
|
||||||
|
|
||||||
|
* You make very clear in your executable name, archive name, program welcome
|
||||||
|
message and anywhere else that this is YOUR version of EC, not the original,
|
||||||
|
i.e. someone who downloads your EC shouldn't be confused as to who made this.
|
||||||
|
Make sure you give the whole project a different name (e.g. BobsEC), and a
|
||||||
|
version number higher than 3.3a.
|
||||||
|
|
||||||
|
* You clearly mention me as the original author, refer to the 3 original
|
||||||
|
archives mentioned in this text, and in general do your best to educate the
|
||||||
|
users of your version to understand how this software came to be.
|
||||||
|
Obviously if they want to modify/redistribute your version, they should
|
||||||
|
also have read the texts in the original archives.
|
||||||
|
|
||||||
|
* EC is unsupported, so don't come back to me to ask wether you can have
|
||||||
|
your changes incorporated in the next official release, because there
|
||||||
|
won't be any. Instead, look around on the internet (the E mailing list
|
||||||
|
is a good place) for other people interested in doing an extended EC, and
|
||||||
|
cooperate with them.
|
||||||
|
|
||||||
|
|
||||||
|
Now to the hard bit, modifying EC's source code. As mentioned above, think
|
||||||
|
long and hard wether this is a really a sensible thing to do, and when you
|
||||||
|
have decided yes, think long and hard again. In the later years of
|
||||||
|
development, modifying EC required a very concentrated effort to modify and
|
||||||
|
debug, especially doing it correctly to not introduce new problems. EC's
|
||||||
|
source code is terribly complicated, very badly programmed, and contains more
|
||||||
|
long distance dependancies than any bit of code I've ever seen. In short,
|
||||||
|
unless the modification you intend to make is absolutely trivial, writing
|
||||||
|
the compiler from scratch would be a lot quicker.
|
||||||
|
|
||||||
|
Given that you still want to modify it:
|
||||||
|
|
||||||
|
* the code was written using AsmOne, most recently with version v1.25. Don't
|
||||||
|
ask me to send it to you, because I don't think it's public domain.
|
||||||
|
|
||||||
|
* If you insist on converting it to another assembler, be prepared for a lot
|
||||||
|
of work. Realise that a lot of the code in the source plays a role in the
|
||||||
|
code generation process, and as such is more critical to change than any
|
||||||
|
other assembly program would be: for example occurrences of MOVE.L #1,D0
|
||||||
|
in the source might be there for me to grab its opcode, if your assembler
|
||||||
|
changes that to MOVEQ #1,D0, the code generator will generate illegal code
|
||||||
|
and all compiled E programs will crash, and it'll be very hard to find out
|
||||||
|
why it went wrong. So make sure you disable any flags/options of your
|
||||||
|
assembler that try to optimise or otherwise change the code.
|
||||||
|
|
||||||
|
* register usage is completely non transparant, i.e. if a certain register
|
||||||
|
isn't used for an entire subroutine, you can't assume it's free to use.
|
||||||
|
there are all sorts of weird conventions, and many registers span entire
|
||||||
|
sections of the compiler in their lifetime. Basically if you modification
|
||||||
|
is in need of registers (and it will be) your safest bet is to JSR
|
||||||
|
from the point where you want to insert the new code, safe all registers
|
||||||
|
that you will be using and in general make sure that you restore the
|
||||||
|
machine state to exactly how you found it. Failing to do this WILL
|
||||||
|
introduce new hairy bugs.
|
||||||
|
|
||||||
|
* the whole compiler (>80k) is written in small model, i.e. uses 16bit
|
||||||
|
signed branch offsets, so insert new code at the end or start, otherwise
|
||||||
|
you'll break a lot of branches.
|
||||||
|
|
||||||
|
* the source code contains hardly any comments :P
|
||||||
|
|
||||||
|
There are tons more pitfals, but these are the most obvious I could think of.
|
||||||
|
|
||||||
|
|
||||||
|
besides the actual source code, the following files have been supplied in
|
||||||
|
the extras/ dir with the sole purpose of helping people who intend to
|
||||||
|
modify/enhance EC. The files are supplied AS IS, please don't come and ask
|
||||||
|
me to explain to you what their contents means.
|
||||||
|
|
||||||
|
extras/TODO.TXT:
|
||||||
|
the most recent todo list I had for future releases. May give you an idea
|
||||||
|
as to what bugs havent been fixed should you get to the point of improving
|
||||||
|
EC a lot.
|
||||||
|
|
||||||
|
extras/EC.TXT
|
||||||
|
this is the text I used internally to keep track of data structures and
|
||||||
|
other data used in EC, as the source itself is rather unselfdocumenting.
|
||||||
|
This text could help you a bit in trying to understand EC's code, but
|
||||||
|
most of the data structures in this text contain no documentation, so
|
||||||
|
you're on your own as to what it all means. Still its better than nothing.
|
||||||
|
|
||||||
|
extras/utils/
|
||||||
|
E source code to utilities supplied in the distribution, i.e. showmodule
|
||||||
|
and others. One utility called ecdebug can dump some of the internal
|
||||||
|
data structures of EC while its running (run it as EC HOLD in another shell),
|
||||||
|
and can be easily extended.
|
||||||
|
|
||||||
|
|
||||||
|
Wouter van Oortmerssen, march 1999
|
||||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,772 @@
|
||||||
|
+---------------------------------------------------------------+
|
||||||
|
| TODO for next release |
|
||||||
|
+---------------------------------------------------------------+
|
||||||
|
|
||||||
|
- jasons sc.e crashes with textview.e -> needed recompiled sctext.e,
|
||||||
|
put in distr.
|
||||||
|
- ignore EXPORT if were not in a MODULE
|
||||||
|
- problems:
|
||||||
|
|
||||||
|
> OBJECT foo
|
||||||
|
> bar
|
||||||
|
> ENDOBJECT
|
||||||
|
>
|
||||||
|
> PROC main()
|
||||||
|
> DEF x:foo
|
||||||
|
> WriteF('\d\n', x.bar.bar)
|
||||||
|
> ENDPROC
|
||||||
|
|
||||||
|
- Facos() / Fsinh() crashes?
|
||||||
|
|
||||||
|
|
||||||
|
+---------------------------------------------------------------+
|
||||||
|
| PUBLIC TODO LIST |
|
||||||
|
+---------------------------------------------------------------+
|
||||||
|
|
||||||
|
Bugs I haven't been able to reproduce:
|
||||||
|
* compilation problems on 060
|
||||||
|
* broken Lock in combination with MCX
|
||||||
|
|
||||||
|
Bugs still not fixed:
|
||||||
|
* dereferencing an untyped OBJECT member is not properly interpreted
|
||||||
|
as "PTR TO CHAR". Easy workaround: Give the member a type.
|
||||||
|
* debug hunks don't contain directory information for projects
|
||||||
|
split up over sub directories.
|
||||||
|
* error reports can sometimes be off by several lines.
|
||||||
|
* error reports for inline assembly often doesn't work.
|
||||||
|
* unreferenced check too conservative in some cases.
|
||||||
|
* doesn't detect two library functions with the same names.
|
||||||
|
* constant folding optimizer often misbehaves, disallowing
|
||||||
|
you to do things like `1+SIZEOF o' in constant expressions
|
||||||
|
or 1+a as list-elements.
|
||||||
|
* internal linker problem doesn't allow for private methods.
|
||||||
|
* strings in lists cannot be joined by "+" (optimizer problem).
|
||||||
|
* StrCopy/MidStr don't fill dest with empty-string if len=0
|
||||||
|
* methods and procs in one module cannot have the same name
|
||||||
|
(other than by inheritance).
|
||||||
|
* Exists() evaluates all regardless.
|
||||||
|
* SUPER doesn't check wether the superclass actually has such
|
||||||
|
a method.
|
||||||
|
* recompiling a module that is being used at the same time
|
||||||
|
by another invocation of EC can give problems.
|
||||||
|
|
||||||
|
Future Todo's
|
||||||
|
* fix above bugs :)
|
||||||
|
* extend CONST expressions with floats etc.
|
||||||
|
* find a way to split up executables in more hunks
|
||||||
|
* add symbols for methods
|
||||||
|
* lots of extensions to the optimizer
|
||||||
|
* proper 020+ support
|
||||||
|
* improve error reporting
|
||||||
|
* class variables
|
||||||
|
* short-circuit AND / OR
|
||||||
|
|
||||||
|
Wild Ideas (aka Big Todo's)
|
||||||
|
* plug a general back-end onto EC (to allow for code
|
||||||
|
generation to other cpus (e.g. PPC) or even languages
|
||||||
|
(e.g. C) or better optimizers etc.)
|
||||||
|
* extend the language with lambda's / higher order
|
||||||
|
functions / lazyness etc.
|
||||||
|
* concurrency system based on Linda
|
||||||
|
* better debugging / analysis / type inference tools
|
||||||
|
* full visual language on top of E
|
||||||
|
|
||||||
|
|
||||||
|
+---------------------------------------------------------------+
|
||||||
|
| TODO for maybe some future release ? |
|
||||||
|
+---------------------------------------------------------------+
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
[unconfirmed bugs / could not reproduce / does it make sense?]
|
||||||
|
- doesn't compile large sources on 060
|
||||||
|
- lock still doesn't work in some circumstances (v2.1b does work ok, does work
|
||||||
|
with snoopdos, doesn't with mcx: register trashing?) [tried with mcx no prob]
|
||||||
|
- bug: object x; x; x; ENDOBJECT no all detected
|
||||||
|
-> tested all combinations with modules and inheritance, no prob
|
||||||
|
- LIBB_DELEXP is meant to be used on lib_flags, not sb_flags, though
|
||||||
|
sample.library does it this way and it seems to function ok?
|
||||||
|
- the GTMENUITEM_USERDATA(x) macro doesn't work [jolyon]? not true.
|
||||||
|
- flushcache flushes all
|
||||||
|
- unsigned comparison????: no practical example sofar
|
||||||
|
- [2,4,826,0,$1C8E],[2,4,27,0,$2A7E],[9,4,207,0,$802E] on a 030 no hits: which EC?
|
||||||
|
- DEF a[; missing "]" found only in next proc!
|
||||||
|
- myfunc(a,b,[blah1,BLAH,[foo,[bar,NIL,a]) <error here>
|
||||||
|
- in v3.1a: EC INTERNAL ERROR [2,0,-1,1,$0] (please report!) (Started this while an other EC was compiling...)
|
||||||
|
- not in exec.m: private functions. (no prob?)
|
||||||
|
- supposedly tools/pt.m would not produce a sound on 030, but does on 020 same
|
||||||
|
config [tried on my 030 which works]
|
||||||
|
|
||||||
|
[bug-fixes]
|
||||||
|
- a membor 'bla' and a method 'bla()' on the same OBJECT collide.
|
||||||
|
- bug: doing obj.f[1] where f is untyped interpreted f as "PTR TO
|
||||||
|
SIZEOF obj" instead of "PTR TO CHAR", because type for next deref
|
||||||
|
isn't properly set. Fixing it however causess
|
||||||
|
imsg.iaddress::gadget.gadgetid to break
|
||||||
|
- sourcecode name in debug hunks misses path if compiled in a
|
||||||
|
different directory, e.g. '*tools/bla'
|
||||||
|
- large mui window def, comma fails in the middle, error at first line.
|
||||||
|
- MULU.W A0,D0 (error) gives ERRBYTE of 0
|
||||||
|
- library bases are shown as unreferenced if only calls (opened in other module)
|
||||||
|
- bug: proc (or libsfuns) with same names from libs aren't detected!
|
||||||
|
- bug: RealVal('\0...') = 0
|
||||||
|
- since CONST idents aren't resolved until the end of the line in lex,
|
||||||
|
RAISE IF gives 'immediate value expected' instead of 'unknown keyword/const'.
|
||||||
|
- INCBIN doesn't work in ASM mode !
|
||||||
|
- OBJECT testobject;ENDPROC;PROC main();ENDPROC -> gives label expected at "main"
|
||||||
|
- StrCopy returns NIL on empty string. check retvals other stringfuns as well.
|
||||||
|
- ERRBYTE = 0 als MODULE 'x' \n 'y' (no comma)
|
||||||
|
- module by o2m with empty section causes [3,0,-1,1,$0]
|
||||||
|
- EOR.W #xx,Dx not automatically translated to EORI
|
||||||
|
- (in main:) if in an object in a module there is a "PTR TO x", and the
|
||||||
|
module defining x is not included, attempting to dereference to a
|
||||||
|
member in x gives "illegal use of/reference to object", whereas
|
||||||
|
"x undeclared" would be more helpful. (while loading EC converts
|
||||||
|
"PTR TO x" to "LONG" if "x" can't be found).
|
||||||
|
-> have a typed object member in a module remember type even if obj is not available.
|
||||||
|
- a local in a proc in a mod is not shown as unreferenced
|
||||||
|
- unknown function only shown after (...) ?
|
||||||
|
- obj::class.method() ignores ::class part!
|
||||||
|
- 1*SIZEOF LONG werkt niet want ASM_GRABVALUE kent geen SIZEOF LONG
|
||||||
|
- macro prep kan #define DEBUG -> etc niet, want comment stopt bij 0?
|
||||||
|
- 2x zelfde label naam -> geen error! (x: na x: of x())
|
||||||
|
- een module met alleen labels en inline asm geeft scope errors
|
||||||
|
- private classes in een module worden NIET geexporteerd,
|
||||||
|
dus krijgen ook geen geinit delegate obeject in main!!
|
||||||
|
temp solution: compiler forces export
|
||||||
|
- I can declare instance var "x" even if already in super
|
||||||
|
(and access both via different pointers!)
|
||||||
|
- strings in list cannot have "+"
|
||||||
|
- MOVEQ doesn't accept -128
|
||||||
|
- StrCopy/MidStr don't fill dest with empty-string if len=0
|
||||||
|
- tools/vector.m saved geen regs
|
||||||
|
- can't have methods with the same name without inheritance.
|
||||||
|
methods and procs in one module cannot have same name.
|
||||||
|
- flushing: what if .m's get flushed currently used by EC?
|
||||||
|
- SUPER doesn't check if super has a method there
|
||||||
|
-> could pass a maxoffset to expcallmethod
|
||||||
|
- Exists() evaluates all regardless
|
||||||
|
|
||||||
|
|
||||||
|
[todo]
|
||||||
|
- "-D" like switch for defines
|
||||||
|
- floats in CONSTs etc?
|
||||||
|
- see etext/truelambda.txt!
|
||||||
|
- one hunk
|
||||||
|
- lowercase sourcename
|
||||||
|
- () in constexp (and maybe other things)
|
||||||
|
- %bin for binary nums
|
||||||
|
- a read_e_module(): jason/gregor/vincenzo and others
|
||||||
|
- "LINE" debug of modules -> "L" thus unusable with enforcer!
|
||||||
|
- some space in library node for magic values?
|
||||||
|
GoldED (V3) syntax-parser 'magicid' ("SCAN") at offset $24 in libbase(a6) ($22=exec base size, +word pad)
|
||||||
|
-> could easily preserve $22 to $32 or so for own data... how to give A6 to
|
||||||
|
main() so that magic can be poked there?
|
||||||
|
- symbols for methods?
|
||||||
|
- \num for strings?
|
||||||
|
- "::" on anything? -> on typedecls as short for ":PTR TO "
|
||||||
|
- for libs: stack alloc [] or somesuch. warning for [] in opt library
|
||||||
|
maybe a NEW equivalent for existing mem (and stackmem?)
|
||||||
|
- linenum by var used as function warning?
|
||||||
|
- put some forbids in modulecache access.
|
||||||
|
- faq: more interesting questions (how to start on own compilers etc.)
|
||||||
|
|
||||||
|
[code-optis]
|
||||||
|
- OPTI: lea 4..8(A7),A7 --> ADDQ
|
||||||
|
- OPTI: CMP.L #1,D0 --> MOVEQ #1,D1; CMP.L D1,D0
|
||||||
|
- OPTI: recognise a:=a+<exp>
|
||||||
|
- OPTI: ELSEIF doesn't optimize EXTs away
|
||||||
|
- OPYI: LISTs should make use of EXPEA
|
||||||
|
- OPTI: get rid of LINK iff no args + only REG locals
|
||||||
|
- OPTI: optimize NEW [1,2,3] code with EXPEA
|
||||||
|
|
||||||
|
[020/030/040/881]
|
||||||
|
- 680x0 code gen.
|
||||||
|
- jsr --> bsr.l (no relochunks needed!)
|
||||||
|
- array/record access etc. with ([base,a5],d2*4,1) etc. (FAAST!!)
|
||||||
|
- EXTL etc. + see how other 020 modi can be applied
|
||||||
|
- 020/881 asm
|
||||||
|
|
||||||
|
[unstablenesses]
|
||||||
|
- [..] allows for private CHAR fields to be cleared,
|
||||||
|
NEW [..] allows all private fields.
|
||||||
|
prob = CHAR is equal to align byte
|
||||||
|
- arrange for double declarations in idents from module etc.
|
||||||
|
- check more number of args etc. where main->b->c, c is
|
||||||
|
changed and recompiled, but b is just linked.
|
||||||
|
- OPTIBUG: OPTIIF makes too many assumptions about code
|
||||||
|
- we might need a token-look-back-stack or similar for lex
|
||||||
|
|
||||||
|
[module-system]
|
||||||
|
- o2m: maybe do something with C in .library-link mode.
|
||||||
|
- o2m: "Raise" en "dosbase" als XREF
|
||||||
|
- checksums on modules
|
||||||
|
|
||||||
|
[errors]
|
||||||
|
- selected multiple errormessages for most occuring errors
|
||||||
|
(lex, for starters) with per-case specific recovery code (+ switch)
|
||||||
|
- catch errors in writemod: otherwise readlock
|
||||||
|
Locks on modules when error from moduleload
|
||||||
|
- calc Levenstein distance for idents? unknown=Writef, suggest: WriteF
|
||||||
|
- problems with error-reconstruction:
|
||||||
|
- assembly not properly supported
|
||||||
|
- stat;stat wrong spot error
|
||||||
|
- problems if "->ENDPROC" last line
|
||||||
|
- bla,bla <no_comma> ->comment <lf> <err_spot> bla,bla
|
||||||
|
- "....:REG",10,"<here>whatever"
|
||||||
|
- meer foutmeldingen, syntax error opsplitsen
|
||||||
|
- foutmeldingen in andere talen (frans/duits/italiaans)
|
||||||
|
(locale.library??)
|
||||||
|
|
||||||
|
[EC]
|
||||||
|
- resident check?
|
||||||
|
- SHOWBUF option also lines/minute etc.
|
||||||
|
|
||||||
|
[compiler-organisation-OPTI]
|
||||||
|
- raise-tab linked maken
|
||||||
|
- enlarge hash tables from 211 to near 1024
|
||||||
|
- change all sorts IF-THEN situations into jump-tables
|
||||||
|
- OPTI: hash bij first search members tijdens lex?
|
||||||
|
- OPTI: E-system functions ge-hashed
|
||||||
|
- OPTI: intui/exec/dos/gfx functions ge-hashed
|
||||||
|
|
||||||
|
[code-generation]
|
||||||
|
- chip/data hunk stuff? INCBIN x TO CHIP/DATA -> how to put in var?
|
||||||
|
more code hunks?
|
||||||
|
- Jan Schulz zn SELECT idee
|
||||||
|
- better implementation of [1-x] etc.
|
||||||
|
- lists have to be able to get longer then a few hundred entries!
|
||||||
|
prob: runtime stack (just a little).
|
||||||
|
better code gen altogether?
|
||||||
|
- LANGUAGE TEST SUITE for code-gen testing
|
||||||
|
- write disassembler for E exe's to some interim language.
|
||||||
|
|
||||||
|
[inline-asm]
|
||||||
|
- asm instr + LONG use of reloc possibilties
|
||||||
|
- MOVE.L .member:obj(A0),D0
|
||||||
|
|
||||||
|
[run-time-system]
|
||||||
|
- if >37 then conout AUTO/WAIT/CLOSE
|
||||||
|
|
||||||
|
[oo-language-features]
|
||||||
|
- method hiding
|
||||||
|
- x <=> self.x ??
|
||||||
|
- constructor without need for pointer
|
||||||
|
allow NEW [..]:x.constr(), since '..' inits only public fields
|
||||||
|
- SHARED (class variables) as "Side Effect Functions"
|
||||||
|
- private methods
|
||||||
|
- PROBLEM: auto-destructors <=> exceptions
|
||||||
|
for now: ONLY DYNAMIC objects from classes.
|
||||||
|
sol: linked list on the stack, upon raise etc. call destructors
|
||||||
|
below return stackpoint. data=(next,objptr),depth=stackptr,
|
||||||
|
destructor at fixed offset.
|
||||||
|
[END (do it yourself),normal ENDPROC,Raise(),CleanUp()]
|
||||||
|
|
||||||
|
[language features]
|
||||||
|
- E component project doen? (weinig werk voor mij)
|
||||||
|
- tagged args doenbaar? (helpt niet erg voor easygui) tagged objects+defaults dan mischien?
|
||||||
|
- FastString() etc., -> table in docs
|
||||||
|
- more constexps (enum/select_of)
|
||||||
|
- richer constexps (and/shl/mod)
|
||||||
|
- let "::" work on anything, so you can do gfxbase::gfxbase.defaultfont
|
||||||
|
:: on any exp (only lhs) func()::y.x
|
||||||
|
- macros should allow for macro()
|
||||||
|
-> module format has nargs=0 as macro, not macro()
|
||||||
|
- case insensitive OstrCmp() (how localisation?)
|
||||||
|
-> better: convert all stuff to one case anyway, or use utility.library
|
||||||
|
- EXIT in LOOP and REPEAT
|
||||||
|
- +:= for self.x:=self.x+1 cases
|
||||||
|
- ASSERT / DEBUG..ENDDEBUG
|
||||||
|
- _vector_ calc on lists?: MAP x,y,z IN x:=fun(y)+z+a*10
|
||||||
|
- Linda tuples + persistance (combined with Linda / as a class to
|
||||||
|
inherit from / special language feature ??)
|
||||||
|
- RENAME statement? could work with almost all types of idents.
|
||||||
|
- LOOP as short form
|
||||||
|
- IF a AND THEN b ..., IF a OR ELSE b ...
|
||||||
|
BUT AND/OR <=> major probs OPTIIF
|
||||||
|
x BUT OR y = IF x THEN x ELSE y = r:=x; IF r=0 THEN r:=y
|
||||||
|
x BUT AND y = IF x THEN y ELSE x = r:=x; IF r THEN r:=y
|
||||||
|
- UNIFICATION:
|
||||||
|
- strings: _pattern_matching_ REGEXPs, and string binding
|
||||||
|
- ranges and comparisons: a <=> [1 TO 10,<5] (watch <5 with CONS)
|
||||||
|
- UNIFY more than one exp with list of patterns (a la SELECT)
|
||||||
|
- ENDSELECT <var> (beter als ListItem hack)
|
||||||
|
- NOT unary operator (mischien ook "-")
|
||||||
|
- more useful quoted expression functions
|
||||||
|
- [<constexp>]:ARRAY
|
||||||
|
- const exps pre-calc uitbreiden met ">" "AND" etc.
|
||||||
|
|
||||||
|
[docs]
|
||||||
|
- extend the FAQ! be even clearer about pointers? -> hints & pitfalls for beginners
|
||||||
|
- an INDEX!
|
||||||
|
- [chapter on optimizing, more on (data)types
|
||||||
|
- design_of_e, good_programming_style, debugging etc.
|
||||||
|
|
||||||
|
[dist]
|
||||||
|
- UTIL: fd2module
|
||||||
|
- Lex equivalent for E
|
||||||
|
- large set of utils in E, a la diff/grep
|
||||||
|
- p2m en Iconvert update with options:
|
||||||
|
- examples: keep_prefixes egs_window etc.
|
||||||
|
|
||||||
|
[modules]
|
||||||
|
- serial.device!!
|
||||||
|
- also very simple things, like classes for intuition etc. -> as many HQ mods as possible!
|
||||||
|
- SEE: container classes
|
||||||
|
- task object ofzo
|
||||||
|
functions for tasks. (see E source) +other amiga.lib
|
||||||
|
geta4() routine etc.
|
||||||
|
- common Lex() routine.
|
||||||
|
lex+pars
|
||||||
|
- play samples, allocating channels, playsample() audiodevice support
|
||||||
|
- garbage collecting string-lib
|
||||||
|
- String matching (regexps) Parse() en Match() routines
|
||||||
|
- bignums.m: lijst [] of LONGs makes up an integer of any size
|
||||||
|
use ADDX. how opti for short ints?
|
||||||
|
- bobs, sprites
|
||||||
|
- fast mem-copy + string shifting [b], fastinsert/delete
|
||||||
|
- all sorts of Rexx string parsing stuff
|
||||||
|
- OohNo! module
|
||||||
|
- 24bit module
|
||||||
|
- harwarebang module :-)
|
||||||
|
- structure:
|
||||||
|
emodules:tools/ -> general tools supplied with E
|
||||||
|
emodules:class/ -> same, but now as class
|
||||||
|
emodules:other/ -> tools not standard with E, 3rd party stuff
|
||||||
|
^^^nobody listens to that...
|
||||||
|
|
||||||
|
|
||||||
|
+---------------------------------------------------------------+
|
||||||
|
| DONE !!!! |
|
||||||
|
+---------------------------------------------------------------+
|
||||||
|
|
||||||
|
[see other texts for pre-v3.0a stuff]
|
||||||
|
[sofar EC627 (=3.0a)]
|
||||||
|
|
||||||
|
+ odd-size created with NEWM macro -> internal errors on 68000
|
||||||
|
+ ODEL field not properly set in all cases of NEWMETHODS
|
||||||
|
caused delegates not to be constructed -> method probs!
|
||||||
|
++ keep track of PTR TO object idents from modules, and find
|
||||||
|
objects after lex. (not found = no action)
|
||||||
|
+ modules: 4 bytes of code [mathexit generated RTS]
|
||||||
|
+ defargs of method not installed correctly after moduleload
|
||||||
|
+ double-decl test on method tests for bit that wasn't set yet
|
||||||
|
thus: not possible to redefine method for subclass!
|
||||||
|
|
||||||
|
[EC631;v3.0b, released as such (patch)]
|
||||||
|
|
||||||
|
[EC632;v3.0c]
|
||||||
|
+++ debugger! DEBUG/S option also switches on LINEDEBUG/S
|
||||||
|
+ adds NOPs and EVAR hunks.
|
||||||
|
++ SUPER method calls possible! notes: [calls from there
|
||||||
|
not super, super-calc is static]
|
||||||
|
[EC639;c3.0c]
|
||||||
|
|
||||||
|
[EC640;v3.0d]
|
||||||
|
+ enforcer hit in linker fixed (strcmp)
|
||||||
|
++ BIGBUG: BSR GLOBJUMP
|
||||||
|
in initcode didn't work with modules-total >32k!!!!!!!!!
|
||||||
|
+ code-buf estimate bigger ivm modules in small main
|
||||||
|
now starts at 100k ivp 50k, and ReadArgs allows ADDBUFs
|
||||||
|
1..1000 (each *100k add, i.e. max 100meg exes)
|
||||||
|
+ method_arg_stacklea bug: code for SUPER trashed reg
|
||||||
|
+ bug: String() would trash D3
|
||||||
|
+ bug: error report within OBJECT always on 1st line
|
||||||
|
+ modules with only constdefs etc. would still save debughunk
|
||||||
|
+ compiler would accept things like PROC pippo(a,b,)
|
||||||
|
+ method def of object in other module!!! now illegal.
|
||||||
|
+ BUG: privateness would continue in next object decl
|
||||||
|
+ AND.L Dx,regvar (and similar constructions, such as with ADD)
|
||||||
|
assembles as AND.L Dn,ea instead of AND.L ea,Dn -> ILLEGAL!!!
|
||||||
|
+ CMP.B A3,D0 (and other An <=> .B) -> ILLEGAL!
|
||||||
|
+ temp export force on accessed classes.
|
||||||
|
[EC655;v3.0d]
|
||||||
|
|
||||||
|
[v3.0e released as patch]
|
||||||
|
|
||||||
|
[v3.0f]
|
||||||
|
++++ newer EDBG!
|
||||||
|
+++ Macro preprocessor and conditional compilation!
|
||||||
|
+++ enhanced EasyGUI
|
||||||
|
++ a FAQ!!!!
|
||||||
|
+ doc
|
||||||
|
* .guide niet duidelijk genoeg
|
||||||
|
* error doc: too size exp also strings
|
||||||
|
* Organise, optimise... It's organize and optimize!
|
||||||
|
* ERROR: illegal use of/reference to object LINE 66: NEW raum[MAX_RAEUME]
|
||||||
|
* prob: bij module main->a->b, add method in b without recomp a: crash.
|
||||||
|
* In the E.doc it still says "use <another_function>".
|
||||||
|
* syntax errors in <|> syntax!!
|
||||||
|
* no mention of *2 *4 /2 /4 getting optimized
|
||||||
|
* in 4K: automatically at the end of the program, or by hand with ___Dispose(p)___
|
||||||
|
* Fexp(y) gives e^y, not y^e, Fpow(y,z) gives z^y, instead of y^z
|
||||||
|
* explain SUPER
|
||||||
|
* docs: better con-destructor explanation
|
||||||
|
* remarks in letter Martin F. Combs
|
||||||
|
* how to report bugs
|
||||||
|
* no language suggestions please
|
||||||
|
* reg. sites
|
||||||
|
* refs naar ML,EPD etc. -> in FAQ
|
||||||
|
* anti-serge hamm. message
|
||||||
|
- dist: fonts, nieuwe aprof
|
||||||
|
- fix errors in stack
|
||||||
|
- BUG: method with equivalent name inobject wouldn't work (left,name...)
|
||||||
|
- BUG: nested method calls disturb self at call site
|
||||||
|
- closing brackets would continue until decl of next proc with stange error,
|
||||||
|
now "missing )" at endproc
|
||||||
|
- demoversion limit to 8k
|
||||||
|
- finished macroprep + save macros. tested with BGUI/MUI
|
||||||
|
- lets fix it on "FastDisposeList()" + LONG align opti for NEW [1,2,3]
|
||||||
|
- unification bug in LISP-lists <>
|
||||||
|
- RealF(s,-3.14159,0) produces '-', RealVal() returns bagger as readlen?? (68000?)
|
||||||
|
RealF(s,12345.67,4) produces '1234.6699'??
|
||||||
|
more than one complaint about RealF on 68000
|
||||||
|
FIXED: all non-regsave 68881 vs. emulation
|
||||||
|
- flushcache flushes everything
|
||||||
|
- E-build executes actions in reverse order! :-) -> jasons version in dist
|
||||||
|
- docs: linedebug buiten procs
|
||||||
|
- docs: EXPORT gebruik met methods? currently not poss., EXPORT voor nonsense
|
||||||
|
- smod uses proper commandline arg (for completion)
|
||||||
|
- dist: nieuwe modules, sources etc.
|
||||||
|
- devices/serial.m termarray member were 0,1
|
||||||
|
- In inputevent fehlen ie_x, ie_y und ie_addr. Warum? -> in C it's a union
|
||||||
|
- BUG: SUPER doesn't work as exp
|
||||||
|
- list consts will accept no negative floating point
|
||||||
|
- SMOD doet foute arg-idents met nummers + defargs
|
||||||
|
- PROC ...IS obj.unknown; PROC <HERE> ...
|
||||||
|
Without following pro: internal [3,3,-1,0,$750E]
|
||||||
|
.unknown had a continue_on_next_line because of .method()
|
||||||
|
- docs: explain all new features
|
||||||
|
- docs: nieuwe errors
|
||||||
|
- finish easygui -> in DIST!
|
||||||
|
- finish EDBG
|
||||||
|
* weird bugs: doesn't find vars in allx.e
|
||||||
|
-> in DIST!
|
||||||
|
|
||||||
|
[EC666, v3.1a, released as such]
|
||||||
|
|
||||||
|
[EC667, v3.1b]
|
||||||
|
- BUG: .L in some instructions needs to be rejected, f.e. MULU.L #3,D1 = bagger
|
||||||
|
(done for mulu/muls/divu/divs)
|
||||||
|
- BUG: labels not in symhunk
|
||||||
|
- BUG: ASR.B #1,(A0) word toegestaan (alleen ASR.W geldig hier)
|
||||||
|
- BUG: Bounds(-1,0,255) -> 255
|
||||||
|
- BUG: macros of other module are saved AGAIN in current module
|
||||||
|
- BUG: 1/0 causes internal error
|
||||||
|
- stringf/textf made use of task-stack, now e-allocated-stack (like writef does)
|
||||||
|
- various emodules with problems, but fixed in v40 (datatypes/datatypesclass.m)
|
||||||
|
- v40 std modules in EC
|
||||||
|
- BUG: voegde linedebug toe voor elk statement, maar alloc alleen voor elke
|
||||||
|
sourceregel (nu alleen linedebug voor stats zonder ";" ervoor)
|
||||||
|
- forth.e closes console twice (still!)
|
||||||
|
- HUGEBUG: exp*globvar in module not compiled ok: not offset-2
|
||||||
|
now also operand_size in modules >= v10 (makes incompat!)
|
||||||
|
- bug: .B etc. at end of stat didn't work
|
||||||
|
- globals in modules used in inline asm were not exported/offsets adjusted
|
||||||
|
- incbin does any size no prob!?
|
||||||
|
- a-- where a:PTR TO o where SIZEOF o>8 translated into ADDI ipv SUBI
|
||||||
|
- ignorecache werkt niet: trashes own cookie. by load module.
|
||||||
|
- macros don't allow for ";"
|
||||||
|
-> disabled altogether ivm problems in eol-processing otherwise
|
||||||
|
- show expanded text within macros -> only for lex-phase
|
||||||
|
[EC676, v3.1b, to_jason]
|
||||||
|
|
||||||
|
[EC677, v3.1c]
|
||||||
|
- fixed bug in easygui: used gl instead of glist
|
||||||
|
- another bug in easygui: listv put userdata in all parts
|
||||||
|
++ NILCHECK/S, build in NIL checker for deref! raises "NIL" for "." and "[]"
|
||||||
|
- bigbug: (found in niklas' sources): when adding methods to an object that
|
||||||
|
inherits from a method-less object, delegates would start at 0 (not 4),
|
||||||
|
thereby trashing the object_dynamic_length and causing END trouble.
|
||||||
|
- self.s <=> <x|y> gives syntax error
|
||||||
|
- LEA -99(A2,D0.W),A2 becomes LEA -$0063(A2,A7.L),A2 ??? -xx(Ax,Dx.w)
|
||||||
|
- NEW en END moeten ook op subobjs werken.
|
||||||
|
- edbg: show current line one too high in t/test2.e -> file
|
||||||
|
[EC685, v3.1c, to_jason?,pnolan]
|
||||||
|
|
||||||
|
[v3.1d]
|
||||||
|
- EasyGUI: fixed newlook menus
|
||||||
|
- EasyGUI: activates first stringgad
|
||||||
|
- EDBG: doesn't work with SYM hunks
|
||||||
|
- EDBG: cursor keys: down = step over, right = step in
|
||||||
|
- EDBG: output to debugger window?
|
||||||
|
- EDBG: source menu option improved
|
||||||
|
- EDBG: menus in strange colour
|
||||||
|
- EDBG: scroller prob
|
||||||
|
- EDBG: doesn't jump to correct spot if outta sight.
|
||||||
|
- EDBG: doesn't display tabs: tempfix: convert to space
|
||||||
|
- EDBG: better graphics for toolbar & about
|
||||||
|
- EDBG: breakpoints on sourcelines
|
||||||
|
- EDBG: breakpoints on memory/vars
|
||||||
|
- EDBG: run upto breakpoint
|
||||||
|
- EDBG: E expression parser for all value/addr inputs
|
||||||
|
- EDBG: Raise()
|
||||||
|
- EDBG: shows vars own proc + globals only (scoped!)
|
||||||
|
- EDBG: watched variables window
|
||||||
|
[v3.1d, to various persons]
|
||||||
|
|
||||||
|
[v3.1e,EC688]
|
||||||
|
- .library output
|
||||||
|
- integrated jasons newest modules, etc.
|
||||||
|
- new easygui integrated
|
||||||
|
- new EE
|
||||||
|
- easygui leaves 160 bytes if exception from callback: missed a ReplyMsg()
|
||||||
|
- forbid when changing task-a4 table!
|
||||||
|
- #else mentioned in some error messy
|
||||||
|
- sc.m etc.
|
||||||
|
- BUG: /2 added 1 also for >0
|
||||||
|
- unknown ident of functionname still at the end.
|
||||||
|
- doc:
|
||||||
|
+ clear about where globals need to be defined
|
||||||
|
+ too many use flushcache
|
||||||
|
+ SELECT_OF takes n-1 as max, not n (DEFAULT)
|
||||||
|
+ labels ook exportable->doc
|
||||||
|
+ (wrong!): ' ARRAYs are declared by specifying their lenght (in bytes) '
|
||||||
|
+ explain that error message can point as much as 2 tokens beyond real cause of error.
|
||||||
|
+ FastDipose#? accepts NIL
|
||||||
|
+ macros don't do ;
|
||||||
|
+ libdoc: eg, and what happens to stdout etc.
|
||||||
|
+ FAQ uitbreiden
|
||||||
|
|
||||||
|
[v3.2a, EC707, released as such]
|
||||||
|
|
||||||
|
[v3.2b,EC708]
|
||||||
|
- hung or gave wrong error on procident.member [jaco]
|
||||||
|
- prepbug: rkrmsrc/intuition/windows/lines.e. internal [2,3,232,0,$698] on 68000
|
||||||
|
- It has problems using control characters or extended character set characters in #defines
|
||||||
|
- extra safeties for preprocessor (i.e. tries to expand interim buf after each
|
||||||
|
macro expansion).
|
||||||
|
- 'W\0Save Statistics...' exists in the compiled binary as 'W' (under 3.1)
|
||||||
|
[v3.2b,EC712,released to almathera]
|
||||||
|
|
||||||
|
- eg: fixed an enforcer hit
|
||||||
|
v3.2a all.e with EC DEBUG byte reads (-1,0) on amosaic & font, from rom(-1) too. 569A(0) (= StrLen)
|
||||||
|
EDBG v3.2a byte-reads from addr. 0 hunk offset E6FE (often when an easygui opens/closes?) (= StrLen)
|
||||||
|
- eg: cleangui() -> $DEADBEEF mem already freed: used window after closing!
|
||||||
|
- eg: added settext() and setnum()
|
||||||
|
- eg: fixed NUM gadget
|
||||||
|
- eg: advice not to give to high values for proportional values because of high fonts (fheight*val)
|
||||||
|
- eg: topaz function
|
||||||
|
- eg: easygui renderen soms in bottom border: check wborbot! check with sysihack
|
||||||
|
- eg: PLUGINs!!!
|
||||||
|
- eg: there might still be some string-fields causing hits at -1 in an OS call?
|
||||||
|
- edbg: about & toolbar do not respect bigger borders
|
||||||
|
- edbg: console (+tool+about?) don't work on PUBSCREEN
|
||||||
|
- edbg: sourcenaam in title
|
||||||
|
- edbg: check doubles in var view
|
||||||
|
- edbg: save & loads prefs + window sizes + watched vars
|
||||||
|
- edbg: arexx-port & script launching
|
||||||
|
- bug: problem with Lock() in EC: 2 instead of -2 as mode
|
||||||
|
- bug: $FFFF as default arg becomes $FFFF0000
|
||||||
|
- bug: macro errors printed incorrectly: fixed with a kludge
|
||||||
|
- bug: /* */ in macros didn't work (e:t/macc.e)
|
||||||
|
- bug: OBJECT x OF x not detected and causes major guru in exe
|
||||||
|
- bug: doesn't accept a:REG in 0(A3,a) or 0(A3,a.L) or lab(PC,a) etc.
|
||||||
|
- bug: AND.L D0, tst where tst:REG screws up (changes dest-src, also for OR/EOR etc.)
|
||||||
|
[v3.2e, EC716, released as small update]
|
||||||
|
|
||||||
|
[EC719]
|
||||||
|
- faq: these features will not be implemented (so don't ask for them):
|
||||||
|
* assembly source output by the compiler
|
||||||
|
* .o
|
||||||
|
* inits for global vars in modules
|
||||||
|
* unaligned objectmembers
|
||||||
|
* bitfields
|
||||||
|
* not opening intuition.library etc, in startup-code
|
||||||
|
* automatically include _all_ modules used by a module
|
||||||
|
* `member' ipv `self.member'
|
||||||
|
* DEFs halfway PROCs
|
||||||
|
* disassembly mixed mode for EDBG
|
||||||
|
* sourcecode include
|
||||||
|
* unsigned INT/LONG or signed CHAR
|
||||||
|
* ";" in macros
|
||||||
|
* multiple error messages
|
||||||
|
* x.y in assignment expression, {}
|
||||||
|
* inline code for Shl() etc.
|
||||||
|
- doc changes:
|
||||||
|
* mark all obsolete features in the docs as such (CleanUp() etc.)
|
||||||
|
* :: works only on object types, <>cast
|
||||||
|
* New and NEW behaviour in libraries
|
||||||
|
* you can't have zero argument macros
|
||||||
|
* update registration sites (sweden + germany)
|
||||||
|
* Link() doesn't work on NEW [...] etc.
|
||||||
|
* if you want to use vars from other modules, you have to EXPORT DEF them too
|
||||||
|
* make EXTREMELY clear the error-indication is always after the error!
|
||||||
|
* be more specific about module-loading behaviour (type set to LONG if
|
||||||
|
object is not defined _before_ this object).
|
||||||
|
* warning for POLAND
|
||||||
|
* E_bonk.e: make clearer that p.i++ works on p, not i
|
||||||
|
* chequepoint stuff
|
||||||
|
* tell crackers about double protection
|
||||||
|
* .end() is not called automatically
|
||||||
|
* In de docs staat het eerste deel van 12A aan het einde van 11C.
|
||||||
|
* backslashes dubbel doen in guide
|
||||||
|
* ^ is NOT equivalent to []
|
||||||
|
* OO alleen met NEW duidelijK?
|
||||||
|
* make clear librarybases are provided by the modules
|
||||||
|
* no lectures on asm, opti in compiler is hard
|
||||||
|
* modules circularity?
|
||||||
|
* OO objs with NEW only
|
||||||
|
* edbg: raise exceptions for current handler only 1 after DEF
|
||||||
|
- tell regs for libraries! myfun(A0), but slower than movem.
|
||||||
|
- in methods with a handler and register allocation on, "self" could
|
||||||
|
be allocated to a register where it shouldn't, and cause trashed
|
||||||
|
variables elsewhere
|
||||||
|
- "EXPORT PROC array( w, h, m=$10000) OF array" with OBJECT array
|
||||||
|
-> dirty backwards parsing, now made more specific
|
||||||
|
- compiles no errors, crashes on run: PROC main();DEF x;WriteF('\d\n',x.4);ENDPROC
|
||||||
|
-> two bugs: floatval would sometimes not lex as float (reg-prob), and
|
||||||
|
not check for preceding operator in expression.
|
||||||
|
[EC722]
|
||||||
|
|
||||||
|
[EC723]
|
||||||
|
- p2m maakt bagger van tweede char als libname bv `E_..' is.
|
||||||
|
- pp: #define X Unknown; X results in LINE 22: X ;WITH: Unknown;MACRO: nknown
|
||||||
|
- twice the same label: doesn't give error
|
||||||
|
- FOR x:=0 TO 3 DO FOR y:=0 TO 3; ...; ENDFOR
|
||||||
|
- in the compile part, methods could be called before the corresponding
|
||||||
|
OBJECT was processed (by calling it from an instance variable in a method
|
||||||
|
of an object defined higherup), which caused an intern102, now it is
|
||||||
|
an error71.
|
||||||
|
- double declaration objecthead geen probleem
|
||||||
|
- double declaration objectmember geen probleem
|
||||||
|
also solves nokielski's bug (cd2:src/betas/todebug/pack.lha)
|
||||||
|
- doc: both /* */ and -> are included in macros, -> gives problems
|
||||||
|
[EC723]
|
||||||
|
|
||||||
|
[EC724]
|
||||||
|
- bigbug: cache problem clear: what if compiled module (that needs to
|
||||||
|
be flushed) was accessed from a less deep dir...
|
||||||
|
paths in cache need to be ABSOLUTE
|
||||||
|
NameFromFH/NameFromLock/MatchFirst: all 2.04, all need file access
|
||||||
|
-> solved: flushcache() compares without path.
|
||||||
|
- IEEEbug
|
||||||
|
* patch is for 040/fpu only?
|
||||||
|
* internal errors on 060?!??
|
||||||
|
* make workaround for 3.1 floatproblem!!! [2,11,36,0,$0]
|
||||||
|
* 030/no881/3.1/mmu
|
||||||
|
PROC main()
|
||||||
|
DEF x
|
||||||
|
x:=2.1
|
||||||
|
ENDPROC
|
||||||
|
* compiling with Blizzard 060 -> "internal Errors": Apollo 1240:
|
||||||
|
[2,11,38,0,$0] DEF x=0.0, exp=0.0, pos, pos2,
|
||||||
|
* doc: FAQ: probs with floats in 3.1:
|
||||||
|
PatchMathSB10.lha util/boot 4K 7+Patch MathIEEESingBas/SPDiv,SPMul
|
||||||
|
-> EC warns + messys in docs.
|
||||||
|
- in Iconvert for PhxAss:
|
||||||
|
1) in makeasmfile(), just before the WriteF(' INCLUDE...), I added this line:
|
||||||
|
WriteF(' TTL ""\n')
|
||||||
|
(this line ensure that the chunk won't get any name, like A68K does).
|
||||||
|
2) The new command line for invokea68k() is:
|
||||||
|
IF Execute('SmallPhxAss I INCLUDES: FROM ram:iconvert.s noexe opt 0',NIL,stdout)=FALSE THEN error(ER_INVOKE)
|
||||||
|
-> added option "-p" for phxass
|
||||||
|
[v3.2g, EC724, to jason]
|
||||||
|
|
||||||
|
[v3.2h, EC725]
|
||||||
|
- device with library feature: not possible without different startup-code, or
|
||||||
|
atleast various registers made available to main() and close() -> doc
|
||||||
|
- doc:
|
||||||
|
* E source should contain atleast one function
|
||||||
|
* e reg is personal
|
||||||
|
* #define CharIsX(x) (x="x") substitutes the "x" too.
|
||||||
|
handy, i.e.: #define debug(x) (WriteF('now evaluating: x\n') BUT x)
|
||||||
|
* main() in lib works under Forbid() of OpenLibrary()
|
||||||
|
* homepage!
|
||||||
|
* FAQ: more on resources (available translations)
|
||||||
|
- eg: extra arg to plugin methods for font
|
||||||
|
- sw: PGA_TOTAL wasn't updated (refresh_scrollbars() (to allow max to change))
|
||||||
|
-> needed in EDBG for varwin
|
||||||
|
- edbg: var view scroller altijd 100%
|
||||||
|
- edbg: a search (for large sources)
|
||||||
|
- edbg: zwarte balk in onderste regel, dan window met een regel kleiner maken: rendering in border
|
||||||
|
- doc
|
||||||
|
* shr is ASR
|
||||||
|
* writef/printf string limit.
|
||||||
|
* address
|
||||||
|
- edbg: fixed enforcer hit in deletetoolbar
|
||||||
|
- fixed obj/member double decl test enfhit
|
||||||
|
- new tools, e.g. Iconvert, p2m, doc2guide? etc.
|
||||||
|
- explorer?
|
||||||
|
- include jasons rkrm2
|
||||||
|
[v3.2i, EC725, for Amy Resource]
|
||||||
|
|
||||||
|
- [easygui: over to jason]
|
||||||
|
[unreproduced bugs]
|
||||||
|
* eg: PALETTE: If the relx Argument is smaller than 3 it has got problems (Recoverble alert, hang-up)
|
||||||
|
* eg: double menu-selections do not trigger actions multiple times
|
||||||
|
[features]
|
||||||
|
* eg: maybe supply a host of handy plugins as standard (for images / image gadgets / boopsi gadgets etc.)
|
||||||
|
* eg: support GTLV_SHOWSELECTED (what were the problems?): really wanted
|
||||||
|
* eg: allow cut of window: smaller than minimum and solves small screens too?! panes? pages?
|
||||||
|
* eg: Other IDCMPs and messages (i.e. rawkey!)
|
||||||
|
* eg: the MX gadget does not use the "abovetext" field
|
||||||
|
* eg: APPWINDOW?
|
||||||
|
* eg: maybe builtin topaz fallback??
|
||||||
|
* eg: block input on other easygui's
|
||||||
|
prob: - intuition/blockinput.e doesn't stop resize and has ugly waitptr
|
||||||
|
- IDCMP_SIZEVERIFY not appropriate for long periods
|
||||||
|
better: if >1 window, process them all!
|
||||||
|
* eg: size num voor slider berekenen.
|
||||||
|
* eg: text in screenmode can't compute max, same for sqrt in raymond
|
||||||
|
* eg: bug: midsize align is mixed when one gadget has text on the left and other
|
||||||
|
on the right
|
||||||
|
- [edbg->to->jason]
|
||||||
|
* allow input hunkoffset->source in EDBG (cooperation with enforcer)
|
||||||
|
* allow obj editing with explorer
|
||||||
|
* double clicking on scroller gives breakpoint
|
||||||
|
* (see uncommented stuff (=new) in begl2) es wird nur kein SELECT <zahl> OF <variable> weiter verfolgt
|
||||||
|
* with many modules, source window tiling gets too small
|
||||||
|
* allow to break program while running? (non trivial)
|
||||||
|
* no builtin global variables
|
||||||
|
* show changed registers (and vars?)
|
||||||
|
* really describe all features in doc
|
||||||
|
[edbg, less urgent]
|
||||||
|
* skip statement/jump?
|
||||||
|
* object browser!
|
||||||
|
* repeat step in/over "exp" times (so in a for-loop of 3 lines, step over a-1*3)
|
||||||
|
* record step as rexx script
|
||||||
|
* do something about debug-interactivity or rexx-scripts
|
||||||
|
* more gadgets in toolbar
|
||||||
|
* enable more windows to hang around instead of requester-like
|
||||||
|
* add all builtin vars. what about other librarybases? -> redefine
|
||||||
|
* make memory and other stuff modifiable
|
||||||
|
* optimize refresh of some windows
|
||||||
|
|
||||||
|
[EC726, v3.2k]
|
||||||
|
- bug: doing obj.f[1] where f is untyped interpreted f as "PTR TO
|
||||||
|
SIZEOF obj" instead of "PTR TO CHAR", because type for next deref
|
||||||
|
wasn't properly set.
|
||||||
|
- bug: module-flush flushed only first module that matches. now guaranteed if
|
||||||
|
you compile x.m from anywhere, all modules called x.m (any path) will be flushed.
|
||||||
|
(also when you create a .m with LIBRARY)
|
||||||
|
- enum now works with negative numbers (ENUM AA=-1,BB,CC)
|
||||||
|
- if the source for a LIBRARY contains a proc close(), it will be
|
||||||
|
called when the user of the library calls CloseLibrary() (i.e. it is
|
||||||
|
the counterpart to main()). [close still mentioned as unref]
|
||||||
|
- OPT RTD flawed in the context of hooks, call backs etc -> disabled
|
||||||
|
- include other float ops ArcSin, ArcCos and ArcTan etc.
|
||||||
|
- "self" now has own (code 5) entry in debughunk for EDBG -> jason?
|
||||||
|
- bug fix of obj.f[1] undone :( now part of known bugs
|
||||||
|
- dist: include ieee patch? -> setpatch v43.6
|
||||||
|
- dist: texturemapper (v7)
|
||||||
|
- dist: nieuwe sw
|
||||||
|
- docs:
|
||||||
|
* validness of other registration sites
|
||||||
|
* fabio's new ML
|
||||||
|
* better email warning
|
||||||
|
* add to version history
|
||||||
|
* new float functions
|
||||||
|
* new features etc.
|
||||||
|
* make a known bugs list and a todo list (without any promises!)
|
||||||
|
- dist:
|
||||||
|
* fabio's objects
|
||||||
|
* gregors OOstuff, new build
|
||||||
|
* edbg: do more tests: doesn't find vars correctly. test self
|
||||||
|
* jason's EasyGUI, EDBG etc.
|
||||||
|
[-> out as beta to jason/gregor/fabio]
|
||||||
|
- incorporated some final tiny changes
|
||||||
|
- ebug.e: battclockbase not real base so needs EXPORT DEF
|
||||||
|
- EDBG sources
|
||||||
|
- demo ec + patches for 30 31 32
|
||||||
|
[EC733, v3.3a final release version]
|
|
@ -0,0 +1,43 @@
|
||||||
|
-RS
|
||||||
|
-L7
|
||||||
|
+NL
|
||||||
|
+AA
|
||||||
|
+RL
|
||||||
|
+XR
|
||||||
|
-PD
|
||||||
|
-LF
|
||||||
|
+PG
|
||||||
|
+HP
|
||||||
|
-AE
|
||||||
|
+DB
|
||||||
|
-IL
|
||||||
|
-1B
|
||||||
|
+.S
|
||||||
|
-CW
|
||||||
|
-LN
|
||||||
|
+AI
|
||||||
|
+L:
|
||||||
|
+DA
|
||||||
|
+SS
|
||||||
|
-OA
|
||||||
|
-UL
|
||||||
|
+;C
|
||||||
|
-PI
|
||||||
|
+PS
|
||||||
|
+RR
|
||||||
|
+AU
|
||||||
|
+SM
|
||||||
|
-FW
|
||||||
|
+FP
|
||||||
|
+OD
|
||||||
|
+DC
|
||||||
|
+UD
|
||||||
|
+EP
|
||||||
|
+LD
|
||||||
|
+KX
|
||||||
|
!.s
|
||||||
|
*ec:
|
||||||
|
|797|000|EEE|575|
|
||||||
|
[CPU2
|
||||||
|
[MMU0
|
||||||
|
\\700\r\
|
|
@ -0,0 +1,280 @@
|
||||||
|
/* Iconvert.e
|
||||||
|
|
||||||
|
This little utility converts a '.i' assembly include file
|
||||||
|
into an E binary module.
|
||||||
|
it understands '=' and 'EQU' for constant definitions, and builds
|
||||||
|
OBJECTs out of STRUCTURE definitions (from "exec/types.i").
|
||||||
|
it needs a macroassembler like A68k to run.
|
||||||
|
USAGE: iconvert <.ifile> */
|
||||||
|
|
||||||
|
OBJECT def
|
||||||
|
type:INT,name
|
||||||
|
ENDOBJECT
|
||||||
|
|
||||||
|
ENUM T_ARRAY,T_CHAR,T_INT,T_LONG=4,T_CONST=$10,T_STRUCT,T_END
|
||||||
|
ENUM ER_NONE,ER_IN,ER_OUT,ER_MEM,ER_WORK,ER_BREAK,ER_FORM,ER_TEMPW,
|
||||||
|
ER_INVOKE,ER_TEMPR,ER_OFORM,ER_I
|
||||||
|
|
||||||
|
CONST MAX_DEF=2500,IDMAX=10
|
||||||
|
|
||||||
|
DEF defs[MAX_DEF]:ARRAY OF def,
|
||||||
|
infile[100]:STRING,outfile[100]:STRING,
|
||||||
|
handle=NIL,flen,buf,bbuf,defn=0,line=0,mode=0,a,phx=FALSE
|
||||||
|
|
||||||
|
PROC main()
|
||||||
|
WriteF('Iconvert v0.2 1992 $#%!\n\n')
|
||||||
|
readinfile()
|
||||||
|
collectidents()
|
||||||
|
makeasmfile()
|
||||||
|
invokea68k()
|
||||||
|
readbinary()
|
||||||
|
makemodule()
|
||||||
|
error(ER_NONE)
|
||||||
|
ENDPROC
|
||||||
|
|
||||||
|
PROC readinfile()
|
||||||
|
DEF len
|
||||||
|
IF (arg[0]="-") AND (arg[1]="p") AND (arg[2]=" ")
|
||||||
|
arg:=arg+3
|
||||||
|
phx:=TRUE
|
||||||
|
ENDIF
|
||||||
|
StrCopy(infile,arg,ALL)
|
||||||
|
len:=EstrLen(infile)-2
|
||||||
|
IF infile[len]<>"." THEN error(ER_I)
|
||||||
|
StrCopy(outfile,arg,ALL); outfile[len+1]:="m"
|
||||||
|
IF (flen:=FileLength(infile))<1 THEN error(ER_IN)
|
||||||
|
IF (buf:=New(flen+1))=NIL THEN error(ER_MEM)
|
||||||
|
buf[flen]:=10
|
||||||
|
IF (handle:=Open(infile,OLDFILE))=NIL THEN error(ER_IN)
|
||||||
|
IF Read(handle,buf,flen)<>flen THEN error(ER_IN)
|
||||||
|
Close(handle); handle:=NIL
|
||||||
|
ENDPROC
|
||||||
|
|
||||||
|
PROC collectidents()
|
||||||
|
DEF p,a,id[IDMAX]:ARRAY OF LONG,num,f,c,end,zero=NIL,macro=FALSE
|
||||||
|
DEF struct=FALSE,d:PTR TO def,size,work[10]:STRING
|
||||||
|
WriteF('Now trying to convert "\s".\n',infile)
|
||||||
|
INC line; p:=buf; end:=p+flen; d:=defs
|
||||||
|
WHILE p<end
|
||||||
|
FOR a:=0 TO IDMAX-1 DO id[a]:=0
|
||||||
|
num:=0; f:=TRUE
|
||||||
|
WHILE f
|
||||||
|
c:=p[]++
|
||||||
|
IF zero; zero[]:=0; zero:=NIL; ENDIF
|
||||||
|
IF (c=10) OR (c=0)
|
||||||
|
f:=FALSE
|
||||||
|
ELSEIF (c<=" ") OR (c=",")
|
||||||
|
ELSEIF (c=";") OR (c="*")
|
||||||
|
f:=FALSE
|
||||||
|
WHILE p[]++<>10 DO NOP
|
||||||
|
ELSE
|
||||||
|
id[num]:=p-1
|
||||||
|
WHILE ((c:=p[]++)>" ") AND (c<>",") AND (c<>";") DO NOP
|
||||||
|
p--; zero:=p
|
||||||
|
IF num++=IDMAX THEN error(ER_WORK)
|
||||||
|
ENDIF
|
||||||
|
ENDWHILE
|
||||||
|
IF num
|
||||||
|
size:=-1
|
||||||
|
IF StrCmp(id[1],'MACRO',ALL)
|
||||||
|
macro:=TRUE
|
||||||
|
ELSEIF StrCmp(id[1],'EQU',ALL) OR StrCmp(id[1],'=',ALL) OR StrCmp(id[1],'equ',ALL)
|
||||||
|
IF macro=FALSE
|
||||||
|
d.type:=T_CONST
|
||||||
|
d.name++:=id[0]
|
||||||
|
defn++
|
||||||
|
ENDIF
|
||||||
|
ELSEIF StrCmp(id[0],'STRUCTURE',ALL)
|
||||||
|
struct:=TRUE
|
||||||
|
d.type:=T_STRUCT
|
||||||
|
d.name++:=id[1]
|
||||||
|
defn++
|
||||||
|
IF (InStr(id[2],'SIZEOF',0)<>-1) OR (InStr(id[2],'SIZE',0)<>-1)
|
||||||
|
IF defn>=MAX_DEF THEN error(ER_WORK)
|
||||||
|
d.type:=T_ARRAY
|
||||||
|
c:=InStr(id[2],'_',0)
|
||||||
|
IF c<>-1 THEN PutChar(id[2]+c,0)
|
||||||
|
d.name++:=id[2]
|
||||||
|
defn++
|
||||||
|
ELSEIF (Val(id[2],{c})<>0) OR (c=0)
|
||||||
|
WriteF('WARNING: object "\s" does not start at offset 0\n',id[1])
|
||||||
|
ENDIF
|
||||||
|
ELSEIF StrCmp(id[0],'LABEL',ALL)
|
||||||
|
IF struct=FALSE THEN error(ER_FORM)
|
||||||
|
IF (InStr(id[1],'SIZEOF',0)<>-1) OR (InStr(id[1],'SIZE',0)<>-1)
|
||||||
|
struct:=FALSE
|
||||||
|
d.type:=T_END
|
||||||
|
d.name++:=id[1]
|
||||||
|
defn++
|
||||||
|
ENDIF
|
||||||
|
ELSEIF StrCmp(id[0],'BYTE',ALL); size:=1
|
||||||
|
ELSEIF StrCmp(id[0],'UBYTE',ALL); size:=1
|
||||||
|
ELSEIF StrCmp(id[0],'WORD',ALL); size:=2
|
||||||
|
ELSEIF StrCmp(id[0],'UWORD',ALL); size:=2
|
||||||
|
ELSEIF StrCmp(id[0],'SHORT',ALL); size:=2
|
||||||
|
ELSEIF StrCmp(id[0],'USHORT',ALL); size:=2
|
||||||
|
ELSEIF StrCmp(id[0],'LONG',ALL); size:=4
|
||||||
|
ELSEIF StrCmp(id[0],'ULONG',ALL); size:=4
|
||||||
|
ELSEIF StrCmp(id[0],'APTR',ALL); size:=4
|
||||||
|
ELSEIF StrCmp(id[0],'BPTR',ALL); size:=4
|
||||||
|
ELSEIF StrCmp(id[0],'CPTR',ALL); size:=4
|
||||||
|
ELSEIF StrCmp(id[0],'BSTR',ALL); size:=4
|
||||||
|
ELSEIF StrCmp(id[0],'BOOL',ALL); size:=2
|
||||||
|
ELSEIF StrCmp(id[0],'FPTR',ALL); size:=4
|
||||||
|
ELSEIF StrCmp(id[0],'STRUCT',ALL); size:=0
|
||||||
|
ELSEIF StrCmp(id[1],'MACRO',ALL)
|
||||||
|
macro:=TRUE
|
||||||
|
ELSEIF StrCmp(id[0],'ENDM',ALL)
|
||||||
|
macro:=FALSE
|
||||||
|
ELSEIF StrCmp(id[0],'BITDEF',ALL) /* depends on correctness */
|
||||||
|
c:=id[1]-1
|
||||||
|
FOR a:=1 TO id[2]-id[1]-1 DO c[]++:=c[1]
|
||||||
|
c[]++:="F"; c[]++:="_"
|
||||||
|
d.type:=T_CONST
|
||||||
|
d.name++:=id[1]-1
|
||||||
|
defn++
|
||||||
|
ELSEIF StrCmp(id[0],'EITEM',ALL)
|
||||||
|
d.type:=T_CONST
|
||||||
|
d.name++:=id[1]
|
||||||
|
defn++
|
||||||
|
ELSEIF StrCmp(id[0],'DEVCMD',ALL)
|
||||||
|
d.type:=T_CONST
|
||||||
|
d.name++:=id[1]
|
||||||
|
defn++
|
||||||
|
ELSEIF StrCmp(id[1],'SET',ALL) OR StrCmp(id[0],'IFND',ALL) OR StrCmp(id[0],'ENDC',ALL) OR StrCmp(id[0],'INCLUDE',ALL) OR StrCmp(id[0],'ENUM',ALL) OR StrCmp(id[0],'LIBENT',ALL) OR StrCmp(id[0],'LIBINIT',ALL) OR StrCmp(id[0],'LIBDEF',ALL) OR StrCmp(id[0],'DEVINIT',ALL) OR StrCmp(id[0],'FUNCDEF',ALL) OR StrCmp(id[0],'include',ALL) OR StrCmp(id[0],'IFD',ALL)
|
||||||
|
ELSE
|
||||||
|
IF macro=FALSE THEN error(ER_FORM)
|
||||||
|
ENDIF
|
||||||
|
IF size<>-1
|
||||||
|
IF (InStr(id[1],'Kludge',0)<>-1) /* ask for deletion */
|
||||||
|
WriteF('Skip member "\s" (y/n)? >',id[1])
|
||||||
|
Read(stdout,work,2)
|
||||||
|
c:=work[0]
|
||||||
|
ELSE
|
||||||
|
c:=0
|
||||||
|
ENDIF
|
||||||
|
IF (c<>"y") AND (macro=FALSE)
|
||||||
|
IF struct=FALSE THEN error(ER_FORM)
|
||||||
|
IF size=1 THEN d.type:=T_CHAR ELSE IF size=2 THEN d.type:=T_INT ELSE IF size=4 THEN d.type:=T_LONG ELSE d.type:=T_ARRAY
|
||||||
|
d.name++:=id[1]
|
||||||
|
defn++
|
||||||
|
ENDIF
|
||||||
|
ENDIF
|
||||||
|
IF defn>=MAX_DEF THEN error(ER_WORK)
|
||||||
|
ENDIF
|
||||||
|
IF CtrlC() THEN error(ER_BREAK)
|
||||||
|
IF (line AND $F)=0 THEN WriteF('line=\d\b',line)
|
||||||
|
INC line
|
||||||
|
ENDWHILE
|
||||||
|
line:=0
|
||||||
|
ENDPROC
|
||||||
|
|
||||||
|
PROC makeasmfile()
|
||||||
|
DEF a,oldout,d:PTR TO def
|
||||||
|
IF (handle:=Open('ram:iconvert.s',NEWFILE))=NIL THEN error(ER_TEMPW)
|
||||||
|
IF defn=0 THEN error(ER_FORM)
|
||||||
|
d:=defs
|
||||||
|
oldout:=SetStdOut(handle)
|
||||||
|
IF phx THEN WriteF(' TTL ""\n')
|
||||||
|
WriteF(' INCLUDE "\s"\n\n',infile)
|
||||||
|
FOR a:=1 TO defn
|
||||||
|
WriteF(' DC.L \s\n',d.name++)
|
||||||
|
ENDFOR
|
||||||
|
WriteF('\n END\n')
|
||||||
|
SetStdOut(oldout)
|
||||||
|
Close(handle); handle:=NIL
|
||||||
|
ENDPROC
|
||||||
|
|
||||||
|
PROC invokea68k()
|
||||||
|
IF phx
|
||||||
|
IF Execute('SmallPhxAss I INCLUDES: FROM ram:iconvert.s noexe opt 0',NIL,stdout)=FALSE THEN error(ER_INVOKE)
|
||||||
|
ELSE
|
||||||
|
IF Execute('A68k -iINCLUDES: ram:iconvert.s',NIL,stdout)=FALSE THEN error(ER_INVOKE)
|
||||||
|
ENDIF
|
||||||
|
ENDPROC
|
||||||
|
|
||||||
|
PROC readbinary()
|
||||||
|
DEF bsize
|
||||||
|
bsize:=defn*4+32
|
||||||
|
IF FileLength('ram:iconvert.o')<>bsize THEN error(ER_TEMPR)
|
||||||
|
bbuf:=New(bsize)
|
||||||
|
IF bbuf=NIL THEN error(ER_MEM)
|
||||||
|
IF (handle:=Open('ram:iconvert.o',OLDFILE))=NIL THEN error(ER_TEMPR)
|
||||||
|
IF Read(handle,bbuf,bsize)<>bsize THEN error(ER_TEMPR)
|
||||||
|
Close(handle); handle:=NIL
|
||||||
|
ENDPROC
|
||||||
|
|
||||||
|
PROC makemodule()
|
||||||
|
DEF v:PTR TO LONG,work[3]:ARRAY OF INT,d:PTR TO def,l,x,obj=NIL
|
||||||
|
v:=bbuf+28; d:=defs
|
||||||
|
IF (handle:=Open(outfile,NEWFILE))=NIL THEN error(ER_OUT)
|
||||||
|
Write(handle,'EMOD',4)
|
||||||
|
FOR a:=1 TO defn
|
||||||
|
IF d.type=T_CONST
|
||||||
|
IF mode=2 THEN endobj(obj)
|
||||||
|
IF mode=0 THEN Write(handle,[1]:INT,2)
|
||||||
|
l:=StrLen(d.name)+1
|
||||||
|
work[0]:=IF Odd(l) THEN l+1 ELSE l
|
||||||
|
PutLong(work+2,v[]++)
|
||||||
|
Write(handle,work,6)
|
||||||
|
UpperStr(d.name)
|
||||||
|
Write(handle,d.name++,l)
|
||||||
|
IF Odd(l) THEN Write(handle,'',1)
|
||||||
|
mode:=1
|
||||||
|
ELSEIF d.type=T_END
|
||||||
|
IF mode<>2 THEN error(ER_OFORM)
|
||||||
|
mode:=0
|
||||||
|
Write(handle,[0,v[]++]:INT,4)
|
||||||
|
d++
|
||||||
|
ELSE
|
||||||
|
IF (mode=2) AND (d.type=T_STRUCT) THEN endobj(obj)
|
||||||
|
IF mode=1 THEN Write(handle,[0]:INT,2)
|
||||||
|
IF mode<>2
|
||||||
|
Write(handle,[2]:INT,2)
|
||||||
|
IF d.type<>T_STRUCT THEN error(ER_OFORM)
|
||||||
|
obj:=d.name
|
||||||
|
ENDIF
|
||||||
|
work[1]:=IF d.type=T_STRUCT THEN -1 ELSE d.type
|
||||||
|
work[2]:=v[]++
|
||||||
|
x:=InStr(d.name,'_',0)
|
||||||
|
IF x<>-1 THEN d.name:=d.name+x+1
|
||||||
|
LowerStr(d.name)
|
||||||
|
l:=StrLen(d.name)+1
|
||||||
|
work[0]:=IF Odd(l) THEN l+1 ELSE l
|
||||||
|
Write(handle,work,6)
|
||||||
|
Write(handle,d.name++,l)
|
||||||
|
IF Odd(l) THEN Write(handle,'',1)
|
||||||
|
mode:=2
|
||||||
|
ENDIF
|
||||||
|
ENDFOR
|
||||||
|
IF mode=2 THEN endobj(obj)
|
||||||
|
IF mode=1 THEN Write(handle,[0]:INT,2)
|
||||||
|
Close(handle); handle:=NIL
|
||||||
|
ENDPROC
|
||||||
|
|
||||||
|
PROC endobj(obj)
|
||||||
|
Write(handle,[0,-1]:INT,4)
|
||||||
|
WriteF('WARNING: object "\s" has no SIZE\n',obj)
|
||||||
|
mode:=0
|
||||||
|
ENDPROC
|
||||||
|
|
||||||
|
PROC error(er)
|
||||||
|
IF handle THEN Close(handle)
|
||||||
|
SELECT er
|
||||||
|
CASE ER_NONE; WriteF('Done.\n')
|
||||||
|
CASE ER_IN; WriteF('Could not read "\s"\n',infile)
|
||||||
|
CASE ER_OUT; WriteF('Could not write "\s"\n',outfile)
|
||||||
|
CASE ER_MEM; WriteF('Could not allocate memory\n')
|
||||||
|
CASE ER_WORK; WriteF('Work buffer overflow\n')
|
||||||
|
CASE ER_BREAK; WriteF('User terminated convertion\n')
|
||||||
|
CASE ER_FORM; WriteF('.i file format error\n')
|
||||||
|
CASE ER_TEMPW; WriteF('Trouble creating temporarily file\n')
|
||||||
|
CASE ER_TEMPR; WriteF('Trouble reading temporarily file\n')
|
||||||
|
CASE ER_INVOKE; WriteF('Could not invoke A68k\n')
|
||||||
|
CASE ER_OFORM; WriteF('Error in object order line \d in "ram:iconvert.s"\n',a+1)
|
||||||
|
CASE ER_I; WriteF('Not a ".i" file: "\s"\n',infile)
|
||||||
|
ENDSELECT
|
||||||
|
IF line THEN WriteF('At line \d in file "\s"\n',line,infile)
|
||||||
|
IF er>0 THEN DeleteFile(outfile)
|
||||||
|
CleanUp(0)
|
||||||
|
ENDPROC
|
|
@ -0,0 +1,106 @@
|
||||||
|
/* Pragma2Module
|
||||||
|
convert a SAS/C library pragma file to an E module.
|
||||||
|
Usage: p2m <file>
|
||||||
|
converts <file.h> to <file.m> */
|
||||||
|
|
||||||
|
ENUM INPUT_ERROR=10,OUTPUT_ERROR,FORMAT_ERROR
|
||||||
|
|
||||||
|
DEF cfh,efh,eof,
|
||||||
|
gotbase=FALSE,
|
||||||
|
offset=30,
|
||||||
|
cfile[200]:STRING,
|
||||||
|
efile[200]:STRING,
|
||||||
|
cstring[200]:STRING
|
||||||
|
|
||||||
|
PROC main()
|
||||||
|
StrCopy(cfile,arg,ALL)
|
||||||
|
StrAdd(cfile,'.h',ALL)
|
||||||
|
StrCopy(efile,arg,ALL)
|
||||||
|
StrAdd(efile,'.m',ALL)
|
||||||
|
WriteF('Amiga E Pragma2Module by $#%! in 1992\nconverting: "\s"\n',cfile)
|
||||||
|
IF (cfh:=Open(cfile,OLDFILE))=0 THEN closeall(INPUT_ERROR)
|
||||||
|
IF (efh:=Open(efile,NEWFILE))=0 THEN closeall(OUTPUT_ERROR)
|
||||||
|
REPEAT
|
||||||
|
eof:=ReadStr(cfh,cstring)
|
||||||
|
convert(cstring)
|
||||||
|
UNTIL eof
|
||||||
|
Write(efh,[$FF,0,0,0]:CHAR,4)
|
||||||
|
WriteF('last offset: -\d\n',offset)
|
||||||
|
WriteF('Done.\n')
|
||||||
|
closeall(0)
|
||||||
|
ENDPROC
|
||||||
|
|
||||||
|
PROC closeall(er)
|
||||||
|
IF cfh<>0 THEN Close(cfh)
|
||||||
|
IF efh<>0 THEN Close(efh)
|
||||||
|
SELECT er
|
||||||
|
CASE INPUT_ERROR; WriteF('Could not open input file!\n')
|
||||||
|
CASE OUTPUT_ERROR; WriteF('Could not open output file!\n')
|
||||||
|
CASE FORMAT_ERROR; WriteF('Pragma file format error!\n')
|
||||||
|
ENDSELECT
|
||||||
|
CleanUp(er)
|
||||||
|
ENDPROC
|
||||||
|
|
||||||
|
/* format of line to convert:
|
||||||
|
#pragma libcall <lib>Base <funcname> <hexoffset> <regsused> */
|
||||||
|
|
||||||
|
PROC convert(str)
|
||||||
|
DEF pos,pos2,off2,len,narg,a,empty,dstr[50]:STRING,basestr[50]:STRING
|
||||||
|
DEF funcstr[50]:STRING,regstr[20]:STRING
|
||||||
|
IF StrCmp(str,'#pragma libcall ',STRLEN) /* just those lines */
|
||||||
|
pos:=STRLEN
|
||||||
|
pos2:=InStr(str,' ',pos) /* find base */
|
||||||
|
IF pos2=-1 THEN closeall(FORMAT_ERROR)
|
||||||
|
IF gotbase=FALSE /* get base */
|
||||||
|
gotbase:=TRUE
|
||||||
|
MidStr(basestr,str,pos,pos2-pos)
|
||||||
|
LowerStr(basestr)
|
||||||
|
WriteF('Base will be: \s\n',basestr)
|
||||||
|
Write(efh,["EM","OD",6]:INT,6)
|
||||||
|
Out(efh,0)
|
||||||
|
Write(efh,basestr,EstrLen(basestr)+1)
|
||||||
|
ENDIF
|
||||||
|
pos:=pos2+1
|
||||||
|
pos2:=InStr(str,' ',pos) /* find func */
|
||||||
|
IF pos2=-1 THEN closeall(FORMAT_ERROR)
|
||||||
|
MidStr(funcstr,str,pos,pos2-pos)
|
||||||
|
IF (funcstr[0]>="a") AND (funcstr[0]<="z") THEN funcstr[0]:=funcstr[0]-32 /* Func, not fUnc */
|
||||||
|
IF (funcstr[1]>="A") AND (funcstr[1]<="Z") THEN funcstr[1]:=funcstr[1]+32
|
||||||
|
str[pos2]:="$"
|
||||||
|
UpperStr(str+pos2)
|
||||||
|
off2:=Val(str+pos2,NIL) /* get offset */
|
||||||
|
IF off2=0 THEN closeall(FORMAT_ERROR)
|
||||||
|
empty:=0
|
||||||
|
WHILE off2<>offset
|
||||||
|
Out(efh,16) /* "empty function slots" */
|
||||||
|
offset:=offset+6
|
||||||
|
INC empty
|
||||||
|
IF offset>off2 THEN closeall(FORMAT_ERROR)
|
||||||
|
ENDWHILE
|
||||||
|
IF empty>0 THEN WriteF('found \d empty or private functions before -\d.\n',empty,offset)
|
||||||
|
pos2:=InStr(str,' ',pos2+1) /* find offset/regs */
|
||||||
|
IF pos2=-1 THEN closeall(FORMAT_ERROR)
|
||||||
|
pos:=pos2+1
|
||||||
|
MidStr(dstr,str,pos,ALL)
|
||||||
|
Write(efh,funcstr,EstrLen(funcstr))
|
||||||
|
len:=EstrLen(dstr)
|
||||||
|
narg:=Val(dstr+len-1,NIL)
|
||||||
|
IF len>2
|
||||||
|
IF len-2<narg THEN StrCopy(regstr,'0',ALL)
|
||||||
|
StrAdd(regstr,dstr,len-2)
|
||||||
|
ELSE
|
||||||
|
StrCopy(regstr,'',ALL)
|
||||||
|
ENDIF
|
||||||
|
len:=EstrLen(regstr)
|
||||||
|
StrCopy(dstr,'$ ',ALL)
|
||||||
|
IF len>0
|
||||||
|
FOR a:=1 TO len
|
||||||
|
dstr[1]:=regstr[len-a]
|
||||||
|
Out(efh,Val(dstr,NIL))
|
||||||
|
ENDFOR
|
||||||
|
ELSE
|
||||||
|
IF narg=0 THEN Out(efh,16) ELSE Out(efh,0)
|
||||||
|
ENDIF
|
||||||
|
offset:=offset+6
|
||||||
|
ENDIF
|
||||||
|
ENDPROC
|
|
@ -0,0 +1,405 @@
|
||||||
|
-> EC debugger
|
||||||
|
|
||||||
|
OBJECT debuginfo
|
||||||
|
objs,procs,identhash,dbugval,dbugadr,modinfolist,codelist,memlist,heap,macrohash
|
||||||
|
ENDOBJECT
|
||||||
|
|
||||||
|
PROC main()
|
||||||
|
DEF s[10]:STRING,c=1,port,db:PTR TO debuginfo
|
||||||
|
WriteF('EC structure debugger\n')
|
||||||
|
port:=FindPort('EmoduleCache')
|
||||||
|
IF port=NIL
|
||||||
|
WriteF('no Cache!\n')
|
||||||
|
RETURN 10
|
||||||
|
ENDIF
|
||||||
|
WHILE c
|
||||||
|
WriteF('show which datastructure? (<lf> to quit)\n')
|
||||||
|
WriteF('["O"bjects,"P"procs,"I"dents,"V"alue,"A"dr,"M"odInfo,"C"odeRem,\n"K"ookiecheck,"H"eap,mac"R"os]: ')
|
||||||
|
ReadStr(stdin,s)
|
||||||
|
LowerStr(s)
|
||||||
|
lf()
|
||||||
|
c:=s[]
|
||||||
|
db:=Long(port+42)
|
||||||
|
do(c,db)
|
||||||
|
lf()
|
||||||
|
ENDWHILE
|
||||||
|
ENDPROC
|
||||||
|
|
||||||
|
PROC do(c,db:PTR TO debuginfo) HANDLE
|
||||||
|
SELECT c
|
||||||
|
CASE "o"; object(Long(db.objs))
|
||||||
|
CASE "p"; proc(Long(db.procs))
|
||||||
|
CASE "m"; modinfo(Long(db.modinfolist))
|
||||||
|
CASE "c"; coderem(Long(db.codelist))
|
||||||
|
CASE "k"; cookie(Long(db.memlist))
|
||||||
|
CASE "h"; heaps(db.heap)
|
||||||
|
CASE "i"; identhash(db.identhash)
|
||||||
|
CASE "v"; val(db.dbugval)
|
||||||
|
CASE "a"; mem(db.dbugadr)
|
||||||
|
CASE "r"; macros(db.macrohash)
|
||||||
|
CASE "\0"; WriteF('\ndone.\n')
|
||||||
|
DEFAULT; WriteF('Unsupported datastructure\n')
|
||||||
|
ENDSELECT
|
||||||
|
EXCEPT
|
||||||
|
WriteF(IF exception="INV" THEN 'terminated because of invalid address\n' ELSE 'exception!')
|
||||||
|
ENDPROC
|
||||||
|
|
||||||
|
PROC valid(a)
|
||||||
|
IF (a<$20000) OR (a>$A00000) -> based on 2meg chip and 8meg fast
|
||||||
|
IF a
|
||||||
|
WriteF('[INVALID:$\z\h[8]]',a)
|
||||||
|
RETURN FALSE
|
||||||
|
ENDIF
|
||||||
|
ENDIF
|
||||||
|
ENDPROC TRUE
|
||||||
|
|
||||||
|
PROC string(s) IS IF valid(s) THEN WriteF(IF s THEN 'str="\s"; ' ELSE 'str=NIL; ',s) ELSE 0
|
||||||
|
PROC str(name,s) IS IF valid(s) THEN IF s<>NIL THEN WriteF('\s="\s"; ',name,s) ELSE 0 ELSE 0
|
||||||
|
PROC flags(b) IS WriteF('flags=$\h; ',b)
|
||||||
|
PROC int(s,i) IS WriteF('\s=\d; ',s,i)
|
||||||
|
PROC lf() IS WriteF('\n')
|
||||||
|
PROC t() IS WriteF(' ')
|
||||||
|
PROC negnext(o) IS Long(o-4)
|
||||||
|
|
||||||
|
OBJECT objectheader
|
||||||
|
->onext:LONG -> -4
|
||||||
|
odel:INT -> 0, delegate size
|
||||||
|
otype:CHAR -> 2, flags: bit 0=export
|
||||||
|
dummy:CHAR -> 3, _empty_spot_
|
||||||
|
osize:INT -> 4, sizeof object
|
||||||
|
oid:INT -> 6, object ID
|
||||||
|
oascii:LONG -> 8
|
||||||
|
omemb:LONG -> 12, memberlist
|
||||||
|
omethod:LONG -> 16, methodlist
|
||||||
|
osuper:LONG -> 20, superclass
|
||||||
|
odcode -> 24
|
||||||
|
oacc -> 28
|
||||||
|
odeloff:INT -> 32
|
||||||
|
odestr:INT -> 34
|
||||||
|
ENDOBJECT
|
||||||
|
|
||||||
|
PROC object(a:PTR TO objectheader)
|
||||||
|
DEF s:PTR TO objectheader
|
||||||
|
IF valid(a)
|
||||||
|
IF a
|
||||||
|
IF a.otype AND 2 = 0
|
||||||
|
string(a.oascii)
|
||||||
|
flags(a.otype)
|
||||||
|
int('sizeof',a.osize)
|
||||||
|
int('id',a.oid)
|
||||||
|
int('delsize',a.odel)
|
||||||
|
int('deloff',a.odeloff)
|
||||||
|
int('destr',a.odestr)
|
||||||
|
s:=a.osuper
|
||||||
|
IF s THEN s:=s.oascii
|
||||||
|
WriteF('superclass:')
|
||||||
|
string(s)
|
||||||
|
WriteF('\nmemberlist:\n')
|
||||||
|
member(a.omemb)
|
||||||
|
WriteF('methodlist:\n')
|
||||||
|
method(a.omethod)
|
||||||
|
lf()
|
||||||
|
object(negnext(a))
|
||||||
|
ENDIF
|
||||||
|
ENDIF
|
||||||
|
ENDIF
|
||||||
|
ENDPROC
|
||||||
|
|
||||||
|
OBJECT member
|
||||||
|
-> onext:PTR TO member -> -4
|
||||||
|
ooff:INT -> 0, offset
|
||||||
|
oflags:CHAR -> 2, SET PRIVATE,HASPTRTYPE
|
||||||
|
dummy:CHAR -> 3, _empty_spot_
|
||||||
|
osize:INT -> 4, fieldsize [1,2,4,0=array]
|
||||||
|
oid:INT -> 6, object ID
|
||||||
|
oascii:LONG -> 8, NIL if PRIVATE
|
||||||
|
optrtype:LONG -> 12, only if HASPTRTYPE
|
||||||
|
ENDOBJECT
|
||||||
|
|
||||||
|
PROC member(m:PTR TO member)
|
||||||
|
IF m
|
||||||
|
IF valid(m)
|
||||||
|
t(); string(m.oascii)
|
||||||
|
int('offset',m.ooff)
|
||||||
|
int('size',m.osize)
|
||||||
|
int('id',m.oid)
|
||||||
|
flags(m.oflags)
|
||||||
|
IF m.oflags AND 2
|
||||||
|
IF m.optrtype AND $FFFFFFF0 = 0
|
||||||
|
int('ptrtype',m.optrtype)
|
||||||
|
ELSE
|
||||||
|
WriteF('ptrtype=<object>')
|
||||||
|
ENDIF
|
||||||
|
ENDIF
|
||||||
|
lf()
|
||||||
|
member(negnext(m))
|
||||||
|
ENDIF
|
||||||
|
ENDIF
|
||||||
|
ENDPROC
|
||||||
|
|
||||||
|
OBJECT proc
|
||||||
|
-> next:PTR TO proc -> -4
|
||||||
|
nrargs:INT -> 0
|
||||||
|
flags:CHAR -> 2, SET COMPILE_RTD,METHOD
|
||||||
|
numregvars:CHAR -> 3, 0..3
|
||||||
|
nrloc:INT -> 4, -(nrloc*4)
|
||||||
|
defargs:LONG -> 6
|
||||||
|
of_object:LONG -> 10
|
||||||
|
ident:LONG -> 14
|
||||||
|
self -> 18
|
||||||
|
method -> 22
|
||||||
|
regtab:PTR TO LONG -> 26
|
||||||
|
ENDOBJECT
|
||||||
|
|
||||||
|
OBJECT method
|
||||||
|
m_next:LONG -> 0
|
||||||
|
m_proc:LONG -> 4
|
||||||
|
m_type:CHAR -> 8 [0=MT_METHOD, 1=MT_FROZEN, 2=MT_SHARED]
|
||||||
|
m_flags:CHAR -> 9 SET INHERITED
|
||||||
|
m_off:INT -> 10 delegate offset
|
||||||
|
m_name:LONG -> 12
|
||||||
|
ENDOBJECT
|
||||||
|
|
||||||
|
PROC method(m:PTR TO method)
|
||||||
|
DEF p:PTR TO proc
|
||||||
|
IF m
|
||||||
|
IF valid(m)
|
||||||
|
t(); string(m.m_name)
|
||||||
|
int('type',m.m_type)
|
||||||
|
flags(m.m_flags)
|
||||||
|
int('deloff',m.m_off)
|
||||||
|
WriteF(' -> proc: ')
|
||||||
|
p:=m.m_proc
|
||||||
|
int('nargs',p.nrargs)
|
||||||
|
lf()
|
||||||
|
method(m.m_next)
|
||||||
|
ENDIF
|
||||||
|
ENDIF
|
||||||
|
ENDPROC
|
||||||
|
|
||||||
|
PROC val(x)
|
||||||
|
WriteF('value = \d, $\h\n',x,x)
|
||||||
|
ENDPROC
|
||||||
|
|
||||||
|
PROC mem(a:PTR TO LONG)
|
||||||
|
DEF b,c
|
||||||
|
c:=a
|
||||||
|
WriteF('dump ($\h) = ',a)
|
||||||
|
IF valid(a)
|
||||||
|
FOR b:=1 TO 16 DO WriteF('\z$\h[8] ',a[]++)
|
||||||
|
ENDIF
|
||||||
|
WriteF('\n"\s"\n',c)
|
||||||
|
lf()
|
||||||
|
ENDPROC
|
||||||
|
|
||||||
|
PROC identhash(p:PTR TO LONG)
|
||||||
|
DEF x
|
||||||
|
IF valid(p)
|
||||||
|
IF p
|
||||||
|
FOR x:=1 TO 256 DO IF p[] THEN ident(p[]++) ELSE p++
|
||||||
|
ENDIF
|
||||||
|
ENDIF
|
||||||
|
ENDPROC
|
||||||
|
|
||||||
|
OBJECT ident
|
||||||
|
|
||||||
|
-> flags = SET USED,SYS_VAR,EXPORT(in main var is uit module),REG,REAL|METHOD
|
||||||
|
-> etype = PTR TO [1,2,4] | PTR TO <object>
|
||||||
|
-> offset = +arg, -localvar
|
||||||
|
|
||||||
|
->next:LONG -> -4
|
||||||
|
etype:LONG -> 0, [type,type,ascii]
|
||||||
|
type:CHAR -> 4, [local,global,lab] = [1,2,3]
|
||||||
|
flags:CHAR -> 5
|
||||||
|
pr:LONG -> 6, [proc,PTR TO globinfo|NIL,proc|NIL if lab]
|
||||||
|
info:INT -> 10, [offset/regnum,offset,ID=label]
|
||||||
|
heavy:LONG -> VARHEAVY, only for local+reg_alloc
|
||||||
|
ENDOBJECT
|
||||||
|
|
||||||
|
PROC ident(p:PTR TO ident)
|
||||||
|
DEF t,n=NIL,pr=NIL,tn,f,fs[100]:STRING,type=NIL
|
||||||
|
IF valid(p)
|
||||||
|
IF (p.flags AND 2)=0 AND p
|
||||||
|
t:=p.type
|
||||||
|
f:=p.flags
|
||||||
|
IF f AND 4 THEN StrAdd(fs,'EXPORT')
|
||||||
|
IF f AND 8 THEN StrAdd(fs,'REG')
|
||||||
|
IF f AND 16 THEN StrAdd(fs,'METHOD/REAL')
|
||||||
|
IF EstrLen(fs)=0 THEN fs:=NIL
|
||||||
|
SELECT t
|
||||||
|
CASE 1; tn:='LOCAL'; pr:=Long(Long(p.pr+14))
|
||||||
|
CASE 2; tn:='GLOB'
|
||||||
|
CASE 3; tn:='PROC'; IF p.pr=NIL THEN tn:='LAB'; n:=p.etype
|
||||||
|
DEFAULT; tn:='UNDEF'
|
||||||
|
ENDSELECT
|
||||||
|
IF (t=1) OR (t=2)
|
||||||
|
type:=p.etype
|
||||||
|
SELECT type
|
||||||
|
CASE 1; type:=NIL
|
||||||
|
CASE 2; type:='INT'
|
||||||
|
CASE 4; type:='LONG'
|
||||||
|
DEFAULT; type:=Long(p.etype+8)
|
||||||
|
ENDSELECT
|
||||||
|
ENDIF
|
||||||
|
WriteF(' ')
|
||||||
|
str('proc',pr)
|
||||||
|
str('name',n)
|
||||||
|
str('sort',tn)
|
||||||
|
str('flags',fs)
|
||||||
|
str('ptr_to',type)
|
||||||
|
WriteF('info=\d; heavy=$\h\n',p.info,p.heavy)
|
||||||
|
ident(negnext(p))
|
||||||
|
ENDIF
|
||||||
|
ENDIF
|
||||||
|
ENDPROC
|
||||||
|
|
||||||
|
PROC proc(p:PTR TO proc)
|
||||||
|
DEF i:PTR TO ident, s:PTR TO objectheader, rt:PTR TO LONG
|
||||||
|
IF valid(p)
|
||||||
|
IF p
|
||||||
|
WriteF('at:$\h; ',p)
|
||||||
|
int('args',p.nrargs)
|
||||||
|
int('locals',p.nrloc)
|
||||||
|
int('numregvars',p.numregvars)
|
||||||
|
flags(p.flags)
|
||||||
|
WriteF('ident:')
|
||||||
|
i:=p.ident
|
||||||
|
IF i AND valid(i) THEN string(i.etype)
|
||||||
|
IF i:=p.self
|
||||||
|
IF valid(i)
|
||||||
|
WriteF('\n self:')
|
||||||
|
IF valid(s:=i.etype) THEN string(s.oascii)
|
||||||
|
ENDIF
|
||||||
|
ENDIF
|
||||||
|
lf()
|
||||||
|
IF rt:=p.regtab
|
||||||
|
WriteF('\tregalloc:')
|
||||||
|
WHILE rt[]
|
||||||
|
WriteF(' \d',Shr(Long(rt[]+12),8)-1)
|
||||||
|
rt++
|
||||||
|
ENDWHILE
|
||||||
|
lf()
|
||||||
|
ENDIF
|
||||||
|
proc(negnext(p))
|
||||||
|
ENDIF
|
||||||
|
ENDIF
|
||||||
|
ENDPROC
|
||||||
|
|
||||||
|
OBJECT modinfo
|
||||||
|
next,flags:INT,namelen,mod,list,name
|
||||||
|
ENDOBJECT
|
||||||
|
|
||||||
|
OBJECT procclass
|
||||||
|
next,type:INT,info,acc
|
||||||
|
ENDOBJECT
|
||||||
|
|
||||||
|
OBJECT acc
|
||||||
|
next,code
|
||||||
|
ENDOBJECT
|
||||||
|
|
||||||
|
PROC modinfo(p:PTR TO modinfo)
|
||||||
|
IF valid(p)
|
||||||
|
IF p
|
||||||
|
str('mod',p.name)
|
||||||
|
int('flags',p.flags)
|
||||||
|
lf()
|
||||||
|
procclass(p.list)
|
||||||
|
modinfo(p.next)
|
||||||
|
ENDIF
|
||||||
|
ENDIF
|
||||||
|
ENDPROC
|
||||||
|
|
||||||
|
PROC procclass(p:PTR TO procclass)
|
||||||
|
DEF x=0,l:PTR TO acc
|
||||||
|
IF valid(p)
|
||||||
|
IF p
|
||||||
|
int('type',p.type)
|
||||||
|
l:=p.acc
|
||||||
|
WHILE l
|
||||||
|
x++
|
||||||
|
l:=l.next
|
||||||
|
ENDWHILE
|
||||||
|
IF p.type=2 THEN str('lab',Long(p.info))
|
||||||
|
int('numacc',x)
|
||||||
|
lf()
|
||||||
|
procclass(p.next)
|
||||||
|
ENDIF
|
||||||
|
ENDIF
|
||||||
|
ENDPROC
|
||||||
|
|
||||||
|
OBJECT coderem
|
||||||
|
next,type:INT,info1,info2
|
||||||
|
ENDOBJECT
|
||||||
|
|
||||||
|
PROC coderem(p:PTR TO coderem)
|
||||||
|
IF valid(p)
|
||||||
|
IF p
|
||||||
|
int('type',p.type)
|
||||||
|
IF (p.type=5) OR (p.type=6) THEN str('ref_name',p.info1)
|
||||||
|
lf()
|
||||||
|
coderem(p.next)
|
||||||
|
ENDIF
|
||||||
|
ENDIF
|
||||||
|
ENDPROC
|
||||||
|
|
||||||
|
PROC template(p:PTR TO proc)
|
||||||
|
IF valid(p)
|
||||||
|
IF p
|
||||||
|
-> ...
|
||||||
|
template(negnext(p))
|
||||||
|
ENDIF
|
||||||
|
ENDIF
|
||||||
|
ENDPROC
|
||||||
|
|
||||||
|
CONST COOKIE=$BE
|
||||||
|
|
||||||
|
OBJECT memlist
|
||||||
|
next,size
|
||||||
|
ENDOBJECT
|
||||||
|
|
||||||
|
PROC cookie(meml:PTR TO memlist)
|
||||||
|
DEF a
|
||||||
|
WHILE meml
|
||||||
|
IF valid(meml)
|
||||||
|
a:=meml+meml.size-1
|
||||||
|
IF a[]<>COOKIE
|
||||||
|
DisplayBeep(NIL)
|
||||||
|
DisplayBeep(NIL)
|
||||||
|
DisplayBeep(NIL)
|
||||||
|
WriteF('cookie check failed!!!!!!!!!\n')
|
||||||
|
RETURN
|
||||||
|
ENDIF
|
||||||
|
meml:=meml.next
|
||||||
|
ELSE
|
||||||
|
RETURN
|
||||||
|
ENDIF
|
||||||
|
ENDWHILE
|
||||||
|
ENDPROC
|
||||||
|
|
||||||
|
PROC heaps(h:PTR TO LONG)
|
||||||
|
WHILE CtrlC()=FALSE
|
||||||
|
IF (h[2]-100>h[]) OR (h[2]+13000<h[])
|
||||||
|
WriteF('!!!!\n')
|
||||||
|
DisplayBeep(NIL)
|
||||||
|
Delay(10)
|
||||||
|
ENDIF
|
||||||
|
ENDWHILE
|
||||||
|
ENDPROC
|
||||||
|
|
||||||
|
OBJECT macro
|
||||||
|
next:PTR TO macro
|
||||||
|
name,body
|
||||||
|
nargs:INT
|
||||||
|
flags:CHAR
|
||||||
|
ENDOBJECT
|
||||||
|
|
||||||
|
PROC macros(m:PTR TO LONG)
|
||||||
|
DEF a,l:PTR TO macro
|
||||||
|
FOR a:=0 TO 255
|
||||||
|
l:=m[a]
|
||||||
|
WHILE l
|
||||||
|
WriteF('\s/\d = "\s"\n',l.name,l.nargs,l.body)
|
||||||
|
l:=l.next
|
||||||
|
ENDWHILE
|
||||||
|
ENDFOR
|
||||||
|
ENDPROC
|
|
@ -0,0 +1,42 @@
|
||||||
|
-> FlushCache.e
|
||||||
|
|
||||||
|
MODULE 'exec/ports'
|
||||||
|
|
||||||
|
OBJECT cache
|
||||||
|
port:mp, cookie, modlist, debuginfo, lock:INT, reserved:INT, name[50]:ARRAY
|
||||||
|
ENDOBJECT
|
||||||
|
|
||||||
|
OBJECT mod
|
||||||
|
next,memlen,stradr,strlen,moduleadr,modulelen,checksum,reserved
|
||||||
|
ENDOBJECT
|
||||||
|
|
||||||
|
PROC main()
|
||||||
|
DEF c:PTR TO cache, m:PTR TO mod, d, find, last:PTR TO LONG
|
||||||
|
WriteF('Emodule Cache Flush (c) 1993 $#%!\n\n')
|
||||||
|
find:=IF arg[] THEN arg ELSE '.m'
|
||||||
|
IF c:=FindPort('EmoduleCache')
|
||||||
|
IF c.lock=0
|
||||||
|
IF c.cookie=$DEADBEEF
|
||||||
|
m:=c.modlist
|
||||||
|
last:=c+38 -> dirty!
|
||||||
|
WHILE m
|
||||||
|
d:=m.next
|
||||||
|
IF InStr(m.stradr,find)<>-1
|
||||||
|
FreeMem(m,m.memlen)
|
||||||
|
^last:=d
|
||||||
|
ELSE
|
||||||
|
last:=m
|
||||||
|
ENDIF
|
||||||
|
m:=d
|
||||||
|
ENDWHILE
|
||||||
|
WriteF('done.\n')
|
||||||
|
ELSE
|
||||||
|
WriteF('Dead cookie!!!\n')
|
||||||
|
ENDIF
|
||||||
|
ELSE
|
||||||
|
WriteF('Cache currently in use!\n')
|
||||||
|
ENDIF
|
||||||
|
ELSE
|
||||||
|
WriteF('No module cache available.\n')
|
||||||
|
ENDIF
|
||||||
|
ENDPROC
|
|
@ -0,0 +1,139 @@
|
||||||
|
-> o2m.e, convert assembly (and a subset of C) .o files to E modules.
|
||||||
|
|
||||||
|
MODULE 'dos/doshunks', 'tools/file'
|
||||||
|
|
||||||
|
DEF type,codesize,codeadr,relocadr=NIL,relocsize -> filled by process()
|
||||||
|
|
||||||
|
OBJECT syminfo
|
||||||
|
next,sym,len,val
|
||||||
|
ENDOBJECT
|
||||||
|
|
||||||
|
DEF slist=NIL
|
||||||
|
|
||||||
|
PROC main() HANDLE
|
||||||
|
DEF infile[100]:STRING, outfile[100]:STRING, m, l
|
||||||
|
WriteF('o2m v0.1 by $#%! 1993\n')
|
||||||
|
StringF(infile,'\s.o',arg)
|
||||||
|
StringF(outfile,'\s.m',arg)
|
||||||
|
WriteF('Converting "\s" to "\s"\n',infile,outfile)
|
||||||
|
m,l:=readfile(infile)
|
||||||
|
process(m,l)
|
||||||
|
writeinfos()
|
||||||
|
writemodule(outfile)
|
||||||
|
EXCEPT
|
||||||
|
SELECT exception
|
||||||
|
CASE "MEM"; WriteF('No Mem!\n')
|
||||||
|
CASE "OPEN"; WriteF('Could not open file\n')
|
||||||
|
CASE "IN"; WriteF('Problems while reading\n')
|
||||||
|
CASE "FORM"; WriteF('Hunk format error!\n')
|
||||||
|
ENDSELECT
|
||||||
|
ENDPROC
|
||||||
|
|
||||||
|
PROC process(o:PTR TO LONG,len) HANDLE
|
||||||
|
DEF n,hunk,s:PTR TO syminfo,c
|
||||||
|
IF o[]++<>HUNK_UNIT THEN Raise("FORM")
|
||||||
|
o:=o[]++*4+o
|
||||||
|
IF o[]=HUNK_NAME
|
||||||
|
o++
|
||||||
|
o:=o[]++*4+o
|
||||||
|
ENDIF
|
||||||
|
type:=o[]++
|
||||||
|
IF (type<>HUNK_CODE) AND (type<>HUNK_DATA) THEN Raise("CODE")
|
||||||
|
codesize:=o[]++
|
||||||
|
codeadr:=o
|
||||||
|
o:=codesize*4+o
|
||||||
|
WHILE (hunk:=o[]++)<>HUNK_END
|
||||||
|
IF hunk=HUNK_RELOC32
|
||||||
|
IF relocadr THEN Raise("1REL")
|
||||||
|
n:=o[]++
|
||||||
|
IF o[]++ THEN Raise("1REL")
|
||||||
|
relocadr:=o
|
||||||
|
o:=n*4+o
|
||||||
|
IF o[]++ THEN Raise("1REL")
|
||||||
|
relocsize:=n
|
||||||
|
ELSEIF hunk=HUNK_EXT
|
||||||
|
WHILE Char(o)=EXT_DEF
|
||||||
|
n:=o[]++ AND $FFFFFF
|
||||||
|
s:=NewR(SIZEOF syminfo)
|
||||||
|
s.sym:=o
|
||||||
|
c:=Char(o)
|
||||||
|
IF (c>="A") AND (c<="Z") THEN PutChar(o,c+32)
|
||||||
|
s.len:=n*4
|
||||||
|
s.next:=slist
|
||||||
|
slist:=s
|
||||||
|
o:=n*4+o
|
||||||
|
s.val:=o[]++
|
||||||
|
ENDWHILE
|
||||||
|
IF o[]++ THEN Raise("DEF")
|
||||||
|
ELSE
|
||||||
|
Raise("HUNK")
|
||||||
|
ENDIF
|
||||||
|
ENDWHILE
|
||||||
|
EXCEPT
|
||||||
|
SELECT exception
|
||||||
|
CASE "HUNK"; WriteF('Only hunk_code, hunk_ext and hunk_reloc32 allowed\n')
|
||||||
|
CASE "CODE"; WriteF('hunk_code or hunk_data expected\n')
|
||||||
|
CASE "1REL"; WriteF('At most one relochunk expected\n')
|
||||||
|
CASE "DEF"; WriteF('Only XDEFs allowed, no XREFs\n')
|
||||||
|
ENDSELECT
|
||||||
|
Raise("FORM")
|
||||||
|
ENDPROC
|
||||||
|
|
||||||
|
PROC writeinfos()
|
||||||
|
DEF list:PTR TO syminfo, s[50]:STRING
|
||||||
|
list:=slist
|
||||||
|
WriteF('codesize=\d, numreloc=\d\n',codesize*4,relocsize)
|
||||||
|
WHILE list
|
||||||
|
StrCopy(s,list.sym,list.len)
|
||||||
|
WriteF('\s=$\h ',s,list.val)
|
||||||
|
list:=list.next
|
||||||
|
ENDWHILE
|
||||||
|
WriteF('\n')
|
||||||
|
ENDPROC
|
||||||
|
|
||||||
|
PROC writemodule(modname)
|
||||||
|
DEF handle,list:PTR TO syminfo, s[50]:STRING, res[50]:STRING, num, l, ll
|
||||||
|
IF (handle:=Open(modname,NEWFILE))=NIL THEN Raise("OPEN")
|
||||||
|
Write(handle,["EM","OD",5,0,1000,0,0,0,0,0,0,2,0,0,3,0,codesize]:INT,34)
|
||||||
|
Write(handle,codeadr,codesize*4)
|
||||||
|
IF relocadr
|
||||||
|
IF relocsize
|
||||||
|
Write(handle,[7,0,relocsize]:INT,6)
|
||||||
|
Write(handle,relocadr,relocsize*4)
|
||||||
|
ENDIF
|
||||||
|
ENDIF
|
||||||
|
Write(handle,[4]:INT,2)
|
||||||
|
list:=slist
|
||||||
|
WHILE list
|
||||||
|
StrCopy(s,list.sym,list.len)
|
||||||
|
num:=findargs(s,res)
|
||||||
|
l:=EstrLen(res)
|
||||||
|
ll:=l+IF Even(l) THEN 2 ELSE 1
|
||||||
|
Write(handle,[ll]:INT,2)
|
||||||
|
Write(handle,res,ll)
|
||||||
|
Write(handle,[list.val]:LONG,4)
|
||||||
|
IF type=HUNK_CODE
|
||||||
|
Write(handle,[1,num,0,0,0]:INT,10)
|
||||||
|
ELSE
|
||||||
|
Write(handle,[2]:INT,2)
|
||||||
|
ENDIF
|
||||||
|
list:=list.next
|
||||||
|
WriteF('\s/\d ',res,num)
|
||||||
|
ENDWHILE
|
||||||
|
Write(handle,[-1,0,0]:INT,6)
|
||||||
|
Close(handle)
|
||||||
|
WriteF('\n')
|
||||||
|
ENDPROC
|
||||||
|
|
||||||
|
PROC findargs(sym,res)
|
||||||
|
DEF fpos,lpos,ipos,a=0,c,s
|
||||||
|
lpos:=IF (ipos:=InStr(sym,'_i'))>1 THEN ipos ELSE EstrLen(sym)
|
||||||
|
fpos:=0
|
||||||
|
WHILE sym[fpos]="_" DO fpos++
|
||||||
|
WHILE sym[lpos-1]="_" DO lpos--
|
||||||
|
IF lpos>fpos THEN StrCopy(res,sym+fpos,lpos-fpos) ELSE StrCopy(res,sym)
|
||||||
|
IF ipos>1
|
||||||
|
s:=ipos+sym
|
||||||
|
WHILE c:=s[]++ DO IF c="i" THEN INC a
|
||||||
|
ENDIF
|
||||||
|
ENDPROC a
|
|
@ -0,0 +1,41 @@
|
||||||
|
-> ShowCache.e
|
||||||
|
|
||||||
|
MODULE 'exec/ports'
|
||||||
|
|
||||||
|
OBJECT cache
|
||||||
|
port:mp, cookie, modlist, debuginfo, lock:INT, reserved:INT, name[50]:ARRAY
|
||||||
|
ENDOBJECT
|
||||||
|
|
||||||
|
OBJECT mod
|
||||||
|
next,memlen,stradr,strlen,moduleadr,modulelen,checksum,reserved
|
||||||
|
ENDOBJECT
|
||||||
|
|
||||||
|
PROC main()
|
||||||
|
DEF c:PTR TO cache, m:PTR TO mod, total=0, x
|
||||||
|
WriteF('Emodule Cache Show (c) 1993 $#%!\n')
|
||||||
|
IF c:=FindPort('EmoduleCache')
|
||||||
|
IF c.lock=0
|
||||||
|
IF c.cookie=$DEADBEEF
|
||||||
|
m:=c.modlist
|
||||||
|
IF m
|
||||||
|
WriteF('\nsize name\n------- -----------------------------------\n')
|
||||||
|
WHILE m
|
||||||
|
WriteF('\d[7] \s\n',m.modulelen,m.stradr)
|
||||||
|
total:=total+m.modulelen+SIZEOF mod+StrLen(m.stradr)+8
|
||||||
|
m:=m.next
|
||||||
|
IF CtrlC() THEN m:=NIL
|
||||||
|
ENDWHILE
|
||||||
|
WriteF('\nTotal memory occupied by cached E-modules: \d bytes.\n',total)
|
||||||
|
ELSE
|
||||||
|
WriteF('\nEmpty cache.\n')
|
||||||
|
ENDIF
|
||||||
|
ELSE
|
||||||
|
WriteF('\nDead cookie.\n')
|
||||||
|
ENDIF
|
||||||
|
ELSE
|
||||||
|
WriteF('Cache currently in use!\n')
|
||||||
|
ENDIF
|
||||||
|
ELSE
|
||||||
|
WriteF('\nNo module cache available.\n')
|
||||||
|
ENDIF
|
||||||
|
ENDPROC
|
|
@ -0,0 +1,313 @@
|
||||||
|
/* ShowModule.e; dumps all the infos in a '.m' binary file */
|
||||||
|
|
||||||
|
ENUM JOB_DONE,JOB_CONST,JOB_OBJ,JOB_CODE,JOB_PROCS,
|
||||||
|
JOB_SYS,JOB_LIB,JOB_RELOC,JOB_GLOBS,JOB_MODINFO,JOB_DEBUG,JOB_MACROS
|
||||||
|
|
||||||
|
ENUM ER_NONE,ER_FILE,ER_MEM,ER_USAGE,ER_JOBID,
|
||||||
|
ER_BREAK,ER_FILETYPE,ER_TOONEW
|
||||||
|
|
||||||
|
CONST MODVERS=10, -> upto which version we understand
|
||||||
|
SKIPMARK=$FFFF8000
|
||||||
|
|
||||||
|
DEF flen,o:PTR TO INT,mem,handle=NIL,file[250]:STRING,thisvers=0,cmode=FALSE
|
||||||
|
|
||||||
|
PROC main() HANDLE
|
||||||
|
DEF a,b,ae
|
||||||
|
PrintF('ShowModule v1.\d (c) 1992 $#%!\n',MODVERS)
|
||||||
|
IF StrCmp(arg,'',1) OR StrCmp(arg,'?',2)
|
||||||
|
Raise(ER_USAGE)
|
||||||
|
ELSE
|
||||||
|
IF (arg[]="-") AND (arg[1]="c") AND (arg[2]=" ")
|
||||||
|
arg:=arg+3
|
||||||
|
WHILE arg[]=" " DO arg++
|
||||||
|
cmode:=TRUE
|
||||||
|
ENDIF
|
||||||
|
ae:=arg+StrLen(arg)
|
||||||
|
WHILE (ae[-1]=" ") AND (ae>arg) DO ae[]--:=0
|
||||||
|
StrCopy(file,arg)
|
||||||
|
LowerStr(file)
|
||||||
|
IF (file[EstrLen(file)-2]<>".") OR (file[EstrLen(file)-1]<>"m") THEN StrAdd(file,'.m')
|
||||||
|
PrintF('now showing: "\s"\n',file)
|
||||||
|
PrintF('NOTE: don\at use this output in your code, use the module instead.\n\n')
|
||||||
|
flen:=FileLength(file)
|
||||||
|
handle:=Open(file,OLDFILE)
|
||||||
|
IF (flen<8) OR (handle=NIL)
|
||||||
|
Raise(ER_FILE)
|
||||||
|
ELSE
|
||||||
|
mem:=New(flen+10)
|
||||||
|
IF mem=NIL
|
||||||
|
Raise(ER_MEM)
|
||||||
|
ELSE
|
||||||
|
IF Read(handle,mem,flen)<>flen THEN Raise(ER_FILE)
|
||||||
|
Close(handle)
|
||||||
|
a:=mem+flen
|
||||||
|
FOR b:=1 TO 6 DO a[]++:=0
|
||||||
|
handle:=NIL
|
||||||
|
process()
|
||||||
|
ENDIF
|
||||||
|
ENDIF
|
||||||
|
ENDIF
|
||||||
|
EXCEPT
|
||||||
|
IF handle THEN Close(handle)
|
||||||
|
PrintF('\n')
|
||||||
|
SELECT exception
|
||||||
|
CASE ER_FILE; PrintF('Could not read file "\s" !\n',file)
|
||||||
|
CASE ER_MEM; PrintF('No memory for loading module!\n')
|
||||||
|
CASE ER_USAGE; PrintF('USAGE: ShowModule [-c] <module>\n')
|
||||||
|
CASE ER_JOBID; PrintF('Illegal job id!\n')
|
||||||
|
CASE ER_BREAK; PrintF('User interupted ShowModule\n')
|
||||||
|
CASE ER_FILETYPE; PrintF('Not an E module file.\n')
|
||||||
|
CASE ER_TOONEW; PrintF('You need a newer version of ShowModule to view this module\n')
|
||||||
|
ENDSELECT
|
||||||
|
ENDPROC
|
||||||
|
|
||||||
|
PROC process()
|
||||||
|
DEF end,job,len,val,f,off,types:PTR TO LONG,c,r,c2,l,narg,priv,darg:PTR TO LONG
|
||||||
|
o:=mem
|
||||||
|
end:=o+flen
|
||||||
|
types:=['substructure','CHAR','INT','','LONG']
|
||||||
|
IF ^o++<>"EMOD" THEN Raise(ER_FILETYPE)
|
||||||
|
WHILE o<end
|
||||||
|
IF CtrlC() THEN Raise(ER_BREAK)
|
||||||
|
job:=o[]++
|
||||||
|
SELECT job
|
||||||
|
CASE JOB_CONST
|
||||||
|
IF thisvers>=6 THEN o:=o+4
|
||||||
|
len:=o[]++; f:=TRUE
|
||||||
|
WHILE len
|
||||||
|
val:=^o++
|
||||||
|
PrintF(IF cmode THEN '#define ' ELSE IF f THEN 'CONST ' ELSE ' ')
|
||||||
|
PrintF(IF cmode THEN '\s ' ELSE '\s=',o)
|
||||||
|
IF (val>=-$20) AND (val<$20) THEN PrintF('\d',val) ELSE PrintF(IF cmode THEN '0x\h' ELSE '$\h',val)
|
||||||
|
o:=o+len; len:=o[]++; f:=FALSE
|
||||||
|
PrintF(IF len THEN (IF cmode THEN '\n' ELSE ',\n') ELSE '\n\n')
|
||||||
|
IF CtrlC() THEN Raise(ER_BREAK)
|
||||||
|
ENDWHILE
|
||||||
|
CASE JOB_OBJ
|
||||||
|
IF thisvers>=6 THEN o:=o+4
|
||||||
|
priv:=0
|
||||||
|
l:=o[]++;
|
||||||
|
PrintF('(----) \s \s\s\n',IF cmode THEN 'struct' ELSE 'OBJECT',o+4,IF cmode THEN ' {' ELSE '')
|
||||||
|
o:=o+4+l
|
||||||
|
WHILE l:=o[]++
|
||||||
|
val:=o[]++
|
||||||
|
off:=o[]++
|
||||||
|
IF l>0
|
||||||
|
PrintF('(\d[4]) \s',off,o)
|
||||||
|
o:=o+l
|
||||||
|
priv:=0
|
||||||
|
ELSE
|
||||||
|
IF priv++=0 THEN PrintF('(----) /* private member(s) here */\n')
|
||||||
|
ENDIF
|
||||||
|
IF thisvers>=6
|
||||||
|
IF (c:=o[]++)>=0
|
||||||
|
IF c=0
|
||||||
|
PrintF(':\s\n',types[val])
|
||||||
|
ELSE
|
||||||
|
PrintF(IF val THEN '\s:PTR TO \s\n' ELSE '[\d]:ARRAY OF \s\n',IF val THEN '' ELSE Int(o+IF o[] THEN 4 ELSE 2)-off/c,ListItem(['','CHAR','INT','','LONG'],c))
|
||||||
|
ENDIF
|
||||||
|
ELSE
|
||||||
|
l:=o[]++
|
||||||
|
PrintF(IF val THEN ':PTR TO \s\n' ELSE ':\s (or ARRAY OF \s)\n',o,o)
|
||||||
|
o:=o+l
|
||||||
|
ENDIF
|
||||||
|
ELSE
|
||||||
|
PrintF(':\s\n',types[val])
|
||||||
|
ENDIF
|
||||||
|
IF CtrlC() THEN Raise(ER_BREAK)
|
||||||
|
ENDWHILE
|
||||||
|
val:=o[]++
|
||||||
|
IF thisvers>=7
|
||||||
|
IF o[]++
|
||||||
|
o:=o+4
|
||||||
|
l:=o[]++
|
||||||
|
o:=o+l+4
|
||||||
|
WHILE (c:=o[]++)<>-1
|
||||||
|
o++; l:=o[]++
|
||||||
|
PrintF(' \s(',o)
|
||||||
|
o:=o+l
|
||||||
|
IF l:=o[]++ THEN FOR off:=1 TO l DO PrintF(IF off=l THEN '\c' ELSE '\c,',off+96)
|
||||||
|
PutStr(')\n')
|
||||||
|
l:=o[]++; o:=l*4+o
|
||||||
|
ENDWHILE
|
||||||
|
WHILE o[]++<>-1 DO o:=o+4
|
||||||
|
ENDIF
|
||||||
|
ENDIF
|
||||||
|
PrintF('(----) \s /* SIZEOF=',IF cmode THEN '}' ELSE 'ENDOBJECT')
|
||||||
|
PrintF(IF val<>-1 THEN '\d */\n\n' ELSE 'NONE !!! */\n\n',val)
|
||||||
|
CASE JOB_CODE
|
||||||
|
l:=^o++*4
|
||||||
|
PrintF('/* this module contains \d bytes of code! */\n\n',l)
|
||||||
|
o:=l+o
|
||||||
|
CASE JOB_PROCS
|
||||||
|
WHILE (l:=o[]++)>0
|
||||||
|
c:=o
|
||||||
|
o:=o+l+4
|
||||||
|
IF o[]++=1
|
||||||
|
PrintF('PROC \s(',c)
|
||||||
|
narg:=o[]++
|
||||||
|
o++
|
||||||
|
c2:=o[]++
|
||||||
|
darg:=o
|
||||||
|
o:=c2*4+o
|
||||||
|
c:=o[]++
|
||||||
|
IF c
|
||||||
|
IF c2
|
||||||
|
l:=o
|
||||||
|
FOR r:=1 TO narg
|
||||||
|
WHILE l[]>"0" DO FputC(stdout,l[]++)
|
||||||
|
IF narg-r<c2 THEN PrintF('=\d',darg[]++)
|
||||||
|
PutStr(IF r<>narg THEN ',' ELSE ')\n')
|
||||||
|
l++
|
||||||
|
ENDFOR
|
||||||
|
ELSE
|
||||||
|
PrintF('\s)\n',o)
|
||||||
|
ENDIF
|
||||||
|
ELSE
|
||||||
|
IF narg THEN FOR c2:=1 TO narg DO PrintF(IF c2=narg THEN '\c' ELSE '\c,',c2+96)
|
||||||
|
PrintF(')\n')
|
||||||
|
ENDIF
|
||||||
|
o:=o+c
|
||||||
|
ELSE
|
||||||
|
PrintF('\s:\n',c)
|
||||||
|
ENDIF
|
||||||
|
ENDWHILE
|
||||||
|
PrintF('\n')
|
||||||
|
CASE JOB_SYS
|
||||||
|
o:=o+4
|
||||||
|
f:=FALSE
|
||||||
|
IF c:=o[]++
|
||||||
|
f:=TRUE
|
||||||
|
PrintF('/* osvers: \d+ ',c)
|
||||||
|
ENDIF
|
||||||
|
o:=o+4
|
||||||
|
IF c:=o[]++
|
||||||
|
IF f=FALSE THEN PrintF('/* ')
|
||||||
|
f:=TRUE
|
||||||
|
PrintF('cpu: \s+ ',ListItem(['68020/030','68040/060'],c-1))
|
||||||
|
ENDIF
|
||||||
|
IF c:=o[]++
|
||||||
|
IF f=FALSE THEN PrintF('/* ')
|
||||||
|
f:=TRUE
|
||||||
|
PrintF('fpu: \s+ ',ListItem(['68881/2','68040/060'],c-1))
|
||||||
|
ENDIF
|
||||||
|
o:=o+2
|
||||||
|
IF (thisvers:=o[]++)>MODVERS THEN Raise(ER_TOONEW)
|
||||||
|
o:=o+4
|
||||||
|
IF f THEN PrintF('*/\n\n')
|
||||||
|
CASE JOB_LIB
|
||||||
|
c:=o
|
||||||
|
WHILE c[]++ DO NOP
|
||||||
|
PrintF(IF cmode THEN '##base _\s\n##bias 30\n##public\n' ELSE 'LIBRARY \s /* informal notation */\n',c)
|
||||||
|
WHILE c[]++ DO NOP
|
||||||
|
off:=-30
|
||||||
|
WHILE (c[]<>$FF) AND (c<end)
|
||||||
|
IF c[]=16
|
||||||
|
INC c
|
||||||
|
ELSE
|
||||||
|
c2:=c
|
||||||
|
WHILE c[]++>" " DO NOP; c--
|
||||||
|
r:=c[]; c[]++:=0
|
||||||
|
PrintF(IF cmode THEN '\s' ELSE ' \s',c2)
|
||||||
|
IF cmode THEN dargs(r,c)
|
||||||
|
PrintF('(')
|
||||||
|
IF r<>16
|
||||||
|
WHILE r<16
|
||||||
|
IF r<8 THEN PrintF('D\d',r) ELSE PrintF('A\d',r-8)
|
||||||
|
r:=c[]++
|
||||||
|
IF r<16 THEN PrintF(',')
|
||||||
|
ENDWHILE
|
||||||
|
c--
|
||||||
|
ENDIF
|
||||||
|
PrintF(IF cmode THEN ')\n' ELSE ') /* \d (\h) */\n',off,Abs(off))
|
||||||
|
ENDIF
|
||||||
|
off:=off-6
|
||||||
|
ENDWHILE
|
||||||
|
PrintF(IF cmode THEN '##end\n\n' ELSE 'ENDLIBRARY\n\n')
|
||||||
|
o:=end
|
||||||
|
CASE JOB_RELOC
|
||||||
|
c:=^o++
|
||||||
|
o:=c*4+o
|
||||||
|
PrintF('/* ... and \d reloc entries */\n\n',c)
|
||||||
|
CASE JOB_DONE
|
||||||
|
o:=end
|
||||||
|
CASE JOB_GLOBS
|
||||||
|
c:=0; f:=TRUE
|
||||||
|
IF o[]=SKIPMARK THEN o:=o+6
|
||||||
|
WHILE (len:=o[]++)>=0
|
||||||
|
IF len
|
||||||
|
IF f
|
||||||
|
PrintF('DEF ')
|
||||||
|
f:=FALSE
|
||||||
|
ELSE
|
||||||
|
PrintF(',')
|
||||||
|
ENDIF
|
||||||
|
PrintF('\s',o)
|
||||||
|
o:=o+len
|
||||||
|
ELSE
|
||||||
|
c++
|
||||||
|
ENDIF
|
||||||
|
WHILE ^o++ DO IF thisvers>=10 THEN o++
|
||||||
|
ENDWHILE
|
||||||
|
IF f=FALSE THEN PrintF('\n')
|
||||||
|
IF c THEN PrintF('/* \d private global variable(s) in this module */\n',c)
|
||||||
|
PrintF('\n')
|
||||||
|
CASE JOB_MODINFO
|
||||||
|
o:=o+4
|
||||||
|
PutStr('/*\n')
|
||||||
|
WHILE len:=o[]++
|
||||||
|
PrintF(' code from module "\s" used:\n',o)
|
||||||
|
o:=o+len
|
||||||
|
WHILE c:=o[]++
|
||||||
|
len:=o[]++
|
||||||
|
c2:=o
|
||||||
|
o:=o+len
|
||||||
|
IF c=2
|
||||||
|
f:=o[]++
|
||||||
|
PrintF(IF f<>-1 THEN ' \s\c)/\d' ELSE ' \s:',c2,"(",f)
|
||||||
|
PrintF(' (\dx)\n',c:=o[]++)
|
||||||
|
o:=c*4+o
|
||||||
|
ELSE
|
||||||
|
c:=o[]++
|
||||||
|
PrintF(' OBJECT \s [\d acc]\n',c2,c)
|
||||||
|
o:=c*6+o
|
||||||
|
ENDIF
|
||||||
|
ENDWHILE
|
||||||
|
ENDWHILE
|
||||||
|
PutStr('*/\n')
|
||||||
|
CASE JOB_DEBUG
|
||||||
|
WHILE ^o++=$3F1
|
||||||
|
len:=^o++
|
||||||
|
o:=o+4
|
||||||
|
c:=^o++
|
||||||
|
o:=len*4+o-8
|
||||||
|
PrintF('/* This module contains \d bytes \s DEBUG infos! */\n\n',
|
||||||
|
len*4,IF c="EVAR" THEN 'EVAR' ELSE 'LINE')
|
||||||
|
ENDWHILE
|
||||||
|
CASE JOB_MACROS
|
||||||
|
WHILE len:=o[]++
|
||||||
|
PrintF('#define \s',o)
|
||||||
|
o:=o+len
|
||||||
|
PrintF('/\d\n',o[]++)
|
||||||
|
o++
|
||||||
|
o:=o[]+++o
|
||||||
|
ENDWHILE
|
||||||
|
PutStr('\n')
|
||||||
|
DEFAULT
|
||||||
|
Raise(ER_JOBID)
|
||||||
|
ENDSELECT
|
||||||
|
ENDWHILE
|
||||||
|
ENDPROC
|
||||||
|
|
||||||
|
PROC dargs(r,c)
|
||||||
|
DEF ch="a"
|
||||||
|
PrintF('(')
|
||||||
|
IF r<>16
|
||||||
|
WHILE r<16
|
||||||
|
PrintF('\c',ch++)
|
||||||
|
r:=c[]++
|
||||||
|
IF r<16 THEN PrintF(',')
|
||||||
|
ENDWHILE
|
||||||
|
ENDIF
|
||||||
|
PrintF(')')
|
||||||
|
ENDPROC
|
Loading…
Reference in New Issue