2019-11-13 15:28:16 -05:00
|
|
|
# basic
|
|
|
|
|
|
|
|
**Work in progress, not finished.**
|
|
|
|
|
|
|
|
This is a BASIC interpreter which is being written from scratch for Collapse OS.
|
|
|
|
There are many existing z80 implementations around, some of them open source
|
|
|
|
and most of them good and efficient, but because a lot of that code overlaps
|
|
|
|
with code that has already been written for zasm, I believe that it's better to
|
|
|
|
reuse those bits of code.
|
|
|
|
|
|
|
|
Integrating an existing BASIC to Collapse OS seemed a bigger challenge than
|
|
|
|
writing from scratch, so here I am, writing from scratch again...
|
|
|
|
|
2019-11-19 15:14:04 -05:00
|
|
|
## Design goal
|
|
|
|
|
|
|
|
The reason for including a BASIC dialect in Collapse OS is to supply some form
|
|
|
|
of system administration swiss knife. zasm, ed and the shell can do
|
|
|
|
theoretically anything, but some tasks (which are difficult to predict) can
|
|
|
|
possibly be overly tedious. One can think, for example, about hardware
|
|
|
|
debugging. Poking and peeking around when not sure what we're looking for can
|
|
|
|
be a lot more effective with the help of variables, conditions and for-loops in
|
|
|
|
an interpreter.
|
|
|
|
|
|
|
|
Because the goal is not to provide a foundation for complex programs, I'm
|
|
|
|
planning on intentionally crippling this BASIC dialect for the sake of
|
|
|
|
simplicity.
|
2019-11-21 17:03:46 -05:00
|
|
|
|
2019-11-24 15:39:36 -05:00
|
|
|
The idea here is that the system administrator would build herself many little
|
|
|
|
tools in assembler and BASIC would be the interactive glue to those tools.
|
|
|
|
|
|
|
|
If you find yourself writing complex programs in Collapse OS BASIC, you're on a
|
|
|
|
wrong path. Back off, that program should be in assembler.
|
|
|
|
|
2019-11-24 10:24:15 -05:00
|
|
|
## Glueing
|
|
|
|
|
|
|
|
The `glue.asm` file in this folder represents the minimal basic system. There
|
|
|
|
are additional modules that can be added that aren't added by default, such
|
|
|
|
as `fs.asm` because they require kernel options that might not be available.
|
|
|
|
|
|
|
|
To include these modules, you'll need to write your own glue file and to hook
|
|
|
|
extra commands through `BAS_FINDHOOK`. Look for examples in `tools/emul` and
|
|
|
|
in recipes.
|
|
|
|
|
2019-11-21 17:03:46 -05:00
|
|
|
## Usage
|
|
|
|
|
|
|
|
Upon launch, a prompt is presented, waiting for a command. There are two types
|
|
|
|
of command invocation: direct and numbered.
|
|
|
|
|
|
|
|
A direct command is executed immediately. Example: `print 42` will print `42`
|
|
|
|
immediately.
|
|
|
|
|
|
|
|
A numbered command is added to BASIC's code listing at the specified line
|
|
|
|
number. For example, `10 print 42` will set line 10 to the string `print 42`.
|
|
|
|
|
|
|
|
Code listing can be printed with `list` and can be ran with `run`. The listing
|
|
|
|
is kept in order of lines. Line number don't need to be sequential. You can
|
|
|
|
keep leeway in between your lines and then insert a line with a middle number
|
|
|
|
later.
|
|
|
|
|
2019-11-21 19:56:51 -05:00
|
|
|
Some commands take arguments. Those are given by typing a whitespace after the
|
|
|
|
command name and then the argument. Additional arguments are given the same way,
|
|
|
|
by typing a whitespace.
|
|
|
|
|
2019-11-21 17:03:46 -05:00
|
|
|
### Numbers, expressions and variables
|
|
|
|
|
2019-11-23 14:56:23 -05:00
|
|
|
Numbers are stored in memory as 16-bit integers (little endian) and numbers
|
|
|
|
being represented by BASIC are expressed as signed integers, in decimal form.
|
|
|
|
Line numbers, however, are expressed and treated as unsigned integers: You can,
|
|
|
|
if you want, put something on line "-1", but it will be the equivalent of line
|
|
|
|
65535. When expressing number literals, you can do so either in multiple forms.
|
|
|
|
See "Number literals" in `apps/README.md` for details.
|
2019-11-21 17:03:46 -05:00
|
|
|
|
|
|
|
Expressions are accepted wherever a number is expected. For example,
|
2019-11-22 14:01:16 -05:00
|
|
|
`print 2+3` will print `5`. See "Expressions" in `apps/README.md`.
|
2019-11-21 17:03:46 -05:00
|
|
|
|
|
|
|
Inside a `if` command, "truth" expressions are accepted (`=`, `<`, `>`, `<=`,
|
|
|
|
`>=`). A thruth expression that doesn't contain a truth operator evaluates the
|
|
|
|
number as-is: zero if false, nonzero is true.
|
|
|
|
|
|
|
|
There are 26 one-letter variables in BASIC which can be assigned a 16-bit
|
|
|
|
integer to them. You assign a value to a variable with `=`. For example,
|
|
|
|
`a=42+4` will assign 46 to `a` (case insensitive). Those variables can then
|
|
|
|
be used in expressions. For example, `print a-6` will print `40`. All variables
|
|
|
|
are initialized to zero on launch.
|
|
|
|
|
2019-11-24 14:55:50 -05:00
|
|
|
### Arguments
|
|
|
|
|
|
|
|
Some commands take arguments and there are some common patterns regarding them.
|
|
|
|
|
|
|
|
One of them is that all commands that "return" something (`input`, `peek`,
|
|
|
|
etc.) always to so in variable `A`.
|
|
|
|
|
|
|
|
Another is that whenever a number is expected, expressions, including the ones
|
|
|
|
with variables in it, work fine.
|
|
|
|
|
2019-11-21 17:03:46 -05:00
|
|
|
### Commands
|
|
|
|
|
|
|
|
There are two types of commands: normal and direct-only. The latter can only
|
|
|
|
be invoked in direct mode, not through a code listing.
|
|
|
|
|
2019-11-24 14:26:32 -05:00
|
|
|
**bye**: Direct-only. Quits BASIC
|
2019-11-21 17:03:46 -05:00
|
|
|
|
2019-11-24 14:26:32 -05:00
|
|
|
**list**: Direct-only. Prints all lines in the code listing, prefixing them
|
2019-11-21 17:03:46 -05:00
|
|
|
with their associated line number.
|
|
|
|
|
2019-11-24 14:26:32 -05:00
|
|
|
**run**: Direct-only. Runs code from the listing, starting with the first one.
|
2019-11-21 17:03:46 -05:00
|
|
|
If `goto` was previously called in direct mode, we start from that line instead.
|
|
|
|
|
2019-11-24 14:26:32 -05:00
|
|
|
**clear**: Direct-only. Clears the current code listing.
|
|
|
|
|
|
|
|
**print**: Prints the result of the specified expression, then CR/LF. Can be
|
2019-11-21 19:56:51 -05:00
|
|
|
given multiple arguments. In that case, all arguments are printed separately
|
|
|
|
with a space in between. For example, `print 12 13` prints `12 13<cr><lf>`
|
|
|
|
|
|
|
|
Unlike anywhere else, the `print` command can take a string inside a double
|
|
|
|
quote. That string will be printed as-is. For example, `print "foo" 40+2` will
|
|
|
|
print `foo 42`.
|
2019-11-21 17:03:46 -05:00
|
|
|
|
2019-11-24 14:26:32 -05:00
|
|
|
**goto**: Make the next line to be executed the line number specified as an
|
2019-11-21 17:03:46 -05:00
|
|
|
argument. Errors out if line doesn't exist. Argument can be an expression. If
|
|
|
|
invoked in direct mode, `run` must be called to actually run the line (followed
|
|
|
|
by the next, and so on).
|
|
|
|
|
2019-11-24 14:26:32 -05:00
|
|
|
**if**: If specified condition is true, execute the rest of the line. Otherwise,
|
2019-11-21 17:03:46 -05:00
|
|
|
do nothing. For example, `if 2>1 print 12` prints `12` and `if 2<1 print 12`
|
|
|
|
does nothing. The argument for this command is a "thruth expression".
|
2019-11-21 20:17:55 -05:00
|
|
|
|
2019-11-24 14:55:50 -05:00
|
|
|
**input**: Prompts the user for a numerical value and puts that value in `A`.
|
|
|
|
The prompted value is evaluated as an expression and then stored. The command
|
|
|
|
takes an optional string literal parameter. If present, that string will be
|
|
|
|
printed before asking for input. Unlike a `print` call, there is no CR/LF after
|
|
|
|
that print.
|
2019-11-23 16:07:10 -05:00
|
|
|
|
2019-11-24 14:55:50 -05:00
|
|
|
**peek/deek**: Put the value at specified memory address into `A`. peek is for
|
|
|
|
a single byte, deek is for a word (little endian). For example, `peek 42` puts
|
|
|
|
the byte value contained in memory address 0x002a into variable `A`. `deek 42`
|
|
|
|
does the same as peek, but also puts the value of 0x002b into `A`'s MSB.
|
2019-11-23 16:07:10 -05:00
|
|
|
|
|
|
|
**poke/doke**: Put the value of specified expression into specified memory
|
|
|
|
address. For example, `poke 42 0x102+0x40` puts `0x42` in memory address
|
|
|
|
0x2a (MSB is ignored) and `doke 42 0x102+0x40` does the same as poke, but also
|
|
|
|
puts `0x01` in memory address 0x2b.
|
2019-11-23 17:07:14 -05:00
|
|
|
|
2019-11-24 14:55:50 -05:00
|
|
|
**in**: Same thing as `peek`, but for a I/O port. `in 42` generates an input
|
|
|
|
I/O on port 42 and stores the byte result in `A`.
|
2019-11-23 20:35:21 -05:00
|
|
|
|
|
|
|
**out**: Same thing as `poke`, but for a I/O port. `out 42 1+2` generates an
|
|
|
|
output I/O on port 42 with value 3.
|
|
|
|
|
2019-11-23 17:07:14 -05:00
|
|
|
**sleep**: Sleep a number of "units" specified by the supplied expression. A
|
|
|
|
"unit" depends on the CPU clock speed. At 4MHz, it is roughly 8 microseconds.
|
2019-11-24 10:24:15 -05:00
|
|
|
|
2019-11-24 15:39:36 -05:00
|
|
|
**addr**: This very handy returns (in `A`), the address you query for. You can
|
|
|
|
query for two types of things: commands or special stuff.
|
|
|
|
|
|
|
|
If you query for a command, type the name of the command as an argument. The
|
|
|
|
address of the associated routine will be returned.
|
|
|
|
|
|
|
|
Then, there's the *special stuff*. This is the list of things you can query for:
|
|
|
|
|
|
|
|
* `$`: the scratchpad.
|
|
|
|
|
2019-11-24 10:24:15 -05:00
|
|
|
## Optional modules
|
|
|
|
|
|
|
|
As explained in "glueing" section abolve, this folder contains optional modules.
|
|
|
|
Here's the documentation for them.
|
|
|
|
|
2019-11-24 20:34:23 -05:00
|
|
|
### blk
|
|
|
|
|
|
|
|
Block devices commands. Block devices are configured during kernel
|
|
|
|
initialization and are referred to by numbers.
|
|
|
|
|
|
|
|
**bsel**: Select the active block device. The active block device is the target
|
|
|
|
of all commands below. You select it by specifying its number. For example,
|
|
|
|
`bsel 0` selects the first configured device. `bsel 1` selects the second.
|
|
|
|
|
|
|
|
A freshly selected blkdev begins with its "pointer" at 0.
|
|
|
|
|
|
|
|
**seek**: Moves the blkdev "pointer" to the specified offset. The first
|
|
|
|
argument is the offset's least significant half (blkdev supports 32-bit
|
|
|
|
addressing). Is is interpreted as an unsigned integer.
|
|
|
|
|
|
|
|
The second argument is optional and is the most significant half of the address.
|
|
|
|
It defaults to 0.
|
|
|
|
|
|
|
|
**getb**: Read a byte in active blkdev at current pointer, then advance the
|
|
|
|
pointer by one. Read byte goes in `A`.
|
|
|
|
|
|
|
|
**putb**: Writes a byte in active blkdev at current pointer, then advance the
|
|
|
|
pointer by one. The value of the byte is determined by the expression supplied
|
|
|
|
as an argument. Example: `putb 42`.
|
|
|
|
|
2019-11-24 10:24:15 -05:00
|
|
|
### fs
|
|
|
|
|
|
|
|
`fs.asm` provides those commands:
|
|
|
|
|
|
|
|
**fls**: prints the list of files contained in the active filesystem.
|
2019-11-24 14:26:32 -05:00
|
|
|
|
|
|
|
**ldbas**: loads the content of the file specified in the argument (as an
|
|
|
|
unquoted filename) and replace the current code listing with this contents. Any
|
|
|
|
line not starting with a number is ignored (not an error).
|