mirror of
https://github.com/hsoft/collapseos.git
synced 2024-11-27 14:48:06 +11:00
parent
25d25d017c
commit
1710c865dc
@ -198,7 +198,7 @@ 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
|
`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
|
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.
|
||||||
|
|
||||||
|
@ -7,8 +7,7 @@
|
|||||||
|
|
||||||
## User guide
|
## User guide
|
||||||
|
|
||||||
* [The shell](../apps/shell/README.md)
|
* [The shell](../apps/basic/README.md)
|
||||||
* [The BASIC shell](../apps/basic/README.md)
|
|
||||||
* [Load code in RAM and run it](load-run-code.md)
|
* [Load code in RAM and run it](load-run-code.md)
|
||||||
* [Using block devices](blockdev.md)
|
* [Using block devices](blockdev.md)
|
||||||
* [Using the filesystem](fs.md)
|
* [Using the filesystem](fs.md)
|
||||||
|
@ -42,71 +42,31 @@ they should try to adhere to the convention, that is:
|
|||||||
|
|
||||||
## Shell usage
|
## Shell usage
|
||||||
|
|
||||||
`blockdev.asm` supplies 4 shell commands that you can graft to your shell thus:
|
`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).
|
||||||
[...]
|
|
||||||
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.
|
|
||||||
|
|
||||||
### Example
|
### Example
|
||||||
|
|
||||||
Let's try an example: You glue yourself a Collapse OS with ACIA as its first
|
Let's try an example: You glue yourself a Collapse OS with a mmap starting at
|
||||||
device and a mmap starting at `0xd000` as your second device. Here's what you
|
`0xe000` as your 4th device (like it is in the shell emulator). Here's what you
|
||||||
could do to copy memory around:
|
could do to copy memory around:
|
||||||
|
|
||||||
> mptr d000
|
> m=0xe000
|
||||||
D000
|
> 10 getc
|
||||||
> poke 4
|
> 20 poke m a
|
||||||
|
> 30 m=m+1
|
||||||
|
> 40 if m<0xe004 goto 10
|
||||||
|
> run
|
||||||
[enter "abcd"]
|
[enter "abcd"]
|
||||||
> peek 4
|
> bsel 3
|
||||||
61626364
|
> clear
|
||||||
> mptr c000
|
> 10 getb
|
||||||
C000
|
> 20 puth a
|
||||||
> peek 4
|
> run
|
||||||
[RAM garbage]
|
61> run
|
||||||
> bsel 1
|
62> run
|
||||||
> load 4
|
63> run
|
||||||
[returns immediately]
|
64> bseek 2
|
||||||
> peek 4
|
> run
|
||||||
61626364
|
63> run
|
||||||
> seek 00 0002
|
64>
|
||||||
> load 2
|
|
||||||
> peek 4
|
|
||||||
63646364
|
|
||||||
|
|
||||||
Awesome, right?
|
|
||||||
|
26
doc/fs.md
26
doc/fs.md
@ -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
|
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 `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
|
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
|
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
|
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
|
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
|
filename`. You would then do `bsel 2` to select your third block device which
|
||||||
is mapped to the file you've just opened.
|
is mapped to the file you've just opened.
|
||||||
|
|
||||||
@ -55,13 +55,23 @@ so it's ready to use:
|
|||||||
> fls
|
> fls
|
||||||
foo
|
foo
|
||||||
bar
|
bar
|
||||||
> mptr 9000
|
> fopen 0 foo
|
||||||
9000
|
|
||||||
> fopn 0 foo
|
|
||||||
> bsel 2
|
> bsel 2
|
||||||
> load 5
|
> getb
|
||||||
> peek 5
|
> puth a
|
||||||
656C6C6F21
|
65
|
||||||
|
> getb
|
||||||
|
> puth a
|
||||||
|
6C
|
||||||
|
> getb
|
||||||
|
> puth a
|
||||||
|
6C
|
||||||
|
> getb
|
||||||
|
> puth a
|
||||||
|
6F
|
||||||
|
> getb
|
||||||
|
> puth a
|
||||||
|
21
|
||||||
> fdel bar
|
> fdel bar
|
||||||
> fls
|
> fls
|
||||||
foo
|
foo
|
||||||
|
@ -31,25 +31,46 @@ look like:
|
|||||||
.equ STDIO_PUTC aciaPutC
|
.equ STDIO_PUTC aciaPutC
|
||||||
.inc "stdio.asm"
|
.inc "stdio.asm"
|
||||||
|
|
||||||
.equ SHELL_RAMSTART STDIO_RAMEND
|
; *** BASIC ***
|
||||||
.equ SHELL_EXTRA_CMD_COUNT 0
|
|
||||||
.inc "shell.asm"
|
; 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:
|
init:
|
||||||
di
|
di
|
||||||
; setup stack
|
; setup stack
|
||||||
ld hl, RAMEND
|
ld sp, RAMEND
|
||||||
ld sp, hl
|
|
||||||
im 1
|
im 1
|
||||||
|
|
||||||
call aciaInit
|
call aciaInit
|
||||||
call shellInit
|
call basInit
|
||||||
ei
|
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
|
## 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
|
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.
|
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
|
.equ BLOCKDEV_COUNT 4
|
||||||
#include "shell.asm"
|
.inc "blockdev.asm"
|
||||||
.dw myCmd1, myCmd2
|
; List of devices
|
||||||
|
.dw fsdevGetB, fsdevPutB
|
||||||
|
.dw stdoutGetB, stdoutPutB
|
||||||
|
.dw stdinGetB, stdinPutB
|
||||||
|
.dw mmapGetB, mmapPutB
|
||||||
[...]
|
[...]
|
||||||
|
|
||||||
### Initialization
|
### Initialization
|
||||||
|
|
||||||
Then, finally, comes the `init` code. This can be pretty much anything really
|
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
|
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/
|
[rc2014]: https://rc2014.co.uk/
|
||||||
[zasm]: ../tools/emul/README.md
|
[zasm]: ../tools/emul/README.md
|
||||||
|
@ -2,7 +2,7 @@
|
|||||||
|
|
||||||
Collapse OS likely runs from ROM code. If you need to fiddle with your machine
|
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
|
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
|
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
|
sitting on your "modern" machine and want to execute on your running Collapse OS
|
||||||
@ -13,16 +13,18 @@ machine:
|
|||||||
ld (0xa100), a
|
ld (0xa100), a
|
||||||
ret
|
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:
|
increase a number at memory address `0xa100`. First, compile it:
|
||||||
|
|
||||||
zasm < tosend.asm > tosend.bin
|
zasm < tosend.asm > tosend.bin
|
||||||
|
|
||||||
Now, we'll send that code to address `0xa000`:
|
Now, we'll send that code to address `0xa000`:
|
||||||
|
|
||||||
> mptr a000
|
> m=0xa000
|
||||||
A000
|
> 10 getc
|
||||||
> poke 8 (resulting binary is 8 bytes long)
|
> 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
|
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
|
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
|
Collapse OS will be waiting for a new command. Go ahead, verify that the
|
||||||
transfer was successful with:
|
transfer was successful with:
|
||||||
|
|
||||||
peek 8
|
> peek 0a000
|
||||||
3A00A13C3200A1C9
|
> 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
|
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):
|
`0xa100` (being RAM, it's random):
|
||||||
|
|
||||||
> mptr a100
|
> peek 0xa100
|
||||||
A100
|
> puth a
|
||||||
> peek
|
|
||||||
61
|
61
|
||||||
|
|
||||||
So, we'll expect this to become `62` after we run the code. Let's go:
|
So, we'll expect this to become `62` after we run the code. Let's go:
|
||||||
|
|
||||||
> mptr a000
|
> usr 0xa100
|
||||||
A000
|
> peek 0xa100
|
||||||
> call 00 0000
|
> puth a
|
||||||
> mptr a100
|
|
||||||
A100
|
|
||||||
> peek
|
|
||||||
62
|
62
|
||||||
|
|
||||||
Success!
|
Success!
|
||||||
|
|
||||||
## The upload.py tool
|
## The upload tool
|
||||||
|
|
||||||
The serial connection is not always 100% reliable and a bad byte can slip in
|
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
|
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,
|
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
|
To this end, there is a `upload` file in `tools/` (run `make` to build it) that
|
||||||
the file and verify the contents. So, instead of doing `mptr a000` followed by
|
takes care of loading the file and verify the contents. So, instead of doing
|
||||||
`poke 8` followed by your `cat` above, you would have done:
|
`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
|
This clears your basic listing and then types in a basic algorithm to receive
|
||||||
`peek` doesn't match sent contents. If the file is larger than `0xff` bytes,
|
and echo and pre-defined number of bytes. The `upload` tool then sends and read
|
||||||
repeat the process until the whole file was sent (file must fit in memory space
|
each byte, verifying that they're the same. Very handy.
|
||||||
though, of course). Very handy.
|
|
||||||
|
|
||||||
## Labels in RAM code
|
## 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
|
If you load that code at `0xa000` and call it, it will print "Hello World!" by
|
||||||
using the `printstr` routine from `core.asm`.
|
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.
|
|
||||||
|
@ -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
|
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.
|
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,
|
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
|
with the power of the `fs` module's autoloader, we'll load our newly compiled
|
||||||
execute it!
|
file and execute it!
|
||||||
|
|
||||||
Collapse OS
|
Collapse OS
|
||||||
> fnew 1 dest ; create destination file
|
> fnew 1 dest ; create destination file
|
||||||
> fopn 0 hello.asm ; open source file in handle 0
|
> fopen 0 hello.asm ; open source file in handle 0
|
||||||
> fopn 1 dest ; open dest binary in handle 1
|
> fopen 1 dest ; open dest binary in handle 1
|
||||||
> zasm 1 2 ; assemble source file into binary file
|
> zasm 1 2 ; assemble source file into binary file
|
||||||
> dest ; call newly compiled file
|
> dest ; call newly compiled file
|
||||||
Assembled from the shell
|
Assembled from the shell
|
||||||
|
Loading…
Reference in New Issue
Block a user