1
0
mirror of https://github.com/hsoft/collapseos.git synced 2024-11-02 20:30:56 +11:00

Compare commits

...

5 Commits

Author SHA1 Message Date
Virgil Dupras
fc23a7c4a1 basic: do registers-to-vars transfer after pgm hook call 2019-11-25 23:16:15 -05:00
Virgil Dupras
264e72e0fd basic: add fnew and fdel commands 2019-11-25 22:49:24 -05:00
Virgil Dupras
817088f794 fs: fix broken "ensure Z" in fsIter 2019-11-25 22:32:55 -05:00
Virgil Dupras
7c893dada1 basic: can now execute user apps!
Shell replacement sequence beginning in 3, 2, 1...
2019-11-25 22:05:10 -05:00
Virgil Dupras
b29073b01d basic: add fopen command 2019-11-25 21:41:37 -05:00
6 changed files with 156 additions and 48 deletions

View File

@ -108,50 +108,53 @@ 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**: Prints the result of the specified expression, then CR/LF. Can be **print <what> [<what>]**: Prints the result of the specified expression,
given multiple arguments. In that case, all arguments are printed separately then CR/LF. Can be given multiple arguments. In that case, all arguments are
with a space in between. For example, `print 12 13` prints `12 13<cr><lf>` 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 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**: Make the next line to be executed the line number specified as an **goto <lineno>**: Make the next line to be executed the line number
argument. Errors out if line doesn't exist. Argument can be an expression. If specified as an argument. Errors out if line doesn't exist. Argument can be
invoked in direct mode, `run` must be called to actually run the line (followed an expression. If invoked in direct mode, `run` must be called to actually
by the next, and so on). run the line (followed by the next, and so on).
**if**: If specified condition is true, execute the rest of the line. Otherwise, **if <cond> <cmd>**: If specified condition is true, execute the rest of the
do nothing. For example, `if 2>1 print 12` prints `12` and `if 2<1 print 12` line. Otherwise, do nothing. For example, `if 2>1 print 12` prints `12` and `if
does nothing. The argument for this command is a "thruth expression". 2<1 print 12` does nothing. The argument for this command is a "thruth
expression".
**input**: Prompts the user for a numerical value and puts that value in `A`. **input [<prompt>]**: Prompts the user for a numerical value and puts that
The prompted value is evaluated as an expression and then stored. The command value in `A`. The prompted value is evaluated as an expression and then stored.
takes an optional string literal parameter. If present, that string will be The command takes an optional string literal parameter. If present, that string
printed before asking for input. Unlike a `print` call, there is no CR/LF after will be printed before asking for input. Unlike a `print` call, there is no
that print. CR/LF after that print.
**peek/deek**: 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**: Put the value of specified expression into specified memory **poke/doke <addr> <val>**: Put the value of specified expression into
address. For example, `poke 42 0x102+0x40` puts `0x42` in memory address specified memory address. For example, `poke 42 0x102+0x40` puts `0x42` in
0x2a (MSB is ignored) and `doke 42 0x102+0x40` does the same as poke, but also memory address 0x2a (MSB is ignored) and `doke 42 0x102+0x40` does the same
puts `0x01` in memory address 0x2b. as poke, but also puts `0x01` in memory address 0x2b.
**in**: Same thing as `peek`, but for a I/O port. `in 42` generates an input **in <port>**: Same thing as `peek`, but for a I/O port. `in 42` generates an
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**: Same thing as `poke`, but for a I/O port. `out 42 1+2` generates an **out <port> <val>**: Same thing as `poke`, but for a I/O port. `out 42 1+2`
output I/O on port 42 with value 3. generates an output I/O on port 42 with value 3.
**sleep**: Sleep a number of "units" specified by the supplied expression. A **sleep <units>**: Sleep a number of "units" specified by the supplied
"unit" depends on the CPU clock speed. At 4MHz, it is roughly 8 microseconds. expression. A "unit" depends on the CPU clock speed. At 4MHz, it is roughly 8
microseconds.
**addr**: This very handy returns (in `A`), the address you query for. You can **addr <what>**: This very handy returns (in `A`), the address you query for.
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
address of the associated routine will be returned. address of the associated routine will be returned.
@ -160,12 +163,12 @@ Then, there's the *special stuff*. This is the list of things you can query for:
* `$`: the scratchpad. * `$`: the scratchpad.
**usr**: This calls the memory address specified as an expression argument. **usr <addr>**: This calls the memory address specified as an expression
Before doing so, it sets the registers according to a specific logic: Variable argument. Before doing so, it sets the registers according to a specific
`A`'s LSB goes in register `A`, variable `D` goes in register `DE`, `H` in `HL` logic: Variable `A`'s LSB goes in register `A`, variable `D` goes in register
`B` in `BC` and `X` in `IX`. `IY` can't be used because it's used for the jump. `DE`, `H` in `HL` `B` in `BC` and `X` in `IX`. `IY` can't be used because
Then, after the call, the value of the registers are put back into the it's used for the jump. Then, after the call, the value of the registers are
variables following the same logic. put back into the variables following the same logic.
Let's say, for example, that you want to use the kernel's `printstr` to print Let's say, for example, that you want to use the kernel's `printstr` to print
the contents of the scratchpad. First, you would call `addr $` to put the the contents of the scratchpad. First, you would call `addr $` to put the
@ -183,14 +186,15 @@ 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**: Select the active block device. The active block device is the target **bsel <blkid>**: Select the active block device. The active block device is
of all commands below. You select it by specifying its number. For example, the target of all commands below. You select it by specifying its number. For
`bsel 0` selects the first configured device. `bsel 1` selects the second. example, `bsel 0` selects the first configured device. `bsel 1` selects the
second.
A freshly selected blkdev begins with its "pointer" at 0. A freshly selected blkdev begins with its "pointer" at 0.
**seek**: Moves the blkdev "pointer" to the specified offset. The first **seek <lsw> <msw>**: Moves the blkdev "pointer" to the specified offset. The
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.
@ -199,9 +203,9 @@ 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**: Writes a byte in active blkdev at current pointer, then advance the **putb <val>**: Writes a byte in active blkdev at current pointer, then
pointer by one. The value of the byte is determined by the expression supplied advance the pointer by one. The value of the byte is determined by the
as an argument. Example: `putb 42`. expression supplied as an argument. Example: `putb 42`.
### fs ### fs
@ -209,6 +213,35 @@ as an argument. Example: `putb 42`.
**fls**: prints the list of files contained in the active filesystem. **fls**: prints the list of files contained in the active filesystem.
**ldbas**: loads the content of the file specified in the argument (as an **fopen <fhandle> <fname>**: Open file "fname" in handle "fhandle". File handles
unquoted filename) and replace the current code listing with this contents. Any are specified in kernel glue code and are in limited number. The kernel glue
line not starting with a number is ignored (not an error). 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.
In the emulated code, there are two file handles. Handle 0 maps to blkid 1 and
handle 1 maps to blkid 2.
Once a file is opened, you can use the mapped blkid as you would with any block
device (bseek, getb, putb).
**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.
**fdel <fname>**: Mark file named "fname" as deleted.
**ldbas <fname>**: 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).
**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
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
pointing to the the remaining args in the command line.
The user code called this way follows the *usr* convention for output, that is,
it converts all registers at the end of the call and stores them in appropriate
variables. If `A` is nonzero, an error is considered to have occurred.
It doesn't do var-to-register transfers on input, however. Only HL is passed
through (with the contents of the command line).

View File

@ -4,6 +4,7 @@
.equ BFS_FILE_HDL BFS_RAMSTART .equ BFS_FILE_HDL BFS_RAMSTART
.equ BFS_RAMEND @+FS_HANDLE_SIZE .equ BFS_RAMEND @+FS_HANDLE_SIZE
; Lists filenames in currently active FS
basFLS: basFLS:
ld iy, .iter ld iy, .iter
jp fsIter jp fsIter
@ -61,9 +62,79 @@ basLDBAS:
ret ret
basFOPEN:
call rdExpr ; file handle index
ret nz
push ix \ pop de
ld a, e
call fsHandle
; DE now points to file handle
call rdSep
; HL now holds the string we look for
call fsFindFN
ret nz ; not found
; Found!
; FS_PTR points to the file we want to open
push de \ pop ix ; IX now points to the file handle.
jp fsOpen
; Takes one byte block number to allocate as well we one string arg filename
; and allocates a new file in the current fs.
basFNEW:
call rdExpr ; file block count
ret nz
call rdSep ; HL now points to filename
push ix \ pop de
ld a, e
jp fsAlloc
; Deletes filename with specified name
basFDEL:
call fsFindFN
ret nz
; Found! delete
jp fsDel
basPgmHook:
; Cmd to find is in (DE)
ex de, hl
; (HL) is suitable for a direct fsFindFN call
call fsFindFN
ret nz
; We have a file! Let's load it in memory
ld ix, BFS_FILE_HDL
call fsOpen
ld hl, 0 ; addr that we read in file handle
ld de, USER_CODE ; addr in mem we write to
.loop:
call fsGetB ; we use Z at end of loop
ld (de), a ; Z preserved
inc hl ; Z preserved in 16-bit
inc de ; Z preserved in 16-bit
jr z, .loop
; Ready to jump. Return .call in IX and basCallCmd will take care
; of setting (HL) to the arg string. .call then takes care of wrapping
; the USER_CODE call.
ld ix, .call
cp a ; ensure Z
ret
.call:
ld iy, USER_CODE
call callIY
call basR2Var
or a ; Z set only if A is zero
ret
basFSCmds: basFSCmds:
.dw basFLS .dw basFLS
.db "fls", 0, 0, 0 .db "fls", 0, 0, 0
.dw basLDBAS .dw basLDBAS
.db "ldbas", 0 .db "ldbas", 0
.dw basFOPEN
.db "fopen", 0
.dw basFNEW
.db "fnew", 0, 0
.dw basFDEL
.db "fdel", 0, 0
.db 0xff, 0xff, 0xff ; end of table .db 0xff, 0xff, 0xff ; end of table

View File

@ -415,6 +415,7 @@ basUSR:
; and finally, A ; and finally, A
ld a, (VAR_TBL) ld a, (VAR_TBL)
call callIY call callIY
basR2Var: ; Just send reg to vars. Used in basPgmHook
; Same dance, opposite way ; Same dance, opposite way
ld (VAR_TBL), a ld (VAR_TBL), a
ld (VAR_TBL+46), ix ld (VAR_TBL+46), ix

View File

@ -555,7 +555,7 @@ fsIter:
call nz, callIY call nz, callIY
call fsNext call fsNext
jr z, .loop ; Z set? fsNext was successful jr z, .loop ; Z set? fsNext was successful
or a ; ensure Z cp a ; ensure Z
ret ret
; Delete currently active file ; Delete currently active file

View File

@ -3,6 +3,7 @@
.inc "err.h" .inc "err.h"
.inc "ascii.h" .inc "ascii.h"
.equ RAMSTART 0x4000 .equ RAMSTART 0x4000
.equ USER_CODE 0x4200
.equ STDIO_PORT 0x00 .equ STDIO_PORT 0x00
.equ FS_DATA_PORT 0x01 .equ FS_DATA_PORT 0x01
.equ FS_ADDR_PORT 0x02 .equ FS_ADDR_PORT 0x02
@ -109,7 +110,9 @@ basFindCmdExtra:
call basFindCmd call basFindCmd
ret z ret z
ld hl, basBLKCmds ld hl, basBLKCmds
jp basFindCmd call basFindCmd
ret z
jp basPgmHook
emulGetC: emulGetC:
; Blocks until a char is returned ; Blocks until a char is returned

View File

@ -1,5 +1,5 @@
.equ SHELL_RAMSTART 0x4100 .equ SHELL_RAMSTART 0x4100
.equ USER_CODE 0x4200 .equ USER_CODE 0x4200 ; in sync with glue.asm
; *** JUMP TABLE *** ; *** JUMP TABLE ***
.equ strncmp 0x03 .equ strncmp 0x03