Update User Manual.md

This commit is contained in:
David Schmenk 2014-05-22 20:42:20 -07:00
parent 93ef9f464d
commit 457167ff53

View File

@ -11,14 +11,14 @@ To keep development compartmentalized and easily managed, PLASMA uses relatively
To start things off, here is the standard introductory program:
```
import stdlib
predef puts
end
import stdlib
predef puts
end
byte hello[] = "Hello, world.\n"
byte hello[] = "Hello, world.\n"
puts(@hello)
done
puts(@hello)
done
```
Three tools are required to build and run this program: **plasm**, **acme**, and **plvm**. The PLASMA compiler, **plasm**, will convert the PLASMA source code (usually with an extension of .pla) into an assembly language source file. **acme**, the portable 6502 assembler, will convert the assembly source into a binary ready for loading. To execute the module, the PLASMA portable VM, **plvm**, can load and interpret the bytecode. The same binary can be loaded onto the target platform and run there with the appropriate VM. On Linux/Unix from lawless-legends/PLASMA/src, the steps would be entered as:
@ -49,7 +49,7 @@ for the make program to automate this.
Comments are allowed throughout a PLASMA source file. The format follows that of an assembler: they begin with a `;` and comment out the rest of the line:
```
; This is a comment, the rest of this line is ignored
; This is a comment, the rest of this line is ignored
```
### Declarations
@ -59,16 +59,16 @@ The beginning of the source file is the best place for certain declarations. Thi
Module dependencies will direct the loader to make sure these modules are loaded first, thus resolving any outstanding references. A module dependency is declared with the `import` statement block with predefined function and data definitions. The `import` block is completed with an `end`. An example:
```
import stdlib
const reshgr1 = $0004
predef putc, puts, getc, gets, cls, gotoxy
end
import stdlib
const reshgr1 = $0004
predef putc, puts, getc, gets, cls, gotoxy
end
import testlib
predef puti
byte testdata, teststring
word testarray
end
import testlib
predef puti
byte testdata, teststring
word testarray
end
```
The `predef` pre-defines functions that can be called throughout the module. The data declarations, `byte` and `word` will refer to data in those modules. `const` can appear in an `import` block, although not required. It does keep values associated with the imported module in a well-contained block for readability and useful with pre-processor file inclusion. Case is not significant for either the module name nor the pre-defined function/data labels. They are all converted to uppercase with 16 characters significant when the loader resolves them.
@ -77,9 +77,9 @@ The `predef` pre-defines functions that can be called throughout the module. Th
Constants help with the readability of source code where hard-coded numbers might not be very descriptive.
```
const MACHID = $BF98
const speaker = $C030
const bufflen = 2048
const MACHID = $BF98
const speaker = $C030
const bufflen = 2048
```
These constants can be used in expressions just like a variable name.
@ -88,7 +88,7 @@ These constants can be used in expressions just like a variable name.
Sometimes a function needs to be referenced before it is defined. The `predef` declaration reserves the label for a function. The `import` declaration block also uses the `predef` declaration to reserve an external function. Outside of an `import` block, `predef` will only predefine a function that must be declared later in the source file, otherwise an error will occur.
```
predef exec_file, mydef
predef exec_file, mydef
```
#### Global Data & Variable Declarations
@ -107,9 +107,9 @@ After all the function definitions are complete, an optional module initiializat
Data and function labels can be exported so other modules may access this modules data and code. By prepending `export` to the data or functions declaration, the label will become available to the loader for inter-module resolution.
```
export def plot(x, y)
romcall(y, 0, x, 0, $F800)
end
export def plot(x, y)
romcall(y, 0, x, 0, $F800)
end
```
#### Module Done
@ -139,9 +139,9 @@ Numbers can be represented in either decimal (base 10), or hexadecimal (base 16)
### Character and String Literals
A character literal, represented by a single character or an escaped character enclosed in single quotes `'`, can be used wherever a number is used. String literals, a character sequence enclosed in double quotes `"`, can only appear in a data definition. A length byte will be calculated and prepended to the character data. This is the Pascal style of string definition used throughout PLASMA and ProDOS. When referencing the string, it's address is used:
```
char mystring[] = "This is my string; I am very proud of \n"
char mystring[] = "This is my string; I am very proud of \n"
puts(@mystring)
puts(@mystring)
```
Excaped characters, like the `\n` above are replaces with the Carriage Return character. The list of escaped characters is:
@ -164,31 +164,33 @@ Words can represent many things in PLASMA, including addresses. PLASMA uses a 16
#### Arrays
Arrays are the most useful data structure in PLASMA. Using an index into a list of values is indispensible. PLASMA has a flexible array operator. Arrays can be defined in many ways, usually as:
[`export`] <`byte`, `word`> [`label`] [= < number, character, string, address, ... >]
For example:
```
predef myfunc
predef myfunc
byte smallarray[4]
byte initbarray[] = 1, 2, 3, 4, 5, 6, 7, 8, 9, 10
byte string[64] = "Initialized string"
word wlabel[]
word = 1000, 2000, 3000, 4000 ; Anonymous array
word funclist = @myfunc, $0000
byte smallarray[4]
byte initbarray[] = 1, 2, 3, 4, 5, 6, 7, 8, 9, 10
byte string[64] = "Initialized string"
word wlabel[]
word = 1000, 2000, 3000, 4000 ; Anonymous array
word funclist = @myfunc, $0000
```
Arrays can be uninitialized and reserve a size, as in `smallarray` above. Initilized arrays without a size specifier in the definition will take up as much data as is present, as in `initbarray` above. Strings are special arrays that include a hidden length byte in the beginning (Pascal strings). When specified with a size, a minimum size is reserved for the string value. Labels can be defined as arrays without size or initializers; this can be useful when overlapping labels with other arrays or defining the actual array data as anonymous arrays in following lines as in `wlabel` and following lines. Addresses of other data (must be defined previously) or function definitions (pre-defined with predef), including imported references, can be initializers.
##### Type Overrides
Arrays are usually identified by the data type specifier, `byte` or `word` when the array is defined. However, this can be overridden with the type override specifiers: `:` and `.`. `:` overrides the type to be `word`, `.` overrides the type to be `byte`. An example of accessing a `word` array as `bytes`:
```
word myarray[] = $AABB, $CCDD, $EEFF
word myarray[] = $AABB, $CCDD, $EEFF
def prarray
byte i
for i = 0 to 5
puti(myarray.[i])
next
end
def prarray
byte i
for i = 0 to 5
puti(myarray.[i])
next
end
```
The override operator becomes more useful when multi-dimenstional arrays are used.
@ -230,12 +232,12 @@ Notice how xscan goes to 39 instead of 19 in the byte accessed version.
#### Offsets (Structure Elements)
Structures are another fundamental construct when accessing in-common data. Using fixed element offsets from a given address means you only have to pass one address around to access the entire record. Offsets are specified with a constant expression following the type override specifier.
```
byte myrec[]
word = 2
byte name[32] = "PLASMA"
byte myrec[]
word = 2
byte name[32] = "PLASMA"
puti(myrec:0) ; ID = 2
puti(myrec.2) ; Name length = 6 (Pascal string puts length byte first)
puti(myrec:0) ; ID = 2
puti(myrec.2) ; Name length = 6 (Pascal string puts length byte first)
```
This contrived example shows how one can access offsets from a variable as either `byte`s or `word`s regardless of how they were defined. This operator becomes more powerful when combined with pointers, defined next.
@ -251,41 +253,41 @@ Along with dereferencing a pointer, there is the question of getting the address
##### Function Pointers
One very powerful combination of operations is the function pointer. This involves getting the address of a function and saving it in a `word` variable. Then, the function can be called be dereferencing the variable as a function call invocation. PLASMA is smart enough to know what you mean when your code looks like this:
```
word funcptr
word funcptr
def addvals(a, b)
return a + b
end
def subvals(a, b)
return a - b
end
def addvals(a, b)
return a + b
end
def subvals(a, b)
return a - b
end
funcptr = @addvals
puti(funcptr(5, 2)) ; Outputs 7
funcptr = @subvals
puti(funcptr(5, 2)) ; Outputs 3
funcptr = @addvals
puti(funcptr(5, 2)) ; Outputs 7
funcptr = @subvals
puti(funcptr(5, 2)) ; Outputs 3
```
These concepts can be combined with the structure offsets to create a function table that can be easily changed on the fly. Virtual functions in object oriented languages are implemented this way.
```
predef myinit, mynew, mydelete
predef myinit, mynew, mydelete
export word myobject_class = @myinit, @mynew, @mydelete
; Rest of class data/code follows...
export word myobject_class = @myinit, @mynew, @mydelete
; Rest of class data/code follows...
```
And an external module can call into this library (class) like:
```
import myclass
const init = 0
const new = 2
const delete = 4
word myobject_class
end
import myclass
const init = 0
const new = 2
const delete = 4
word myobject_class
end
word an_obj ; an object pointer
word an_obj ; an object pointer
myobject_class:init()
an_obj = myobject_class:new()
myobject_class:delete(an_obj)
myobject_class:init()
an_obj = myobject_class:new()
myobject_class:delete(an_obj)
```
## Function Definitions
@ -321,15 +323,15 @@ Assembly code in PLASMA is implemented strictly as a pass-through to the assembl
#### Return Values
PLASMA always returns a value from a function, even if you don't supply one. Probably the easiest optimization to make in PLASMA is to cascade a return value if you don't care about the value you return. This only works if the last thing you do before returning from your routine is calling another definition. You would go from:
```
def mydef
; do some stuff
calldef(10) ; call some other def
end
def mydef
; do some stuff
calldef(10) ; call some other def
end
```
PLASMA will effectively add a RETURN 0 to the end of your function, as well as add code to ignore the result of `calldef(10)`. As long as you don't care about the return value from `mydef`, you can save some code bytes with:
```
def mydef
; do some stuff
return calldef(10) ; call some other def
end
def mydef
; do some stuff
return calldef(10) ; call some other def
end
```