doc: refer to the new BASIC shell in example

ref #80
This commit is contained in:
Virgil Dupras 2019-12-11 14:57:07 -05:00
parent 25d25d017c
commit 1710c865dc
7 changed files with 112 additions and 130 deletions

View File

@ -198,7 +198,7 @@ second.
A freshly selected blkdev begins with its "pointer" at 0.
`seek <lsw> <msw>`: Moves the blkdev "pointer" to the specified offset. The
`bseek <lsw> <msw>`: 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.

View File

@ -7,8 +7,7 @@
## User guide
* [The shell](../apps/shell/README.md)
* [The BASIC shell](../apps/basic/README.md)
* [The shell](../apps/basic/README.md)
* [Load code in RAM and run it](load-run-code.md)
* [Using block devices](blockdev.md)
* [Using the filesystem](fs.md)

View File

@ -42,71 +42,31 @@ they should try to adhere to the convention, that is:
## Shell usage
`blockdev.asm` supplies 4 shell commands that you can graft to your shell thus:
[...]
SHELL_EXTRA_CMD_COUNT .equ 4
#include "shell.asm"
; extra commands
.dw blkBselCmd, blkSeekCmd, blkLoadCmd, blkSaveCmd
[...]
### bsel
`bsel` select the active block device. This specify a target for `load` and
`save`. Some applications also use the active blockdev. It receives one
argument, the device index. `bsel 0` selects the first defined device, `bsel 1`,
the second, etc. Error `0x04` when argument is out of bounds.
### seek
`seek` receives one word argument and sets the pointer for the currently active
device to the specified address. Example: `seek 1234`.
The device position is device-specific: if you seek on a device, then switch
to another device and seek again, your previous position isn't lost. You will
still be on the same position when you come back.
### load
`load` works a bit like `poke` except that it reads its data from the currently
active blockdev at its current position. If it hits the end of the blockdev
before it could load its specified number of bytes, it stops. It only raises an
error if it couldn't load any byte.
It moves the device's position to the byte after the last loaded byte.
### save
`save` is the opposite of `load`. It writes the specified number of bytes from
memory to the active blockdev at its current position.
It moves the device's position to the byte after the last written byte.
`apps/basic/blk.asm` supplies 4 shell commands that you can add to your shell.
See "Optional Modules/blk" in [the shell doc](../apps/basic/README.md).
### Example
Let's try an example: You glue yourself a Collapse OS with ACIA as its first
device and a mmap starting at `0xd000` as your second device. Here's what you
Let's try an example: You glue yourself a Collapse OS with a mmap starting at
`0xe000` as your 4th device (like it is in the shell emulator). Here's what you
could do to copy memory around:
> mptr d000
D000
> poke 4
> m=0xe000
> 10 getc
> 20 poke m a
> 30 m=m+1
> 40 if m<0xe004 goto 10
> run
[enter "abcd"]
> peek 4
61626364
> mptr c000
C000
> peek 4
[RAM garbage]
> bsel 1
> load 4
[returns immediately]
> peek 4
61626364
> seek 00 0002
> load 2
> peek 4
63646364
Awesome, right?
> bsel 3
> clear
> 10 getb
> 20 puth a
> run
61> run
62> run
63> run
64> bseek 2
> run
63> run
64>

View File

@ -18,7 +18,7 @@ files, Collapse OS tries to reuse blocks from deleted files if it can.
Once "mounted" (turned on with `fson`), you can list files, allocate new files
with `fnew`, mark files as deleted with `fdel` and, more importantly, open files
with `fopn`.
with `fopen`.
Opened files are accessed a independent block devices. It's the glue code that
decides how many file handles we'll support and to which block device ID each
@ -26,7 +26,7 @@ file handle will be assigned.
For example, you could have a system with three block devices, one for ACIA and
one for a SD card and one for a file handle. You would mount the filesystem on
block device `1` (the SD card), then open a file on handle `0` with `fopn 0
block device `1` (the SD card), then open a file on handle `0` with `fopen 0
filename`. You would then do `bsel 2` to select your third block device which
is mapped to the file you've just opened.
@ -55,13 +55,23 @@ so it's ready to use:
> fls
foo
bar
> mptr 9000
9000
> fopn 0 foo
> fopen 0 foo
> bsel 2
> load 5
> peek 5
656C6C6F21
> getb
> puth a
65
> getb
> puth a
6C
> getb
> puth a
6C
> getb
> puth a
6F
> getb
> puth a
21
> fdel bar
> fls
foo

View File

@ -31,25 +31,46 @@ look like:
.equ STDIO_PUTC aciaPutC
.inc "stdio.asm"
.equ SHELL_RAMSTART STDIO_RAMEND
.equ SHELL_EXTRA_CMD_COUNT 0
.inc "shell.asm"
; *** BASIC ***
; RAM space used in different routines for short term processing.
.equ SCRATCHPAD_SIZE 0x20
.equ SCRATCHPAD STDIO_RAMEND
.inc "lib/util.asm"
.inc "lib/ari.asm"
.inc "lib/parse.asm"
.inc "lib/fmt.asm"
.equ EXPR_PARSE parseLiteralOrVar
.inc "lib/expr.asm"
.inc "basic/util.asm"
.inc "basic/parse.asm"
.inc "basic/tok.asm"
.equ VAR_RAMSTART SCRATCHPAD+SCRATCHPAD_SIZE
.inc "basic/var.asm"
.equ BUF_RAMSTART VAR_RAMEND
.inc "basic/buf.asm"
.equ BAS_RAMSTART BUF_RAMEND
.inc "basic/main.asm"
init:
di
; setup stack
ld hl, RAMEND
ld sp, hl
ld sp, RAMEND
im 1
call aciaInit
call shellInit
call basInit
ei
jp shellLoop
jp basStart
Once this is written, building it is easy:
Once this is written, you can build it with `zasm`, which takes code from stdin
and spits binary to stdout. Because out code has includes, however, you need
to supply zasm with a block device containing a CFS containing the files to
include. This sounds, compicated, but it's managed by the `tools/zasm.sh` shell
script. The invocation would look like (it builds a CFS with the contents of
both `kernel/` and `apps/` folders):
zasm < glue.asm > collapseos.bin
tools/zasm.sh kernel/ apps/ < glue.asm > collapseos.bin
## Building zasm
@ -122,19 +143,23 @@ label at the very end of its source file. This way, it becomes easy for the
glue code to "graft" entries to the table. This approach, although simple and
effective, only works for one table per part. But it's often enough.
For example, to define extra commands in the shell:
For example, to define block devices:
[...]
.equ SHELL_EXTRA_CMD_COUNT 2
#include "shell.asm"
.dw myCmd1, myCmd2
.equ BLOCKDEV_COUNT 4
.inc "blockdev.asm"
; List of devices
.dw fsdevGetB, fsdevPutB
.dw stdoutGetB, stdoutPutB
.dw stdinGetB, stdinPutB
.dw mmapGetB, mmapPutB
[...]
### Initialization
Then, finally, comes the `init` code. This can be pretty much anything really
and this much depends on the part you select. But if you want a shell, you will
usually end it with `shellLoop`, which never returns.
usually end it with `basStart`, which never returns.
[rc2014]: https://rc2014.co.uk/
[zasm]: ../tools/emul/README.md

View File

@ -2,7 +2,7 @@
Collapse OS likely runs from ROM code. If you need to fiddle with your machine
more deeply, you will want to send arbitrary code to it and run it. You can do
so with the shell's `poke` and `call` commands.
so with the shell's `poke` and `usr` commands.
For example, let's say that you want to run this simple code that you have
sitting on your "modern" machine and want to execute on your running Collapse OS
@ -13,16 +13,18 @@ machine:
ld (0xa100), a
ret
(we must always return at the end of code that we call with `call`). This will
(we must always return at the end of code that we call with `usr`). This will
increase a number at memory address `0xa100`. First, compile it:
zasm < tosend.asm > tosend.bin
Now, we'll send that code to address `0xa000`:
> mptr a000
A000
> poke 8 (resulting binary is 8 bytes long)
> m=0xa000
> 10 getc
> 20 poke m a
> 30 if m<0xa008 goto 10
(resulting binary is 8 bytes long)
Now, at this point, it's a bit delicate. To pipe your binary to your serial
connection, you have to close `screen` with CTRL+A then `:quit` to free your
@ -35,46 +37,45 @@ but if the number of characters sent corresponds to what you gave `poke`, then
Collapse OS will be waiting for a new command. Go ahead, verify that the
transfer was successful with:
peek 8
3A00A13C3200A1C9
> peek 0a000
> puth a
3A
> peek 0a007
> puth a
C9
Good! Now, we can try to run it. Before we run it, let's peek at the value at
`0xa100` (being RAM, it's random):
> mptr a100
A100
> peek
> peek 0xa100
> puth a
61
So, we'll expect this to become `62` after we run the code. Let's go:
> mptr a000
A000
> call 00 0000
> mptr a100
A100
> peek
> usr 0xa100
> peek 0xa100
> puth a
62
Success!
## The upload.py tool
## The upload tool
The serial connection is not always 100% reliable and a bad byte can slip in
when you push your code and that's not fun when you try to debug your code (is
this bad behavior caused by my logic or by a bad serial upload?). Moreover,
sending contents bigger than `0xff` bytes can be a hassle.
sending contents manually can be a hassle.
To this end, there is a `upload.py` file in `tools/` that takes care of loading
the file and verify the contents. So, instead of doing `mptr a000` followed by
`poke 8` followed by your `cat` above, you would have done:
To this end, there is a `upload` file in `tools/` (run `make` to build it) that
takes care of loading the file and verify the contents. So, instead of doing
`getc` followed by `poke` followed by your `cat` above, you would have done:
./upload.py /dev/ttyUSB0 a000 tosend.bin
./upload /dev/ttyUSB0 a000 tosend.bin
This emits `mptr`, `poke` and `peek` commands and fail appropriately if the
`peek` doesn't match sent contents. If the file is larger than `0xff` bytes,
repeat the process until the whole file was sent (file must fit in memory space
though, of course). Very handy.
This clears your basic listing and then types in a basic algorithm to receive
and echo and pre-defined number of bytes. The `upload` tool then sends and read
each byte, verifying that they're the same. Very handy.
## Labels in RAM code
@ -126,16 +127,3 @@ You can then include that file in your "user" code, like this:
If you load that code at `0xa000` and call it, it will print "Hello World!" by
using the `printstr` routine from `core.asm`.
## Doing the same with the BASIC shell
The BASIC shell also has the capacity to load code from serial console but its
semantic is a bit different from the regular shell. Instead of peeking and
poking, you use `getc` to send data and then `putc` to send the same data back
for verification. Then, you can use `poke` to commit it to memory.
There's an upload tool that use these commands and it's `uploadb.py`. It is
invoked with the same arguments as `upload.py`.
Once your code is uploaded, you will call it with BASIC's `usr` command. See
BASIC's README for more details.

View File

@ -13,13 +13,13 @@ on a real machine, you'll have to make sure to provide these requirements.
The emulated shell has a `hello.asm` file in its mounted filesystem that is
ready to compile. It has two file handles 0 and 1, mapped to blk IDs 1 and 2.
We will open our source file in handle 0 and our dest file in handle 1. Then,
with the power of the `pgm` module, we'll autoload our newly compiled file and
execute it!
with the power of the `fs` module's autoloader, we'll load our newly compiled
file and execute it!
Collapse OS
> fnew 1 dest ; create destination file
> fopn 0 hello.asm ; open source file in handle 0
> fopn 1 dest ; open dest binary in handle 1
> fopen 0 hello.asm ; open source file in handle 0
> fopen 1 dest ; open dest binary in handle 1
> zasm 1 2 ; assemble source file into binary file
> dest ; call newly compiled file
Assembled from the shell