mirror of
https://github.com/badvision/lawless-legends.git
synced 2024-12-27 10:29:40 +00:00
Update User Manual.md
This commit is contained in:
parent
93ef9f464d
commit
457167ff53
@ -11,14 +11,14 @@ To keep development compartmentalized and easily managed, PLASMA uses relatively
|
|||||||
To start things off, here is the standard introductory program:
|
To start things off, here is the standard introductory program:
|
||||||
|
|
||||||
```
|
```
|
||||||
import stdlib
|
import stdlib
|
||||||
predef puts
|
predef puts
|
||||||
end
|
end
|
||||||
|
|
||||||
byte hello[] = "Hello, world.\n"
|
byte hello[] = "Hello, world.\n"
|
||||||
|
|
||||||
puts(@hello)
|
puts(@hello)
|
||||||
done
|
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:
|
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:
|
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
|
### 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:
|
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
|
import stdlib
|
||||||
const reshgr1 = $0004
|
const reshgr1 = $0004
|
||||||
predef putc, puts, getc, gets, cls, gotoxy
|
predef putc, puts, getc, gets, cls, gotoxy
|
||||||
end
|
end
|
||||||
|
|
||||||
import testlib
|
import testlib
|
||||||
predef puti
|
predef puti
|
||||||
byte testdata, teststring
|
byte testdata, teststring
|
||||||
word testarray
|
word testarray
|
||||||
end
|
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.
|
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.
|
Constants help with the readability of source code where hard-coded numbers might not be very descriptive.
|
||||||
|
|
||||||
```
|
```
|
||||||
const MACHID = $BF98
|
const MACHID = $BF98
|
||||||
const speaker = $C030
|
const speaker = $C030
|
||||||
const bufflen = 2048
|
const bufflen = 2048
|
||||||
```
|
```
|
||||||
|
|
||||||
These constants can be used in expressions just like a variable name.
|
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.
|
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
|
#### 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.
|
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)
|
export def plot(x, y)
|
||||||
romcall(y, 0, x, 0, $F800)
|
romcall(y, 0, x, 0, $F800)
|
||||||
end
|
end
|
||||||
```
|
```
|
||||||
|
|
||||||
#### Module Done
|
#### Module Done
|
||||||
@ -139,9 +139,9 @@ Numbers can be represented in either decimal (base 10), or hexadecimal (base 16)
|
|||||||
### Character and String Literals
|
### 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:
|
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:
|
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
|
||||||
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:
|
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, ... >]
|
[`export`] <`byte`, `word`> [`label`] [= < number, character, string, address, ... >]
|
||||||
|
|
||||||
For example:
|
For example:
|
||||||
```
|
```
|
||||||
predef myfunc
|
predef myfunc
|
||||||
|
|
||||||
byte smallarray[4]
|
byte smallarray[4]
|
||||||
byte initbarray[] = 1, 2, 3, 4, 5, 6, 7, 8, 9, 10
|
byte initbarray[] = 1, 2, 3, 4, 5, 6, 7, 8, 9, 10
|
||||||
byte string[64] = "Initialized string"
|
byte string[64] = "Initialized string"
|
||||||
word wlabel[]
|
word wlabel[]
|
||||||
word = 1000, 2000, 3000, 4000 ; Anonymous array
|
word = 1000, 2000, 3000, 4000 ; Anonymous array
|
||||||
word funclist = @myfunc, $0000
|
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.
|
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
|
##### 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`:
|
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
|
def prarray
|
||||||
byte i
|
byte i
|
||||||
for i = 0 to 5
|
for i = 0 to 5
|
||||||
puti(myarray.[i])
|
puti(myarray.[i])
|
||||||
next
|
next
|
||||||
end
|
end
|
||||||
```
|
```
|
||||||
The override operator becomes more useful when multi-dimenstional arrays are used.
|
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)
|
#### 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.
|
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[]
|
byte myrec[]
|
||||||
word = 2
|
word = 2
|
||||||
byte name[32] = "PLASMA"
|
byte name[32] = "PLASMA"
|
||||||
|
|
||||||
puti(myrec:0) ; ID = 2
|
puti(myrec:0) ; ID = 2
|
||||||
puti(myrec.2) ; Name length = 6 (Pascal string puts length byte first)
|
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.
|
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
|
##### 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:
|
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)
|
def addvals(a, b)
|
||||||
return a + b
|
return a + b
|
||||||
end
|
end
|
||||||
def subvals(a, b)
|
def subvals(a, b)
|
||||||
return a - b
|
return a - b
|
||||||
end
|
end
|
||||||
|
|
||||||
funcptr = @addvals
|
funcptr = @addvals
|
||||||
puti(funcptr(5, 2)) ; Outputs 7
|
puti(funcptr(5, 2)) ; Outputs 7
|
||||||
funcptr = @subvals
|
funcptr = @subvals
|
||||||
puti(funcptr(5, 2)) ; Outputs 3
|
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.
|
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
|
export word myobject_class = @myinit, @mynew, @mydelete
|
||||||
; Rest of class data/code follows...
|
; Rest of class data/code follows...
|
||||||
```
|
```
|
||||||
And an external module can call into this library (class) like:
|
And an external module can call into this library (class) like:
|
||||||
```
|
```
|
||||||
import myclass
|
import myclass
|
||||||
const init = 0
|
const init = 0
|
||||||
const new = 2
|
const new = 2
|
||||||
const delete = 4
|
const delete = 4
|
||||||
word myobject_class
|
word myobject_class
|
||||||
end
|
end
|
||||||
|
|
||||||
word an_obj ; an object pointer
|
word an_obj ; an object pointer
|
||||||
|
|
||||||
myobject_class:init()
|
myobject_class:init()
|
||||||
an_obj = myobject_class:new()
|
an_obj = myobject_class:new()
|
||||||
myobject_class:delete(an_obj)
|
myobject_class:delete(an_obj)
|
||||||
```
|
```
|
||||||
|
|
||||||
## Function Definitions
|
## Function Definitions
|
||||||
@ -321,15 +323,15 @@ Assembly code in PLASMA is implemented strictly as a pass-through to the assembl
|
|||||||
#### Return Values
|
#### 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:
|
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
|
def mydef
|
||||||
; do some stuff
|
; do some stuff
|
||||||
calldef(10) ; call some other def
|
calldef(10) ; call some other def
|
||||||
end
|
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:
|
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
|
def mydef
|
||||||
; do some stuff
|
; do some stuff
|
||||||
return calldef(10) ; call some other def
|
return calldef(10) ; call some other def
|
||||||
end
|
end
|
||||||
```
|
```
|
||||||
|
Loading…
Reference in New Issue
Block a user