DCPU-Admiral is python inspired operating environment for DCPU
Latest commit 725b220 Jul 21, 2017 @orlof orlof committed on GitHub Update README.md
Permalink
Failed to load latest commit information.
code
src fixed number of params in circle Feb 24, 2017
.gitignore osx shit files to ignore list Mar 25, 2016
README.md Update README.md Jul 21, 2017

README.md


ADMIRAL - Operating Environment for DCPU-16

Admiral is an easy to use all-in-one operating environment for DCPU. It requires no toolchains and comes bundled with efficient high-level programming language. Admiral’s advanced technical solutions and elegant design, make it an ideal language for scripting and rapid application development.


yes, it's hard to write code with LEM resolution...
yes, it's hard to fit IDE, runtime and application software into 64k
...and yes, it is hard to modify reactor core routines in 374 seconds to stop meltdown!

But we have enlisted to the Verge Station, not because it is easy, but because it is hard!


Table of contents


Background

Summary
Design Philosophy
  • DCPU must provide a self sufficient environment for developing and running software
  • Capability is more important than capacity
  • Users shouldn't be bothered with details that the machine can handle
  • A bug in the user’s Admiral-code should not be allowed to lead to undefined behavior of the interpreter (except poke() and call())
  • Should there be no limit on the range of numbers, the length of strings, or the size of collections (other than the total memory available)
Implementation Principles
  • "First have fun. Then make it work. Then make it right. Then make it fast."
  • Everything written in pure assembler
  • Memory allocation targets:
    • 70% for heap
    • 5% for stack
    • 25% for admiral core (including static memory buffers)
  • Memory is conserved by using direct one-pass interpreter
    • It is slow, but can boosted with AST compiler - it is a memory-speed trade-off
  • Pratt’s algorithm for efficient expression parsing
  • Mark and sweep garbage collector for memory conservation and detecting trash even with reference loops
  • Floppy load/save uses object graph serialization e.g.
    • save("big.obj", big_obj)
    • big_obj = load("big.obj")
  • Limitations set for performance reasons
    • Supports only single threaded execution - no multithreading
    • No support for virtual memory
    • No support for LEM colors
Examples

Classic Hello World in Admiral.

>print 'Hello World'
Hello World

..or as a function call:

>hello="print 'Hello World'"  # assign function string to 'hello'
>hello()                      # call 'hello' as function
Hello World                   # output

In Admiral - ANY and ALL strings can be called as functions! You could even write:

>'print greet'(greet='Hello World') # crazy way to call constant string as function
Hello World                         # function output

Here is another example of Admiral code. A function that calculates square Root for integers and floats:

>sqrt=edit()                  # start integrated editor
p=0                           # define variable
x=argv[0]                     # assign first unnamed function argument to x
while not x==p:               # loop to calculate sqrt
 p=x
 x=(x**2+argv[0])/(2*x)
return x                      # return value
>print sqrt(81.0)             # function call with float argument
9.00000000
>print sqrt(81)               # function call with integer argument
9

If you want to save that sqrt function to floppy disc, no problem.

Just format an empty disc:

>format()

and save your object graph (this time only a single string) with a filename:

>save("sqrt", sqrt)

In some other day, to restore the function from floppy to memory just load it:

>sqrt=load("sqrt")
>print sqrt(81)
9

If single function is not enough for you (usually it isn't) just use dictionary to hold all your functions and data. You can save and load dictionaries just as easily as a simple string.

Functions inside a dictionary can even access all the fields of their host dictionary with "me" -reference e.g. "me.do_it()"

Lets have a look to another example: variable swap

>a=1
>b=2
>a,b=b,a
>print a,b
2 1

...and dictionary printing

>d={'a':1,'b':2}
>for k,v in d:print k, v
a 1
b 2

Development status
Current features
  • Pure interpreted language
    • Python inspired grammar
    • Garbage collection
    • Dynamic typing
    • Prototype based inheritance
    • Exception mechanism (try-raise-except)
    • Data types
      • Variable length integers (only limited by available heap space)
      • Floats (float48b) - str<->float conversions functions loose precision (TODO)
      • Booleans, strings, lists, tuples and dicts
  • Integrated code editor with gap buffer
  • System clipboard
  • Object serialization for floppy
  • Dict implementation with binary search
  • Nice starting set of built-in functions
  • Interactive command prompt with line editing
  • Functions: poke(), peek() and call() for low level memory access
  • Functions: hwn(), hwq() and hwi() for low level hardware access
  • Functions: HIC select, status, read and transmit functions for TechCompliant HIC hardware
  • Experimental hi-res 128x96 ;) graphics mode with PIXIE: plot(), point(), line() and circle()
    • Bresenham's line algorithm
    • Midpoint circle algorithm optimized for integer-based math
  • Supported hardware
    • LEM 1802 or PIXIE - required
    • Generic Keyboard - required
    • Generic Timer
    • M35FD
    • KaiComm HIC
    • KaiComm RCI
    • Speaker
Next in development
  • Github issue tracker contains a list of development items
  • Support for other TechCompliant hardware

Special Thanks

Following sources were proven to be invaluable sources for information:

The following subroutines are contributed or copied from public sources to Admiral:

  • Hummingbird-2 Encryption Algorithm by HyperVerse Systems / ColErr
  • "Linear feedback shift register" pseudo random number generator by kirinyaga (via 0x10c - Forum)

Other in-game DCPU projects

There are dozens of advanced compilers that compile DCPU assembler from multitude of different source languages e.g. java, c, c++, ...

However, these compilers run IRL and emit outside processing power to in-game universe :) That is agains the Admiral´s philosophy and I present here only the projects that interpret or compile some higher level language inside DCPU.


Getting started

Download

The latest version of Admiral release is available from github:

https://github.com/orlof/dcpu-admiral/releases

The admiral.bin is the precompiled binary file that should run in emulators.

However, if you only want a quick peek at the Admiral, the easies way to try it is with Admiral Emulator:

https://github.com/orlof/admiral-emu/releases

Note that Admiral Emulator contains old version of Admiral and is not maintained at the moment.


Development

All source code is available in Github:

https://github.com/orlof/dcpu-admiral

Since the collapse of 0x10c, most of the DCPU development tools have been abandoned. Admiral used to work with all the major tools e.g. DevCPU, DCPU toolchain, Organic & Lettuce, F1DE...

Today I use my self-made assembler and debugger:

Those are pretty sophisticated and field proven tools, but will lack proper releases and packaging until someone other than me starts using them ;)


Usage

Interactive Shell

When Admiral starts, it will show the Admiral title header in LEM screen. Header shows Admiral's version number and the amount of available heap memory. After the header Admiral shows an interactive prompt '>' and waits for user input.

  ***  DCPU ADMIRAL  0.96  ***
64K RAM SYSTEM  45657 WORDS FREE
>

Now you can already execute one line statements in Admiral's interactive shell.

For example:

>print "Hello World"
Hello World
>for a in range(5): print a
0
1
2
3
4
># This is a comment
>print 2+2 # code and a comment in the same line
4

The Admiral interpreter acts as a simple calculator: you can type 'print' and an expression at it and it will write the value. Expression syntax is straightforward: the operators +, -, * and / work just like in most other languages (for example, Pascal or C); parentheses can be used for grouping.

>print 2+2
4
>print 1+2**32
4294967297
>print (50-5*6)/4
5

If you accidenttaly enter e.g. infinite loop, execution can be stopped by pressing ALT+c (only available in generation 2 keyboards).

The equal sign ('=') is used to assign a value to a variable.

>width=20
>height=5*9
>print width*height
900

Admiral assignment is also an expression that returns the assigned value. It allows the assignment chaining in a single statement, such as

>x = y = z = 0  # Zero x, y and z
>print x,y,z
0 0 0

Variables must be “defined” (assigned a value) before they can be used, or an error will occur:

>print n  # try to access an undefined variable
ERROR:21AD
print n

Error codes change in every Admiral release and they are documented in admiral.txt.

Clipboard

Admiral has system clipboard that can transfer data between applications. Copying data to clipboard can be done in any Admiral software by manipulating the value of "cb"-variable in global scope.

>globals()['_cb_']="Hello World"

CTRL-y pastes clipboard to any application by writing the clipboard to key buffer.

Exception Handling

Admiral has an exception handling mechanism for user level exceptions. These exceptions have nothing to do with system level errors that always exit the currently active code and return back to interpreter with four number hexadecimal error code.

Later releases may integrate some runtime system level errors to exception handling mechanism.

The simplest way to use exceptions is with a "try-except" block:

x, y = 5, 0
try:
 if y == 0: raise "DivisionByZero"
 return x / y
except e:
 print e
 return 0

Except statement always catch all exceptions. If you want to pass-through some exceptions, you must explicitly re-raise the exception in except block.

Text Editor

Admiral has a built-in text editor to facilitate software development in deep space colonies. It is started by calling edit(). This function returns the edited text as string that should be assigned to a variable.

>result=edit()

If you need to edit an existing text, you can give a string argument for edit():

>result=edit(result)

Editor has following special key commands:

KeyFunction
CTRL-xExit editor, return edited text
CTRL-cExit editor, return original text
CTRL-kKill line to end (copy/append to clipboard)
CTRL-yRe-insert ('yank') the last text that was killed

CTRL-x means to hold down CTRL and then press x (CTRL-x).

Functions

Since Admiral is pure interpreter all strings are callable (i.e. can be used as functions):

>'print msg'(msg='Hello World')
Hello World

Function calls can have positional and keyword arguments in any order.

Example: only keyword arguments (type='Monster", size='XXXL')

get_danger_level(type='Monster", size='XXXL')

Example: only positional arguments (argv[0]='Monster' and argv[1]='XXXL')

get_danger_level('Monster', 'XXXL')

Example: mixed keyword and positional arguments (type='Monster' and argv[0]='XXXL')

get_danger_level(type='Monster', 'XXXL')

Positional arguments are automatically assigned to argv[] array from left to right order.

You can set default value for keyword argument with the following one line idiom in the beginning of the function:

if "type" not in locals(): type='Gigalosaurus'

Admiral has a set of built-in functions that are listed in following chapters. User can also define their own user functions. All functions are either global or class -functions.

E.g. len() and mem() are global built-in functions, while str.encrypt() is a built-in class functions.

Admiral programmer can write global functions and dict instance functions. Functions to other class types cannot be added. Global functions are variables that have string value and class functions can be defined for dicts by adding function with str key.

Example: global user function

>out="print argv[0]"
>out("Hello")
Hello

Example: class user function

>a={}
>a.out="print argv[0]"
>a.out("Hello")
Hello
Prototypes

Dict.create() can be used to create new dicts that are based on the prototype:

>ship={}                  # create prototype object (i.e. dict)
>ship.spd=0               # assign value to prototype
>ship.accelerate=edit()   # define function in prototype
--------------------------
me.spd+=me.acceleration  # function modifies object field
--------------------------
>shuttle=ship.create()    # create new object from prototype
>shuttle.acceleration=8   # set value in new object
>shuttle.accelerate()     # call new object's method (that is defined in prototype)
>print shuttle.spd
8                         # new objects field has changed...
>print ship.spd
0                         # and prototype's fields are intact

Dict created with dict.create() inherits its properties from prototype. Prototype dict is used to read values if property is not defined in dict itself. Prototype values are not copied at creation time, and changes in prototype values are reflected to the created dict unless it defines the same value itself. However, writing values to created dict never modifies the prototype dict.

Graphics

Admiral has 64x48 pixel monochrome graphics mode and a comprehensive set of graphics plotting commands. These commands enable you to set and get individual pixel states, and draw lines and circles without ever having to access memory locations.

To put LEM into high-resolution graphics mode, use command:

hires(true)

In this mode, all points are plotted pixel by pixel and it is not possible to write text to screen. To restore normal text mode, use command:

hires(false)

In graphics mode, you can draw to screen with drawing commands:

hires(true)
circle(32,24,20,true)
circle(24,18,5,true)
plot(24,18,true)
circle(40,18,5,true)
plot(40,18,true)
line(20,34,48,30,true)
getc()
hires(false)

Admiral does not support storing or restoring images without using poke and peek functions:

# Draw image and store it in string
hires(true)
circle(32,24,20,true)
img=peek(peek(0xfff4), 768)
hires(false)

print "Press any key"
getc()

# restore image from string
hires(true)
poke(peek(0xfff4), img)
getc()
hires(false)

NOTE During startup, Admiral initializes 0xfff4 memory location to point to start of video memory. LEM 1802 video memory buffer is 384 words and two color PIXIE graphics mode is 768 words.

This way images can also be saved to floppy with normal string serialization methods.

--

Data types

Admiral provides some built-in data types i.e. dict, list, tuple, str, int, float and boolean.

Numbers

Admiral supports two types of numbers: integers and floats.

  • unlimited length integers, and
  • floating point numbers with 16 bit exponent and 32 bit base (mantissa)
># Integer division returns the number closer to 0:
>print 7/3
2
>print 7/-3
-2

There is full support for operators with mixed types as Admiral converts integer operands to floating point numbers automatically:

>print 3 * 3.75 / 1.5
7.5
>print 7.0 / 2
3.5

Floating points support also special values of nan, inf, -inf, 0.0 and -0.0.

String

Besides numbers, Admiral can also manipulate strings, which can be expressed in several ways. They can be enclosed in single quotes or double quotes:

>'spam eggs'
>"doesn't"
>'"Yes," he said.'

repr() function generates strings in single quotes.

String literals cannot span multiple lines.

The str class can be used to handle 16-bit binary data and DCPU 7-bit text. Some str functions such as replace or split will not work with binary data. (That will be addressed in later releases)

Strings can be concatenated (glued together) with the + operator, and repeated with *:

>word = 'Help' + 'A'
>print word
HelpA
>print '*' + word*5 + '*'
*HelpAHelpAHelpAHelpAHelpA*

Strings can be subscripted (indexed); like in C, the first character of a string has subscript (index) 0. There is no separate character type; a character is simply a string of size one. Like in Python, substrings can be specified with the slice notation: two indices separated by a colon.

>print word[4]
A
>print word[0:2]
He
>print word[2:4]
lp

Slice indices have useful defaults; an omitted first index defaults to zero, an omitted second index defaults to the size of the string being sliced.

>print word[:2]    # The first two characters
He
>print word[2:]    # Everything except the first two characters
lpA

Unlike a C string, Admiral strings cannot be changed. Assigning to an indexed position in the string results in an error.

However, creating a new string with the combined content is easy:

>print 'x' + word[1:]
xelpA
>print 'Splat' + word[4]
SplatA

Here’s a useful invariant of slice operations: s[:i] + s[i:] equals s.

>print word[:2] + word[2:]
HelpA
>print word[:3] + word[3:]
HelpA

Degenerate slice indices are handled gracefully: an index that is too large is replaced by the string size, an upper bound smaller than the lower bound returns an empty string.

>print word[1:100]
elpA
>print repr(word[10:])
''
>print repr(word[2:1])
''

Indices may be negative numbers, to start counting from the right. For example:

>print word[-1]     # The last character
A
>print word[-2]     # The last-but-one character
p
>print word[-2:]    # The last two characters
pA
>print word[:-2]    # Everything except the last two characters
Hel

But note that -0 is really the same as 0, so it does not count from the right!

Out-of-range negative slice indices are truncated, but don’t try this for single-element (non-slice) indices:

The built-in function len() returns the length of a string:

>s = 'supercalifragilisticexpialidocious'
>len(s)
34

Current strings do not support escape characters or output formatting. That will be fixed to future releases.

STRING API
str.encrypt(key)

Encrypts the string with given key using hummingbird2 codec.

str.decrypt(key)

Decrypts encrypted string with given key and hummingbird2 codec.

str.lower()

Return a copy of the string with all the cased characters converted to lowercase.

str.upper()

Return a copy of the string with all the cased characters converted to uppercase.

str.find(sub[, start[, end]])

Return the lowest index in the string where substring sub is found, such that sub is contained in the slice s[start:end]. Optional arguments start and end are interpreted as in slice notation. Return -1 if sub is not found.

The find() method should be used only if you need to know the position of sub. To check if sub is a substring or not, use the in operator:

          >"mi" in "Admiral"
          True
      

str.replace(old, new)

Return a copy of the string with all occurrences of substring old replaced by new.

str.split([sep])

Return a list of the words in the string, using sep as the delimiter string. Consecutive delimiters are not grouped together and are deemed to delimit empty strings:

  <pre>
      >'1,,2'.split(',')
      ['1','','2'])
  </pre>

  The sep argument may consist of multiple characters

  <pre>
      >'1<>2<>3'.split('<>')
      ['1','2','3']).
  </pre>

  Splitting an empty string with a specified separator returns [''].
</p>
<p>
  If sep is not specified, a different splitting algorithm is applied: runs of consecutive whitespace are regarded as a single separator, and the result will contain no empty strings at the start or end if the string has leading or trailing whitespace. Consequently, splitting an empty string or a string consisting of just whitespace with a None separator returns [].

  <pre>
      >' 1  2   3  '.split()
      ['1','2','3']
  </pre>

</p>
str.endswith(suffix)

Return True if the string ends with the specified suffix, otherwise return False. suffix can also be a tuple of suffixes to look for.

str.startswith(prefix)

Return True if string starts with the prefix, otherwise return False. prefix can also be a tuple of prefixes to look for.

str.isalpha()

Return true if all characters in the string are alphabetic and there is at least one character, false otherwise.

str.isdigit()

Return true if all characters in the string are digits and there is at least one character, false otherwise.

Dictionary

Another useful data type built into Admiral is the dictionary. Dictionaries are sometimes found in other languages as “associative memories” or “associative arrays”. Unlike sequences, which are indexed by a range of numbers, dictionaries are indexed by keys, which can be strings or numbers. Tuples can not be used as keys as they can contain other mutable types.

It is best to think of a dictionary as an unordered set of key: value pairs, with the requirement that the keys are unique (within one dictionary). A pair of braces creates an empty dictionary: {}.

>d = {}

Placing a comma-separated list of key:value pairs within the braces adds initial key:value pairs to the dictionary;

>d = {1: 2, 'Hi': [5,4,3,2,1]}

this is also the way dictionaries are written on output.

>print d
{1: 2, 'Hi': [5,4,3,2,1]}

The main operations on a dictionary are storing a value with some key and extracting the value given the key. Admiral supports multiple formats to store key-value pair:

>d={}
>d[1]=3
>d.one=1
>d["two"]=2
>print d
{1: 3, 'one':1, 'two': 2}

It is also possible to delete a key:value pair with del. If you store using a key that is already in use, the old value associated with that key is forgotten. It is an error to extract a value using a non-existent key.

>del d[1]
>print d
>{'one':1, 'two': 2}
DICT API
dict.create()

Return a new dict object that has the existing dict set as prototype.

dict.me

If function is called via dict reference e.g. dict.hello() then "me" keyword can be used to access the properties of the referenced dict inside that function.

      >d={'hello': 'print me.value', 'value':10}
      >d.hello()
      10
      

List

The list is a versatile datatype which can be written as a list of comma-separated values (items) between square brackets. Important thing about a list is that items in a list need not be of the same type.

Creating a list is as simple as putting different comma-separated values between square brackets. For example −

>l1=['Sun','Alpha Centauri',2085,16.7]
>l2=[1,2,3,4,5]
>l3=["a","b","c","d"]

Similar to string indices, list indices start at 0, and lists can be sliced, concatenated and so on.

To access values in lists, use the square brackets for slicing along with the index or indices to obtain value available at that index. For example −

>print l1[0]
Sun
>print l2[1:5]
[2,3,4,5]

You can update single element of lists by giving the index on the left-hand side of the assignment operator, and you can add to elements in a list with the append() method. For example −

>l2[2]=2001
>print l[2]
2001

To remove a list element, you can use the del statement, but you must know the index of the element which you are deleting. For example −

>del l1[1];
>print l1
['Sun',2085,16.7]

Lists respond to the + and * operators much like strings; they mean concatenation and repetition here too, except that the result is a new list, not a string.

ExpressionResultsDescription
len([1, 2, 3])3Length
[1, 2, 3] + [4, 5, 6][1, 2, 3, 4, 5, 6]Concatenation
['Hi!'] * 4['Hi!', 'Hi!', 'Hi!', 'Hi!']Repetition
3 in [1, 2, 3]trueMembership
for x in [1, 2, 3]: print x1 2 3Iteration
LIST API
list.append(x)

Add an item to the end of the list.

list.insert(int, obj)

Insert an item at a given position. The first argument is the index of the element before which to insert, so a.insert(0, x) inserts at the front of the list, and a.insert(len(a), x) is equivalent to a.append(x).

Tuple

A tuple is an immutable sequence of values. Tuples are similar to lists. The differences between tuples and lists are, the tuples cannot be changed unlike lists and tuples use parentheses, whereas lists use square brackets.

Creating a tuple is as simple as putting different comma-separated values. Optionally you can put these comma-separated values between parentheses also. For example −

t1 = ('admiral', 'orlof', 2892, true)
t2 = (1, 2, 3, 4, 5)
t3 = "a", "b", "c", "d"

The empty tuple is written as two parentheses containing nothing −

empty = ()

To write a tuple containing a single value you have to include a comma, even though there is only one value −

single = (50,)

Like string indices, tuple indices start at 0, and they can be sliced, concatenated, and so on.

Boolean

Admiral uses boolean variables to evaluate conditions. The boolean values true and false are returned when an expression is compared or evaluated.

None

None is frequently used to represent the absence of a value, as when default arguments are not passed to a function.

--

Statements

Here is a complete list of all the Admiral's statements.

Simple statements

Simple statements are comprised within a single line.

pass
pass_stmt ::=  "pass"

pass is a null operation — when it is executed, nothing happens. It is useful as a placeholder when a statement is required syntactically, but no code needs to be executed, for example:

while not getchar()=='y': pass
raise
raise_stmt ::=  "raise" [expression]

The raise statement allows the programmer to force an exception to occur. For example:

>raise "IllegalArgument"

If an expression is not present after "raise", None is substituted.

return
return_stmt ::=  "return" [expression]

return may only occur in a function. If an expression is present, it is evaluated, else None is substituted. return leaves the current function call with the expression (or None) as return value.

exit
return_stmt ::=  "exit"

stop everything from executing or evaluating and return to prompt.

break
break_stmt ::=  "break"

break may only occur syntactically nested in a for or while loop. break terminates the nearest enclosing loop.

continue
continue_stmt ::=  "continue"

continue may only occur syntactically nested in a for or while loop. It continues with the next cycle of the nearest enclosing loop.

print
print_stmt ::=  "print" [expression ([","] expression)* ]

print evaluates each expression in turn and writes the resulting object to LEM screen. If an object is not a string, it is first converted to a string using the rules for string conversions. A space is written between each object separated by comma. You can also leave out the comma, but then items are written without separator.

e.g.

>print "Hello", "World"
Hello World
>print "Hello" "World"
HelloWorld
>name="Orlof"
'Orlof'
>print "My name is " name "."
My name is Orlof.

Usage of plus operator to concatenate string in print statement is not recommended as it is much slower than using comma or implicit concatenation.

>print "This", "is", "good"
This is good
>print "This" + " " + "is" + " " + "BAD!"
This is BAD!
del
del_stmt ::=  "del" target_list

Deletion removes the binding of that name from the local or global namespace. If the name is unbound, an error will be raised.

Deletion of attribute reference removes the attribute from the primary object involved

cls
cls_stmt ::=  "cls"

"cls" (for clear screen) clears the LEM1802 screen and restores cursor to top left -corner position. "cls" also works in hi-res mode.

reset
reset_stmt ::=  "reset"

Resets the Admiral interpreter and peripheral devices (as if it were turned off and then on again). This command retains the data that is stored into global scope!

run
run_stmt ::=  "run" [filename [args] ]
filename ::=  expression
args ::= expression*

Run loads the named object serialization graph from file and executes it with given arguments. If filename is omitted, "MAIN" is used. Object serialization graph can be either dict or string. String object is executed directly and in dict object execution start point is the value associated with key "main".

Compound statements

Compound statements contain other statements; they affect or control the execution of those other statements in some way. In general, compound statements span multiple lines, although in simple incarnations a whole compound statement may be contained in one line.

The if, while and for statements implement traditional control flow constructs.

Compound statements consist of one or more ‘clauses.’ A clause consists of a header and a ‘suite.’ Each clause header begins with a uniquely identifying keyword and ends with a colon. A suite is a group of statements controlled by a clause. A suite can be one simple statements on the same line as the header, following the header’s colon, or it can be one or more indented statements on subsequent lines. Only the latter form of suite can contain nested compound statements, mostly because it wouldn’t be clear to which if clause else clause would belong.

All compound statements are executed in the enclosing scope.

NOTE: To help fitting source code into LEM 32x12 screen, INDENT and DEDENT MUST always BE a SINGLE SPACE!

try except

The if statement is used for conditional execution:

try_stmt ::=  "try" ":" suite
              "except" target-list ":" suite

The try statement works as follows.

  • First, the try clause (the statement(s) between the try and except keywords) is executed.
  • If raise statement is not executed inside the try block, the except clause is skipped and execution of the try statement is finished.
  • If raise statement is executed inside the try block, the rest of the clause is skipped. Then the except clause is executed, and then execution continues after the try statement.
  • A try statement may have only one except clause. To specify handlers for different exceptions you must inspect the expect parameters to distinguish between different error conditions.

If you need to determine whether an exception was raised but don’t intend to handle it, you can re-raise the exception:

try:
 print "Helo World"
 raise "SyntaxError"
 print "Not executed"
except e:
 print e
 raise e
print "Not executed"
if

The if statement is used for conditional execution:

if_stmt ::=  "if" expression ":" suite
             ( "elif" expression ":" suite )*
             ["else" ":" suite]

It selects exactly one of the suites by evaluating the expressions one by one until one is found to be true (see section Boolean operations for the definition of true and false); then that suite is executed (and no other part of the if statement is executed or evaluated). If all expressions are false, the suite of the else clause, if present, is executed.

while

The while statement is used for repeated execution as long as an expression is true:

while_stmt ::=  "while" expression ":" suite

This repeatedly tests the expression and, if it is true, executes the suite; if the expression is false (which may be the first time it is tested) the loop terminates.

A break statement executed in the suite terminates the loop. A continue statement executed in the suite skips the rest of the suite and goes back to testing the expression.

for

The for statement is used to iterate over the elements of a string, tuple, list or dict.

for_stmt ::=  "for" target_list "in" expression_list ":" suite

The expression list is evaluated once. The suite is then executed once for each item provided by the expression list in the order of ascending indices. Each item in turn is assigned to the target list using the standard rules for assignments, and then the suite is executed. When the items are exhausted the loop terminates.

A break statement executed in the suite terminates the loop. A continue statement executed in the suite skips the rest of the suite and continues with the next item, or terminates if there was no next item.

The suite may assign to the variable(s) in the target list; this does not affect the next item assigned to it.

Hint: the built-in function range() returns a sequence of integers suitable to emulate the effect of Pascal’s

for i := a to b do;

e.g., range(3) returns the list [0, 1, 2].


Global functions

GENERIC FUNCTIONS
cmp(x, y)

Compare the two objects x and y and return an integer according to the outcome. The return value is negative if x < y, zero if x == y and strictly positive if x > y.

  • Numbers are compared arithmetically
  • Strings are compared lexicographically using the numeric equivalents (the result of the built-in function ord()) of their characters.
  • Tuples and lists are compared lexicographically using comparison of corresponding elements. This means that to compare equal, each element must compare equal and the two sequences must be of the same type and have the same length.
  • If not equal, the sequences are ordered the same as their first differing elements. For example, cmp([1,2,x], [1,2,y]) returns the same as cmp(x,y). If the corresponding element does not exist, the shorter sequence is ordered first (for example, [1,2] < [1,2,3]).
  • Other objects of built-in types compare unequal unless they are the same object; the choice whether one object is considered smaller or larger than another one is made arbitrarily but consistently within one execution of a program.

len(S)

Return the length (the number of items) of an object. The argument may be a sequence (string, tuple or list) or a mapping (dictionary).

range(end)
range(start, end[, step])

This is a versatile function to create lists containing arithmetic progressions. It is most often used in for loops. The arguments must be plain integers. If the step argument is omitted, it defaults to 1. If the start argument is omitted, it defaults to 0. The full form returns a list of plain integers [start, start + step, start + 2 * step, ...]. If step is positive, the last element is the largest start + i * step less than stop; if step is negative, the last element is the smallest start + i * step greater than stop. step must not be zero.

sort(S[, reverse])

Return a sorted version from the items in iterable. Strings and tuples are sorted by creating a new sorted iterable and lists are sorted in place. Reverse is a boolean value. If set to True, then the list elements are sorted as if each comparison were reversed.

type(obj)

Return the type of an object. The return value is an integer specifiying the type.

TypeValue
none0x0080
float0x0040
int0x0020
bool0x0010
str0x0008
tuple0x0004
list0x0002
dict0x0001
NUMERIC FUNCTIONS
abs(x)

Return the absolute value of a number. The argument may be a plain integer or a floating point number.

rnd([start[, end]])

Return the next pseudorandom number.

CHARACTER FUNCTIONS
ord(char)

Given a string of length one, return the value of the byte. For example, ord('a') returns the integer 97. This is the inverse of chr().

chr(i)

Return a string of one character whose ASCII code is the integer i. For example, chr(97) returns the string 'a'. This is the inverse of ord().

getc()

Blocks until user types a key and return the key typed as a string of one character.

key([int])

Without argument return immediately the next key typed from keyboard buffer, or 0 if the buffer is empty. If int is specified return true if the specified key is down or false otherwise. Note that ASCII keyboard supports only modifier keys to be tested separately: Shift (0x90), Control (0x91) and Alt (0x92).

USER INPUT / OUTPUT FUNCTIONS
input([str])

If str argument is present, it is written to standard output without a trailing newline. The function then reads a line from input, converts it to a string (stripping a trailing newline), and returns that.

edit([str])

Opens interactive text editor. If the input argument is present, editor is initialized with input string. Editor can be used to modify the contents. Editing can be canceled by typing (press and release) CTRL followed by typing 'c', or confirmed by typing CTRL and 'x'. The function then converts editor contents (confirm) or the original input string (cancel) to a string, and returns that.

repr(obj)

Return a string containing a printable representation of an object. This is similar to str funtion, but surrounds string type values in quotes.

hex(int)

Convert an integer number (of any size) to a lowercase hexadecimal string prefixed with “0x”.

wget(int x, int y)

Return character (string of length 1) containing the character at screen coordinates x, y.

wset(int x, int y, char c)

Draw character (string of length 1) to screen coordinates x, y.

cursor(int x, int y)

Move cursor to screen coordinates x, y. Next print statement will start from the cursor coordinates.

scroll(int dx, int dy)

Scroll screen dx, dy characters. Areas that scroll in are filled with zero and appear empty on screen.

sound(int channel, int frequency[, int duration])

Create a sound with DCPU speaker hardware. Channel number is 0 or 1. Frequence 0 silences the sound. Duration is in milliseconds.

MEMORY FUNCTIONS
id(x)

Return the “identity” of an object. This is an integer which is guaranteed to be unique and constant for this object during its lifetime. Two objects with non-overlapping lifetimes may have the same id() value.

mem()

Runs the garbage collector and returns the amount of free heap space in words.

  Calling the gc method makes Admiral expend effort to recycling unused objects in order to make the memory they currently occupy available for quick reuse. When control returns from the method call, the Admiral has made a best effort to reclaim space from all discarded objects.
</p>
locals()

Return a dictionary representing the current local symbol table.

globals()

Return a dictionary representing the current global symbol table.

TYPE CONVERSION FUNCTIONS
bool(x)

Convert a value to a Boolean, using the standard truth testing procedure.

The following values are interpreted as false: false, numeric zero of all types, string 'false' and empty strings and containers (including tuples, lists and dictionaries). All other values are interpreted as true.

int(x)

Convert a number or string x to an integer. If x is a number, it can be a boolean, a plain integer, or a floating point number. If x is floating point, the conversion truncates towards zero.

float(x)

Convert a string or a number to floating point. If the argument is a string, it must contain a possibly signed decimal or floating point number. The argument may also be [+|-]nan or [+|-]inf. Otherwise, the argument may be a plain integer or a floating point number, and a floating point number with the same value is returned.

str(x)

Return a string containing an object representation of obj. For strings, this returns the string itself.

FLOPPY FUNCTIONS
format()

Format is used to initialize a DCPU M35FD floppy for use. It erases all information off the floppy.

dir()

The dir command returns a dictionary containing the available files in DCPU M35FD floppy.

load(str)

The load command returns the object stored in DCPU M35FD floppy with filename str.

save(str, obj)

The save command serializes the defined obj with filename str to DCPU M35FD floppy.

rm(str)

Removes serialized object with filename str from DCPU M35FD floppy and frees the reserved disk space.

HIC FUNCTIONS

HIC is a bi-directional multipurpose data port. Transmissions in either direction are independent of each other and can operate asynchronously of one another.

int hsel()
bool hsel(int port)

Returns the lowest port which has data available or -1 if no data is available. If port number is specified hsel returns true if data is available in the port, false otherwise.

str hinfo(int port)

Returns name for given port or None if port is invalid.

int hrecv(int port)

Returns an integer containing a word of data received from given port or none if no data is available.

void hsend(int port, int data)

Transmits a word of data to specified port.

RCI FUNCTIONS

RCI is a half-duplex datagram-based radiofrequency communications device.

rconf(channel, power)

Configure radio. Channel must be an integer between 0 and 255, and transmit power must be an integer between 0 and 7. Returns true if the new settings are accepted and applied, and false if the proposed settings are invalid.

rinfo()

Returns information about RCI status. Return value is a tuple (channel, power, buffer, antenna, device):

ValueTypeDescription
channelintThe current channel, from 0x0000 to 0x00FF
powerintThe current transmit power, from 0x0000 to 0x0007
bufferboolReceive buffer status:
True=buffer has a datagram
False=buffer is empty
antennaboolAntenna status:
True=antenna operational
False=antenna failure
deviceboolDevice status:
True=device operational
False=device failure

rrecv()

Returns the contents of the receive buffer as a string, or none if receive buffer is empty.

rsend(str)

Transmits the contents of the string as RCI datagram. Returns true if datagram is successfully queued for transmission, or false if there is already a datagram being transmitted.

HARDWARE FUNCTIONS
call(addr)

Hands the CPU over to a the machine language subroutine at a specific address. Typical values for addr are the start of the floppy drive buffer and start of the stack memory. Floppy drive buffer provides 512 words of space that is used only when Admiral executes floppy commands. Floppy commands will overwrite the buffer area completely. Floppy drive buffer address is stored in memory location 0xfffe and can be read with peek(0xfffe).

Given address should be in the range 0 thru 65535, or 0x0000 thru 0xFFFF. If the given address is outside these limits, Admiral will use LSW as address.

Parameters can be passed between Admiral and subroutine via registers. Before calling the specified address Admiral “loads” a, b, c, x, y, z, i and j registers with the words stored at storage addresses (see table below).

If or when the routine at the specified address returns control to Admiral (via an RTS instruction), Admiral immediately saves the contents of the registers back into the storage addresses memory range: This can be used to transfer results from the machine language routine to Admiral for further processing.

  Storage addresses:
  <pre>
  a: [0xfffa]
  b: [0xfffb]
  c: [0xfffc]
  x: [0xfffd]
  y: [0xfffe]
  z: [0xffff]
  i: [0xfff8]
  j: [0xfff9]
  </pre>

  Subroutine can pollute registers a-j, but must restore stack and return with rts (set pc, pop).
</p>
peek(addr[, len])

Returns the memory contents of the specified address, which must be in the range 0x0000 through 0xffff. The int value returned will be in the range from 0x0000 thru 0xffff. If the address given exceeds the limits of the memory map, Admiral will use the LSW of the address.

The second form with 'length' argument returns a string that contains 'length' words copied from the memory area that starts from the given address.

poke(addr, val)

Changes the content of the memory. New value can be specified either as an integer or string. Integer form stores LSW to given addr and str form copies the str starting from the given addr.

Caution: A misplaced POKE may cause the DCPU to lock up, or garble or delete the program currently in memory. To restore a locked-up DCPU one has to reboot the DCPU, thereby losing any program or data in RAM!

hwn()

Returns the number of connected hardware devices.

hwq(int)

Returns tuple containing three integers (hardware_id, hardware_version, manufacturer).

      for n in range(hwn()):
       hw=hwq(n)
       print hex(hw[0]), hex(hw[1]), hex(hw[2])
      

hwi(int)

Sends the interrupt to hardware n.

Parameters can be passed between Admiral and interrupt via registers. Before interrupt Admiral “loads” a, b, c, x, y, z, i and j registers with the words stored at storage addresses (see call()).

If or when the interrupt returns control, Admiral immediately saves the contents of the registers back into the storage addresses memory range: This can be used to transfer results from the interrupt to Admiral for further processing.

read(int)

Reads a single sector from floppy and stores it to floppy buffer "start = peek(0xfff7)". This method is provided for integrating with non-Admiral floppy formats.

write(int)

Writes a single sector to floppy from floppy buffer "start=peek(0xfff7)". This method is provided for integrating with non-Admiral floppy formats.

time()

Returns a tuple containing calendar time in format (year, month, day, hour, minute, seconds, ms). Only available if Generic Clock is replaced with v2 Generic Timer.

sleep(int)

Wait for specified amount of milliseconds. Only lowest 16 bits are used to define the wait time - thus giving maximum value of 0xffff.

GRAPHICS FUNCTIONS
hires(boolean enable)

Initializes a high-resolution graphics mode or returns to normal text mode, and clears the screen.

plot(int x, int y, int mode)

Depending on the mode parameter the pixel in coordinates x, y is either:

      0: Clear - Pixel is set to color 0
      1: Set   - Pixel is set to color 1
     -1: Xor   - Pixel color is changed
      

point(int x, int y)

Returns true if pixel in given coordinates is set, otherwise returns false.

line(int x0, int y0, int x1, int y1, int mode)

Draws a line with specified drawing mode (see plot for available modes).

circle(int x, int y, int radius, int mode)

Draws a circle centered to x, y and given radius with specified drawing mode (see plot for available modes).

Appendixes

EXPRESSION PRECEDENCE TABLE
OPERATORDESCRIPTIONASSOCIATIVITY
=, +=, -=, *=, /=, %=, **=, >>=, <<=, &=, ^=, %=Assignment, augmented assignmentsRight
,CommaLeft
orBoolean ORLeft
andBoolean ANDLeft
not xBoolean NOT (unary)-
in, not inMembership testLeft
is, is notIdentity testsLeft
<, <=, >, >=, !=, ==ComparisonsLeft
|Bitwise ORLeft
^Bitwise XORLeft
&Bitwise ANDLeft
<<, >>ShiftsLeft
+, -Addition and subtractionLeft
*, /, %Multiplication, division, remainderLeft
+x, -xPositive, negative (unary)-
~xBitwise NOT-
**ExponentiationRight
x[index]SubscriptionLeft
x[start:end]SlicingLeft
x(arguments...)CallLeft
x.attributeReferenceLeft
(expression...)Binding or tuple display (unary)-
[expressions...]List display (unary)-
{key:datum...}Dictionary display (unary)-

NOTES

  • Admiral provides optimized integer division algorithm. Based on the divident and divisor size it select one of the three different division strategies
    • 16b / 16b -> DCPU hardware supported DIV operation
    • n bit / 8 bit -> Optimized proprietary division algorithm that divides with DIV in 8 bit chunks
    • n bit / n bit -> Standard long division is used
  • Assignment is an expression, not statement
    • yelds the assigned value
    • e.g. "a = (b += 1)" is a valid command
  • Assignment right side is alway evaluated before left side
    • e.g. "for n in range(3): a += b += 1"
      • round 1: a=1, b=1
      • round 2: a=3, b=2
      • round 3: a=6, b=3
  • Slicing is not supported as assignment left side
    • e.g. "a[1:2] = 1,2" is NOT working!
  • Both sides of boolean operators are always evaluated
    • e.g. "if true or (a+=1):" will increment a with every evaluation
  • INDENT and DEDENT must be exactly one space

ADMIRAL MEMORY MAP

Admiral stores values of important memory addresses in defined memory locations:

LocationAddress ofRD/RWDescription
0xffffRegister Z*/*call() and hwi() use these memory locations to load registers before execution and store register values after execution.
0xfffeRegister Y*/*
0xfffdRegister X*/*
0xfffcRegister C*/*
0xfffbRegister B*/*
0xfffaRegister A*/*
0xfff9Register J*/*
0xfff8Register I*/*
0xfff7Stack memory start*/-Admiral startup sequence initializes this memory location with the address of the lowest address available for stack. (Changing the value has no effect)
0xfff6Heap memory start*/-Admiral startup sequence initializes this memory location with the address of the lowest address used by heap. Admiral utilizes whole heap space, thus heap area must not be used for any other purposes. (Changing the value has no effect)
0xfff5Floppy buffer start*/-Admiral startup sequence initializes this memory location with the floppy buffer start address. (Changing the value has no effect)
0xfff4Video memory start*/-Admiral startup sequence initializes this memory location with the video memory start address. (Changing the value has no effect)

The exact location and size of each segment depends on the Admiral build.

SOME EXTRA BITS

ASSIGNMENTS

>a = b = 0
>a += b += 1
1
>a += b += 1
3

Currently Admiral does not support assigning to slices: i.e. a[1:3]=(1,2,3) is not working. If that REALLY is a language feature that anyone would use, I will consider adding it :-)

UNKNOWN IDENTS IN FUNCTIONS

Currently Admiral does not produce error if unknown variable name is present in function body, but it is not evaluated. E.g.

>f=edit()
'print "Hello"
foobar
print "The End"

foobar would not yield error, as it is not used for anything. However,

>f=edit()
'print "Hello"
foobar+1
print "The End"

will yield error, as unknown IDENT (foobar) cannot be evaluated for addition operator.

PYTHON FEATURES MISSING (INCOMPLETE LIST)

  • 'def' function definitions
    • use strings as functions instead
  • 'class' class definitions
    • use prototype objects instead
  • 'lambda' functions
    • only one stack, please
  • generators
    • this would be useful, but would grow the interpreter size while only optimizing some special cases
  • list comprehension e.g. [x**2 for x in range(10)]
    • these would be reasonable, but code size just adds up...
  • args and kwargs
    • makes no sense as functions are not declared
  • % string operator
    • I would love this, but just too much code
  • 'yield'
    • only one stack, please
  • 'try' - 'except' exception handling
    • Admiral has this, but only for exceptions explicitly raised by user code
  • lot of built-in functions