|
|
@ -1,11 +1,16 @@
|
|
|
|
# basic
|
|
|
|
# basic
|
|
|
|
|
|
|
|
|
|
|
|
This is a BASIC interpreter which has been written from scratch for Collapse OS.
|
|
|
|
**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
|
|
|
|
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
|
|
|
|
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
|
|
|
|
with code that has already been written for zasm, I believe that it's better to
|
|
|
|
reuse those bits of code.
|
|
|
|
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...
|
|
|
|
|
|
|
|
|
|
|
|
## Design goal
|
|
|
|
## Design goal
|
|
|
|
|
|
|
|
|
|
|
|
The reason for including a BASIC dialect in Collapse OS is to supply some form
|
|
|
|
The reason for including a BASIC dialect in Collapse OS is to supply some form
|
|
|
@ -93,17 +98,17 @@ with variables in it, work fine.
|
|
|
|
There are two types of commands: normal and direct-only. The latter can only
|
|
|
|
There are two types of commands: normal and direct-only. The latter can only
|
|
|
|
be invoked in direct mode, not through a code listing.
|
|
|
|
be invoked in direct mode, not through a code listing.
|
|
|
|
|
|
|
|
|
|
|
|
`bye`: Direct-only. Quits BASIC
|
|
|
|
**bye**: Direct-only. Quits BASIC
|
|
|
|
|
|
|
|
|
|
|
|
`list`: Direct-only. Prints all lines in the code listing, prefixing them
|
|
|
|
**list**: Direct-only. Prints all lines in the code listing, prefixing them
|
|
|
|
with their associated line number.
|
|
|
|
with their associated line number.
|
|
|
|
|
|
|
|
|
|
|
|
`run`: Direct-only. Runs code from the listing, starting with the first one.
|
|
|
|
**run**: Direct-only. Runs code from the listing, starting with the first one.
|
|
|
|
If `goto` was previously called in direct mode, we start from that line instead.
|
|
|
|
If `goto` was previously called in direct mode, we start from that line instead.
|
|
|
|
|
|
|
|
|
|
|
|
`clear`: Direct-only. Clears the current code listing.
|
|
|
|
**clear**: Direct-only. Clears the current code listing.
|
|
|
|
|
|
|
|
|
|
|
|
`print <what> [<what>]`: Prints the result of the specified expression,
|
|
|
|
**print <what> [<what>]**: Prints the result of the specified expression,
|
|
|
|
then CR/LF. Can be given multiple arguments. In that case, all arguments are
|
|
|
|
then CR/LF. Can be given multiple arguments. In that case, all arguments are
|
|
|
|
printed separately with a space in between. For example, `print 12 13` prints
|
|
|
|
printed separately with a space in between. For example, `print 12 13` prints
|
|
|
|
`12 13<cr><lf>`
|
|
|
|
`12 13<cr><lf>`
|
|
|
@ -112,48 +117,48 @@ 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
|
|
|
|
quote. That string will be printed as-is. For example, `print "foo" 40+2` will
|
|
|
|
print `foo 42`.
|
|
|
|
print `foo 42`.
|
|
|
|
|
|
|
|
|
|
|
|
`goto <lineno>`: Make the next line to be executed the line number
|
|
|
|
**goto <lineno>**: Make the next line to be executed the line number
|
|
|
|
specified as an argument. Errors out if line doesn't exist. Argument can be
|
|
|
|
specified as an argument. Errors out if line doesn't exist. Argument can be
|
|
|
|
an expression. If invoked in direct mode, `run` must be called to actually
|
|
|
|
an expression. If invoked in direct mode, `run` must be called to actually
|
|
|
|
run the line (followed by the next, and so on).
|
|
|
|
run the line (followed by the next, and so on).
|
|
|
|
|
|
|
|
|
|
|
|
`if <cond> <cmd>`: If specified condition is true, execute the rest of the
|
|
|
|
**if <cond> <cmd>**: If specified condition is true, execute the rest of the
|
|
|
|
line. Otherwise, do nothing. For example, `if 2>1 print 12` prints `12` and `if
|
|
|
|
line. Otherwise, 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
|
|
|
|
2<1 print 12` does nothing. The argument for this command is a "thruth
|
|
|
|
expression".
|
|
|
|
expression".
|
|
|
|
|
|
|
|
|
|
|
|
`input [<prompt>]`: Prompts the user for a numerical value and puts that
|
|
|
|
**input [<prompt>]**: Prompts the user for a numerical value and puts that
|
|
|
|
value in `A`. The prompted value is evaluated as an expression and then stored.
|
|
|
|
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
|
|
|
|
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
|
|
|
|
will be printed before asking for input. Unlike a `print` call, there is no
|
|
|
|
CR/LF after that print.
|
|
|
|
CR/LF after that print.
|
|
|
|
|
|
|
|
|
|
|
|
`peek/deek <addr>`: Put the value at specified memory address into `A`. peek is for
|
|
|
|
**peek/deek <addr>**: 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
|
|
|
|
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`
|
|
|
|
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.
|
|
|
|
does the same as peek, but also puts the value of 0x002b into `A`'s MSB.
|
|
|
|
|
|
|
|
|
|
|
|
`poke/doke <addr> <val>`: Put the value of specified expression into
|
|
|
|
**poke/doke <addr> <val>**: Put the value of specified expression into
|
|
|
|
specified memory address. For example, `poke 42 0x102+0x40` puts `0x42` in
|
|
|
|
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
|
|
|
|
memory address 0x2a (MSB is ignored) and `doke 42 0x102+0x40` does the same
|
|
|
|
as poke, but also puts `0x01` in memory address 0x2b.
|
|
|
|
as poke, but also puts `0x01` in memory address 0x2b.
|
|
|
|
|
|
|
|
|
|
|
|
`in <port>`: Same thing as `peek`, but for a I/O port. `in 42` generates an
|
|
|
|
**in <port>**: 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`.
|
|
|
|
input I/O on port 42 and stores the byte result in `A`.
|
|
|
|
|
|
|
|
|
|
|
|
`out <port> <val>`: Same thing as `poke`, but for a I/O port. `out 42 1+2`
|
|
|
|
**out <port> <val>**: 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.
|
|
|
|
generates an output I/O on port 42 with value 3.
|
|
|
|
|
|
|
|
|
|
|
|
`getc`: Waits for a single character to be typed in the console and then puts
|
|
|
|
**getc**: Waits for a single character to be typed in the console and then puts
|
|
|
|
that value in `A`.
|
|
|
|
that value in `A`.
|
|
|
|
|
|
|
|
|
|
|
|
`putc <char>`: Puts the specified character to the console.
|
|
|
|
**putc <char>**: Puts the specified character to the console.
|
|
|
|
|
|
|
|
|
|
|
|
`sleep <units>`: Sleep a number of "units" specified by the supplied
|
|
|
|
**sleep <units>**: Sleep a number of "units" specified by the supplied
|
|
|
|
expression. A "unit" depends on the CPU clock speed. At 4MHz, it is roughly 8
|
|
|
|
expression. A "unit" depends on the CPU clock speed. At 4MHz, it is roughly 8
|
|
|
|
microseconds.
|
|
|
|
microseconds.
|
|
|
|
|
|
|
|
|
|
|
|
`addr <what>`: This very handy returns (in `A`), the address you query for.
|
|
|
|
**addr <what>**: This very handy returns (in `A`), the address you query for.
|
|
|
|
You can query for two types of things: commands or special stuff.
|
|
|
|
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
|
|
|
|
If you query for a command, type the name of the command as an argument. The
|
|
|
@ -163,7 +168,7 @@ Then, there's the *special stuff*. This is the list of things you can query for:
|
|
|
|
|
|
|
|
|
|
|
|
* `$`: the scratchpad.
|
|
|
|
* `$`: the scratchpad.
|
|
|
|
|
|
|
|
|
|
|
|
`usr <addr>`: This calls the memory address specified as an expression
|
|
|
|
**usr <addr>**: This calls the memory address specified as an expression
|
|
|
|
argument. Before doing so, it sets the registers according to a specific
|
|
|
|
argument. Before doing so, it sets the registers according to a specific
|
|
|
|
logic: Variable `A`'s LSB goes in register `A`, variable `D` goes in register
|
|
|
|
logic: Variable `A`'s LSB goes in register `A`, variable `D` goes in register
|
|
|
|
`DE`, `H` in `HL` `B` in `BC` and `X` in `IX`. `IY` can't be used because
|
|
|
|
`DE`, `H` in `HL` `B` in `BC` and `X` in `IX`. `IY` can't be used because
|
|
|
@ -186,24 +191,24 @@ Here's the documentation for them.
|
|
|
|
Block devices commands. Block devices are configured during kernel
|
|
|
|
Block devices commands. Block devices are configured during kernel
|
|
|
|
initialization and are referred to by numbers.
|
|
|
|
initialization and are referred to by numbers.
|
|
|
|
|
|
|
|
|
|
|
|
`bsel <blkid>`: Select the active block device. The active block device is
|
|
|
|
**bsel <blkid>**: Select the active block device. The active block device is
|
|
|
|
the target of all commands below. You select it by specifying its number. For
|
|
|
|
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
|
|
|
|
example, `bsel 0` selects the first configured device. `bsel 1` selects the
|
|
|
|
second.
|
|
|
|
second.
|
|
|
|
|
|
|
|
|
|
|
|
A freshly selected blkdev begins with its "pointer" at 0.
|
|
|
|
A freshly selected blkdev begins with its "pointer" at 0.
|
|
|
|
|
|
|
|
|
|
|
|
`seek <lsw> <msw>`: Moves the blkdev "pointer" to the specified offset. The
|
|
|
|
**seek <lsw> <msw>**: Moves the blkdev "pointer" to the specified offset. The
|
|
|
|
first argument is the offset's least significant half (blkdev supports 32-bit
|
|
|
|
first argument is the offset's least significant half (blkdev supports 32-bit
|
|
|
|
addressing). Is is interpreted as an unsigned integer.
|
|
|
|
addressing). Is is interpreted as an unsigned integer.
|
|
|
|
|
|
|
|
|
|
|
|
The second argument is optional and is the most significant half of the address.
|
|
|
|
The second argument is optional and is the most significant half of the address.
|
|
|
|
It defaults to 0.
|
|
|
|
It defaults to 0.
|
|
|
|
|
|
|
|
|
|
|
|
`getb`: Read a byte in active blkdev at current pointer, then advance the
|
|
|
|
**getb**: Read a byte in active blkdev at current pointer, then advance the
|
|
|
|
pointer by one. Read byte goes in `A`.
|
|
|
|
pointer by one. Read byte goes in `A`.
|
|
|
|
|
|
|
|
|
|
|
|
`putb <val>`: Writes a byte in active blkdev at current pointer, then
|
|
|
|
**putb <val>**: Writes a byte in active blkdev at current pointer, then
|
|
|
|
advance the pointer by one. The value of the byte is determined by the
|
|
|
|
advance the pointer by one. The value of the byte is determined by the
|
|
|
|
expression supplied as an argument. Example: `putb 42`.
|
|
|
|
expression supplied as an argument. Example: `putb 42`.
|
|
|
|
|
|
|
|
|
|
|
@ -211,9 +216,9 @@ expression supplied as an argument. Example: `putb 42`.
|
|
|
|
|
|
|
|
|
|
|
|
`fs.asm` provides those commands:
|
|
|
|
`fs.asm` provides those commands:
|
|
|
|
|
|
|
|
|
|
|
|
`fls`: prints the list of files contained in the active filesystem.
|
|
|
|
**fls**: prints the list of files contained in the active filesystem.
|
|
|
|
|
|
|
|
|
|
|
|
`fopen <fhandle> <fname>`: Open file "fname" in handle "fhandle". File handles
|
|
|
|
**fopen <fhandle> <fname>**: Open file "fname" in handle "fhandle". File handles
|
|
|
|
are specified in kernel glue code and are in limited number. The kernel glue
|
|
|
|
are specified in kernel glue code and are in limited number. The kernel glue
|
|
|
|
code also maps to blkids through the glue code. So to know what you're doing
|
|
|
|
code also maps to blkids through the glue code. So to know what you're doing
|
|
|
|
here, you have to look at your glue code.
|
|
|
|
here, you have to look at your glue code.
|
|
|
@ -224,16 +229,16 @@ handle 1 maps to blkid 2.
|
|
|
|
Once a file is opened, you can use the mapped blkid as you would with any block
|
|
|
|
Once a file is opened, you can use the mapped blkid as you would with any block
|
|
|
|
device (bseek, getb, putb).
|
|
|
|
device (bseek, getb, putb).
|
|
|
|
|
|
|
|
|
|
|
|
`fnew <blkcnt> <fname>`: Allocates space of "blkcnt" blocks (each block is
|
|
|
|
**fnew <blkcnt> <fname>**: Allocates space of "blkcnt" blocks (each block is
|
|
|
|
0x100 bytes in size) for a new file names "fname". Maximum blkcnt is 0xff.
|
|
|
|
0x100 bytes in size) for a new file names "fname". Maximum blkcnt is 0xff.
|
|
|
|
|
|
|
|
|
|
|
|
`fdel <fname>`: Mark file named "fname" as deleted.
|
|
|
|
**fdel <fname>**: Mark file named "fname" as deleted.
|
|
|
|
|
|
|
|
|
|
|
|
`ldbas <fname>`: loads the content of the file specified in the argument
|
|
|
|
**ldbas <fname>**: loads the content of the file specified in the argument
|
|
|
|
(as an unquoted filename) and replace the current code listing with this
|
|
|
|
(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).
|
|
|
|
contents. Any line not starting with a number is ignored (not an error).
|
|
|
|
|
|
|
|
|
|
|
|
`basPgmHook`: That is not a command, but a routine to hook into
|
|
|
|
**basPgmHook**: That is not a command, but a routine to hook into
|
|
|
|
`BAS_FINDHOOK`. If you do, whenever a command name isn't found, the filesystem
|
|
|
|
`BAS_FINDHOOK`. If you do, whenever a command name isn't found, the filesystem
|
|
|
|
is iterated to see if it finds a file with the same name. If it does, it loads
|
|
|
|
is iterated to see if it finds a file with the same name. If it does, it loads
|
|
|
|
its contents at `USER_CODE` (from `user.h`) and calls that address, with HL
|
|
|
|
its contents at `USER_CODE` (from `user.h`) and calls that address, with HL
|
|
|
|