parent
20151a97f8
commit
25d25d017c
@ -124,13 +124,6 @@ basERR:
|
|||||||
; 2 - the beginning of the arg, with whitespace properly skipped.
|
; 2 - the beginning of the arg, with whitespace properly skipped.
|
||||||
;
|
;
|
||||||
; Commands are expected to set Z on success.
|
; Commands are expected to set Z on success.
|
||||||
basBYE:
|
|
||||||
; To quit the loop, let's return the stack to its initial value and
|
|
||||||
; then return.
|
|
||||||
xor a
|
|
||||||
ld sp, (BAS_INITSP)
|
|
||||||
ret
|
|
||||||
|
|
||||||
basLIST:
|
basLIST:
|
||||||
call bufFirst
|
call bufFirst
|
||||||
jr nz, .end
|
jr nz, .end
|
||||||
@ -449,8 +442,6 @@ basR2Var: ; Just send reg to vars. Used in basPgmHook
|
|||||||
|
|
||||||
; direct only
|
; direct only
|
||||||
basCmds1:
|
basCmds1:
|
||||||
.db "bye", 0
|
|
||||||
.dw basBYE
|
|
||||||
.db "list", 0
|
.db "list", 0
|
||||||
.dw basLIST
|
.dw basLIST
|
||||||
.db "run", 0
|
.db "run", 0
|
||||||
|
@ -1,98 +0,0 @@
|
|||||||
# shell
|
|
||||||
|
|
||||||
**This shell is currently being replaced with the
|
|
||||||
[BASIC shell](../basic/README.md). While it's still used in many places, it's
|
|
||||||
being phased out.**
|
|
||||||
|
|
||||||
The shell is a text interface giving you access to commands to control your
|
|
||||||
machine. It is not built to be user friendly, but to minimize binary space and
|
|
||||||
maximize code simplicity.
|
|
||||||
|
|
||||||
We expect the user of this shell to work with a copy of the user guide within
|
|
||||||
reach.
|
|
||||||
|
|
||||||
It is its design goal, however, to give you the levers you need to control your
|
|
||||||
machine fully.
|
|
||||||
|
|
||||||
## Commands and arguments
|
|
||||||
|
|
||||||
You invoke a command by typing its name, followed by a list of arguments. All
|
|
||||||
numerical arguments have to be typed in hexadecimal form, without prefix or
|
|
||||||
suffix. Lowercase is fine. Single digit is fine for byte (not word) arguments
|
|
||||||
smaller than `0x10`. Example calls:
|
|
||||||
|
|
||||||
mptr 01ff
|
|
||||||
peek 4
|
|
||||||
poke 1f
|
|
||||||
call 00 0123
|
|
||||||
|
|
||||||
All numbers printed by the shell are in hexadecimals form.
|
|
||||||
|
|
||||||
Whenever a command is malformed, the shell will print `ERR` with a code. This
|
|
||||||
table describes those codes:
|
|
||||||
|
|
||||||
| Code | Description |
|
|
||||||
|------|---------------------------|
|
|
||||||
| `01` | Unknown command |
|
|
||||||
| `02` | Badly formatted arguments |
|
|
||||||
| `03` | Out of bounds |
|
|
||||||
| `04` | Unsupported command |
|
|
||||||
| `05` | I/O error |
|
|
||||||
|
|
||||||
Applications have their own error codes as well. If you see an error code that
|
|
||||||
isn't in this list, it's an application-specific error code.
|
|
||||||
|
|
||||||
## mptr
|
|
||||||
|
|
||||||
The shell has a global memory pointer (let's call it `memptr`) that is used by
|
|
||||||
other commands. This pointer is 2 bytes long and starts at `0x0000`. To move
|
|
||||||
it, you use the mptr command with the new pointer position. The command
|
|
||||||
prints out the new `memptr` (just to confirm that it has run). Example:
|
|
||||||
|
|
||||||
> mptr 42ff
|
|
||||||
42FF
|
|
||||||
|
|
||||||
## peek
|
|
||||||
|
|
||||||
Read memory targeted by `memptr` and prints its contents in hexadecimal form.
|
|
||||||
This command takes one byte argument (optional, default to 1), the number of
|
|
||||||
bytes we want to read. Example:
|
|
||||||
|
|
||||||
> mptr 0040
|
|
||||||
0040
|
|
||||||
> peek 2
|
|
||||||
ED56
|
|
||||||
|
|
||||||
## poke
|
|
||||||
|
|
||||||
Puts the serial console in input mode and waits for a specific number of
|
|
||||||
characters to be typed (that number being specified by a byte argument). These
|
|
||||||
characters will be literally placed in memory, one after the other, starting at
|
|
||||||
`memptr`.
|
|
||||||
|
|
||||||
Example:
|
|
||||||
|
|
||||||
> poke 5
|
|
||||||
Hello
|
|
||||||
> peek 5
|
|
||||||
48656C6C6F
|
|
||||||
|
|
||||||
## call
|
|
||||||
|
|
||||||
Calls the routine at `memptr`, setting the `A` and `HL` registers to the value
|
|
||||||
specified by its optional arguments (default to 0).
|
|
||||||
|
|
||||||
Be aware that this results in a call, not a jump, so your routine needs to
|
|
||||||
return if you don't want to break your system.
|
|
||||||
|
|
||||||
The following example works in the case where you've made yourself a jump table
|
|
||||||
in your glue code a `jp printstr` at `0x0004`:
|
|
||||||
|
|
||||||
> mptr a000
|
|
||||||
A000
|
|
||||||
> poke 6
|
|
||||||
Hello\0 (you can send a null char through a terminal with CTRL+@)
|
|
||||||
> mptr 0004
|
|
||||||
0004
|
|
||||||
> call 00 a000
|
|
||||||
Hello>
|
|
@ -1,118 +0,0 @@
|
|||||||
; *** REQUIREMENTS ***
|
|
||||||
; blkSelPtr
|
|
||||||
; blkSel
|
|
||||||
; blkSeek
|
|
||||||
; blkTell
|
|
||||||
|
|
||||||
blkBselCmd:
|
|
||||||
.db "bsel", 0b001, 0, 0
|
|
||||||
ld a, (hl) ; argument supplied
|
|
||||||
push de
|
|
||||||
call blkSelPtr
|
|
||||||
call blkSel
|
|
||||||
pop de
|
|
||||||
jr nz, .error
|
|
||||||
xor a
|
|
||||||
ret
|
|
||||||
.error:
|
|
||||||
ld a, BLOCKDEV_ERR_OUT_OF_BOUNDS
|
|
||||||
ret
|
|
||||||
|
|
||||||
blkSeekCmd:
|
|
||||||
.db "seek", 0b001, 0b011, 0b001
|
|
||||||
; First, the mode
|
|
||||||
ld a, (hl)
|
|
||||||
inc hl
|
|
||||||
push af ; save mode for later
|
|
||||||
; HL points to two bytes that contain out address. Seek expects HL
|
|
||||||
; to directly contain that address.
|
|
||||||
ld a, (hl)
|
|
||||||
ex af, af'
|
|
||||||
inc hl
|
|
||||||
ld a, (hl)
|
|
||||||
ld l, a
|
|
||||||
ex af, af'
|
|
||||||
ld h, a
|
|
||||||
pop af ; bring mode back
|
|
||||||
ld de, 0 ; DE is used for seek > 64K which we don't support
|
|
||||||
call blkSeek
|
|
||||||
call blkTell
|
|
||||||
ld a, h
|
|
||||||
call printHex
|
|
||||||
ld a, l
|
|
||||||
call printHex
|
|
||||||
call printcrlf
|
|
||||||
xor a
|
|
||||||
ret
|
|
||||||
|
|
||||||
; Load the specified number of bytes (max 0x100, 0 means 0x100) from IO and
|
|
||||||
; write them in the current memory pointer (which doesn't change). If the
|
|
||||||
; blkdev hits end of stream before we reach our specified number of bytes, we
|
|
||||||
; stop loading.
|
|
||||||
;
|
|
||||||
; Returns a SHELL_ERR_IO_ERROR only if we couldn't read any byte (if the first
|
|
||||||
; call to GetB failed)
|
|
||||||
;
|
|
||||||
; Example: load 42
|
|
||||||
blkLoadCmd:
|
|
||||||
.db "load", 0b001, 0, 0
|
|
||||||
blkLoad:
|
|
||||||
push bc
|
|
||||||
push hl
|
|
||||||
|
|
||||||
ld a, (hl)
|
|
||||||
ld b, a
|
|
||||||
ld hl, (SHELL_MEM_PTR)
|
|
||||||
call blkGetB
|
|
||||||
jr nz, .ioError
|
|
||||||
jr .intoLoop ; we'v already called blkGetB. don't call it
|
|
||||||
; again.
|
|
||||||
.loop:
|
|
||||||
call blkGetB
|
|
||||||
.intoLoop:
|
|
||||||
ld (hl), a
|
|
||||||
inc hl
|
|
||||||
jr nz, .loopend
|
|
||||||
djnz .loop
|
|
||||||
.loopend:
|
|
||||||
; success
|
|
||||||
xor a
|
|
||||||
jr .end
|
|
||||||
.ioError:
|
|
||||||
ld a, SHELL_ERR_IO_ERROR
|
|
||||||
.end:
|
|
||||||
pop hl
|
|
||||||
pop bc
|
|
||||||
ret
|
|
||||||
|
|
||||||
; Load the specified number of bytes (max 0x100, 0 means 0x100) from the current
|
|
||||||
; memory pointer and write them to I/O. Memory pointer doesn't move. This puts
|
|
||||||
; chars to blkPutB. Raises error if not all bytes could be written.
|
|
||||||
;
|
|
||||||
; Example: save 42
|
|
||||||
blkSaveCmd:
|
|
||||||
.db "save", 0b001, 0, 0
|
|
||||||
blkSave:
|
|
||||||
push bc
|
|
||||||
push hl
|
|
||||||
|
|
||||||
ld a, (hl)
|
|
||||||
ld b, a
|
|
||||||
ld hl, (SHELL_MEM_PTR)
|
|
||||||
.loop:
|
|
||||||
ld a, (hl)
|
|
||||||
inc hl
|
|
||||||
call blkPutB
|
|
||||||
jr nz, .ioError
|
|
||||||
djnz .loop
|
|
||||||
.loopend:
|
|
||||||
; success
|
|
||||||
xor a
|
|
||||||
jr .end
|
|
||||||
.ioError:
|
|
||||||
ld a, SHELL_ERR_IO_ERROR
|
|
||||||
.end:
|
|
||||||
pop hl
|
|
||||||
pop bc
|
|
||||||
ret
|
|
||||||
|
|
@ -1,75 +0,0 @@
|
|||||||
; *** SHELL COMMANDS ***
|
|
||||||
fsOnCmd:
|
|
||||||
.db "fson", 0, 0, 0
|
|
||||||
jp fsOn
|
|
||||||
|
|
||||||
; Lists filenames in currently active FS
|
|
||||||
flsCmd:
|
|
||||||
.db "fls", 0, 0, 0, 0
|
|
||||||
ld iy, .iter
|
|
||||||
call fsIter
|
|
||||||
ret z
|
|
||||||
ld a, FS_ERR_NO_FS
|
|
||||||
ret
|
|
||||||
.iter:
|
|
||||||
ld a, FS_META_FNAME_OFFSET
|
|
||||||
call addHL
|
|
||||||
call printstr
|
|
||||||
jp printcrlf
|
|
||||||
|
|
||||||
; Takes one byte block number to allocate as well we one string arg filename
|
|
||||||
; and allocates a new file in the current fs.
|
|
||||||
fnewCmd:
|
|
||||||
.db "fnew", 0b001, 0b1001, 0b001
|
|
||||||
push hl
|
|
||||||
ld a, (hl)
|
|
||||||
inc hl
|
|
||||||
call intoHL
|
|
||||||
call fsAlloc
|
|
||||||
pop hl
|
|
||||||
xor a
|
|
||||||
ret
|
|
||||||
|
|
||||||
; Deletes filename with specified name
|
|
||||||
fdelCmd:
|
|
||||||
.db "fdel", 0b1001, 0b001, 0
|
|
||||||
push hl
|
|
||||||
call intoHL ; HL now holds the string we look for
|
|
||||||
call fsFindFN
|
|
||||||
jr nz, .notfound
|
|
||||||
; Found! delete
|
|
||||||
call fsDel
|
|
||||||
jr z, .end
|
|
||||||
; weird error, continue to error condition
|
|
||||||
.notfound:
|
|
||||||
ld a, FS_ERR_NOT_FOUND
|
|
||||||
.end:
|
|
||||||
pop hl
|
|
||||||
ret
|
|
||||||
|
|
||||||
|
|
||||||
; Opens specified filename in specified file handle.
|
|
||||||
; First argument is file handle, second one is file name.
|
|
||||||
; Example: fopn 0 foo.txt
|
|
||||||
fopnCmd:
|
|
||||||
.db "fopn", 0b001, 0b1001, 0b001
|
|
||||||
push hl
|
|
||||||
push de
|
|
||||||
ld a, (hl) ; file handle index
|
|
||||||
call fsHandle
|
|
||||||
; DE now points to file handle
|
|
||||||
inc hl
|
|
||||||
call intoHL ; HL now holds the string we look for
|
|
||||||
call fsFindFN
|
|
||||||
jr nz, .notfound
|
|
||||||
; Found!
|
|
||||||
; FS_PTR points to the file we want to open
|
|
||||||
push de \ pop ix ; IX now points to the file handle.
|
|
||||||
call fsOpen
|
|
||||||
jr .end
|
|
||||||
.notfound:
|
|
||||||
ld a, FS_ERR_NOT_FOUND
|
|
||||||
.end:
|
|
||||||
pop de
|
|
||||||
pop hl
|
|
||||||
ret
|
|
@ -1,35 +0,0 @@
|
|||||||
; This repesents a full-featured shell, that is, a shell that includes all
|
|
||||||
; options it has to offer. For a minimal shell, use "gluem.asm"
|
|
||||||
.inc "user.h"
|
|
||||||
.inc "err.h"
|
|
||||||
.inc "ascii.h"
|
|
||||||
.inc "blkdev.h"
|
|
||||||
.inc "fs.h"
|
|
||||||
jp init
|
|
||||||
|
|
||||||
.inc "core.asm"
|
|
||||||
.inc "lib/util.asm"
|
|
||||||
.inc "lib/parse.asm"
|
|
||||||
.inc "lib/args.asm"
|
|
||||||
.equ SHELL_RAMSTART USER_RAMSTART
|
|
||||||
.equ SHELL_EXTRA_CMD_COUNT 9
|
|
||||||
.inc "shell/main.asm"
|
|
||||||
.dw blkBselCmd, blkSeekCmd, blkLoadCmd, blkSaveCmd
|
|
||||||
.dw fsOnCmd, flsCmd, fnewCmd, fdelCmd, fopnCmd
|
|
||||||
|
|
||||||
.inc "lib/ari.asm"
|
|
||||||
.inc "lib/fmt.asm"
|
|
||||||
.inc "shell/blkdev.asm"
|
|
||||||
.inc "shell/fs.asm"
|
|
||||||
|
|
||||||
.equ PGM_RAMSTART SHELL_RAMEND
|
|
||||||
.equ PGM_CODEADDR USER_CODE
|
|
||||||
.inc "shell/pgm.asm"
|
|
||||||
|
|
||||||
init:
|
|
||||||
call shellInit
|
|
||||||
ld hl, pgmShellHook
|
|
||||||
ld (SHELL_CMDHOOK), hl
|
|
||||||
jp shellLoop
|
|
||||||
|
|
||||||
USER_RAMSTART:
|
|
@ -1,22 +0,0 @@
|
|||||||
; This repesents a minimal shell, that is, the smallest shell our configuration
|
|
||||||
; options allow. For a full-featured shell, see "glue.asm"
|
|
||||||
.inc "user.h"
|
|
||||||
.inc "err.h"
|
|
||||||
.inc "ascii.h"
|
|
||||||
jp init
|
|
||||||
|
|
||||||
.inc "core.asm"
|
|
||||||
.inc "lib/util.asm"
|
|
||||||
.inc "lib/parse.asm"
|
|
||||||
.inc "lib/args.asm"
|
|
||||||
.inc "lib/fmt.asm"
|
|
||||||
.equ SHELL_RAMSTART USER_RAMSTART
|
|
||||||
.equ SHELL_EXTRA_CMD_COUNT 0
|
|
||||||
.inc "shell/main.asm"
|
|
||||||
|
|
||||||
init:
|
|
||||||
call shellInit
|
|
||||||
jp shellLoop
|
|
||||||
|
|
||||||
USER_RAMSTART:
|
|
||||||
|
|
@ -1,361 +0,0 @@
|
|||||||
; shell
|
|
||||||
;
|
|
||||||
; Runs a shell over a block device interface.
|
|
||||||
|
|
||||||
; The shell spits a welcome prompt, wait for input and compare the first 4 chars
|
|
||||||
; of the input with a command table and call the appropriate routine if it's
|
|
||||||
; found, an error if it's not.
|
|
||||||
;
|
|
||||||
; To determine the correct routine to call we first go through cmds in
|
|
||||||
; shellCmdTbl. This means that we first go through internal cmds, then cmds
|
|
||||||
; "grafted" by glue code.
|
|
||||||
;
|
|
||||||
; If the command isn't found, SHELL_CMDHOOK is called, which should set A to
|
|
||||||
; zero if it executes something. Otherwise, SHELL_ERR_UNKNOWN_CMD will be
|
|
||||||
; returned.
|
|
||||||
;
|
|
||||||
; See constants below for error codes.
|
|
||||||
;
|
|
||||||
; All numerical values in the Collapse OS shell are represented and parsed in
|
|
||||||
; hexadecimal form, without prefix or suffix.
|
|
||||||
|
|
||||||
; *** REQUIREMENTS ***
|
|
||||||
; err
|
|
||||||
; core
|
|
||||||
; parse
|
|
||||||
; stdio
|
|
||||||
|
|
||||||
; *** DEFINES ***
|
|
||||||
; SHELL_EXTRA_CMD_COUNT: Number of extra cmds to be expected after the regular
|
|
||||||
; ones. See comment in COMMANDS section for details.
|
|
||||||
; SHELL_RAMSTART
|
|
||||||
|
|
||||||
; *** CONSTS ***
|
|
||||||
|
|
||||||
; number of entries in shellCmdTbl
|
|
||||||
.equ SHELL_CMD_COUNT 6+SHELL_EXTRA_CMD_COUNT
|
|
||||||
|
|
||||||
; maximum length for shell commands. Should be confortably below stdio's
|
|
||||||
; readline buffer length.
|
|
||||||
.equ SHELL_MAX_CMD_LEN 0x10
|
|
||||||
|
|
||||||
; *** VARIABLES ***
|
|
||||||
; Memory address that the shell is currently "pointing at" for peek, load, call
|
|
||||||
; operations. Set with mptr.
|
|
||||||
.equ SHELL_MEM_PTR SHELL_RAMSTART
|
|
||||||
|
|
||||||
; Places where we store arguments specifiers and where resulting values are
|
|
||||||
; written to after parsing.
|
|
||||||
.equ SHELL_CMD_ARGS @+2
|
|
||||||
|
|
||||||
; Pointer to a hook to call when a cmd name isn't found
|
|
||||||
.equ SHELL_CMDHOOK @+PARSE_ARG_MAXCOUNT
|
|
||||||
|
|
||||||
.equ SHELL_RAMEND @+2
|
|
||||||
|
|
||||||
; *** CODE ***
|
|
||||||
shellInit:
|
|
||||||
xor a
|
|
||||||
ld (SHELL_MEM_PTR), a
|
|
||||||
ld (SHELL_MEM_PTR+1), a
|
|
||||||
ld hl, noop
|
|
||||||
ld (SHELL_CMDHOOK), hl
|
|
||||||
|
|
||||||
; print welcome
|
|
||||||
ld hl, .welcome
|
|
||||||
jp printstr
|
|
||||||
|
|
||||||
.welcome:
|
|
||||||
.db "Collapse OS", CR, LF, "> ", 0
|
|
||||||
|
|
||||||
; Inifite loop that processes input. Because it's infinite, you should jump
|
|
||||||
; to it rather than call it. Saves two precious bytes in the stack.
|
|
||||||
shellLoop:
|
|
||||||
call stdioReadLine
|
|
||||||
call printcrlf
|
|
||||||
call shellParse
|
|
||||||
ld hl, .prompt
|
|
||||||
call printstr
|
|
||||||
jr shellLoop
|
|
||||||
|
|
||||||
.prompt:
|
|
||||||
.db "> ", 0
|
|
||||||
|
|
||||||
; Parse command (null terminated) at HL and calls it
|
|
||||||
shellParse:
|
|
||||||
; first thing: is command empty?
|
|
||||||
ld a, (hl)
|
|
||||||
or a
|
|
||||||
ret z ; empty, nothing to do
|
|
||||||
|
|
||||||
push af
|
|
||||||
push bc
|
|
||||||
push de
|
|
||||||
push hl
|
|
||||||
push ix
|
|
||||||
|
|
||||||
; Before looking for a suitable command, let's make the cmd line more
|
|
||||||
; usable by replacing the first ' ' with a null char. This way, cmp is
|
|
||||||
; easy to make.
|
|
||||||
push hl ; --> lvl 1
|
|
||||||
ld a, ' '
|
|
||||||
call findchar
|
|
||||||
jr z, .hasArgs
|
|
||||||
; no arg, (HL) is zero to facilitate processing later, add a second
|
|
||||||
; null next to that one to indicate unambiguously that we have no args.
|
|
||||||
inc hl
|
|
||||||
; Oh wait, before we proceed, is our cmd length within limits? cmd len
|
|
||||||
; is currently in A from findchar
|
|
||||||
cp SHELL_MAX_CMD_LEN
|
|
||||||
jr c, .hasArgs ; within limits
|
|
||||||
; outside limits
|
|
||||||
ld a, SHELL_ERR_UNKNOWN_CMD
|
|
||||||
jr .error
|
|
||||||
.hasArgs:
|
|
||||||
xor a
|
|
||||||
ld (hl), a
|
|
||||||
pop hl ; <-- lvl 1, beginning of cmd
|
|
||||||
|
|
||||||
ld de, shellCmdTbl
|
|
||||||
ld b, SHELL_CMD_COUNT
|
|
||||||
|
|
||||||
.loop:
|
|
||||||
push de ; we need to keep that table entry around...
|
|
||||||
call intoDE ; Jump from the table entry to the cmd addr.
|
|
||||||
ld a, 4 ; 4 chars to compare
|
|
||||||
call strncmp
|
|
||||||
pop de
|
|
||||||
jr z, .found
|
|
||||||
inc de
|
|
||||||
inc de
|
|
||||||
djnz .loop
|
|
||||||
|
|
||||||
; exhausted loop? not found
|
|
||||||
ld a, SHELL_ERR_UNKNOWN_CMD
|
|
||||||
; Before erroring out, let's try SHELL_HOOK.
|
|
||||||
ld ix, (SHELL_CMDHOOK)
|
|
||||||
call callIX
|
|
||||||
jr z, .end ; oh, not an error!
|
|
||||||
; still an error. Might be different than SHELL_ERR_UNKNOWN_CMD though.
|
|
||||||
; maybe a routine was called, but errored out.
|
|
||||||
jr .error
|
|
||||||
|
|
||||||
.found:
|
|
||||||
; we found our command. DE points to its table entry. Now, let's parse
|
|
||||||
; our args.
|
|
||||||
call intoDE ; Jump from the table entry to the cmd addr.
|
|
||||||
|
|
||||||
; advance the HL pointer to the beginning of the args.
|
|
||||||
xor a
|
|
||||||
call findchar
|
|
||||||
inc hl ; beginning of args
|
|
||||||
; Now, let's have DE point to the argspecs
|
|
||||||
ld a, 4
|
|
||||||
call addDE
|
|
||||||
|
|
||||||
; We're ready to parse args
|
|
||||||
ld ix, SHELL_CMD_ARGS
|
|
||||||
call parseArgs
|
|
||||||
or a ; cp 0
|
|
||||||
jr nz, .parseerror
|
|
||||||
|
|
||||||
; Args parsed, now we can load the routine address and call it.
|
|
||||||
; let's have DE point to the jump line
|
|
||||||
ld hl, SHELL_CMD_ARGS
|
|
||||||
ld a, PARSE_ARG_MAXCOUNT
|
|
||||||
call addDE
|
|
||||||
push de \ pop ix
|
|
||||||
; Ready to roll!
|
|
||||||
call callIX
|
|
||||||
or a ; cp 0
|
|
||||||
jr nz, .error ; if A is non-zero, we have an error
|
|
||||||
jr .end
|
|
||||||
|
|
||||||
.parseerror:
|
|
||||||
ld a, SHELL_ERR_BAD_ARGS
|
|
||||||
.error:
|
|
||||||
call shellPrintErr
|
|
||||||
.end:
|
|
||||||
pop ix
|
|
||||||
pop hl
|
|
||||||
pop de
|
|
||||||
pop bc
|
|
||||||
pop af
|
|
||||||
ret
|
|
||||||
|
|
||||||
; Print the error code set in A (in hex)
|
|
||||||
shellPrintErr:
|
|
||||||
push af
|
|
||||||
push hl
|
|
||||||
|
|
||||||
ld hl, .str
|
|
||||||
call printstr
|
|
||||||
call printHex
|
|
||||||
call printcrlf
|
|
||||||
|
|
||||||
pop hl
|
|
||||||
pop af
|
|
||||||
ret
|
|
||||||
|
|
||||||
.str:
|
|
||||||
.db "ERR ", 0
|
|
||||||
|
|
||||||
; *** COMMANDS ***
|
|
||||||
; A command is a 4 char names, followed by a PARSE_ARG_MAXCOUNT bytes of
|
|
||||||
; argument specs, followed by the routine. Then, a simple table of addresses
|
|
||||||
; is compiled in a block and this is what is iterated upon when we want all
|
|
||||||
; available commands.
|
|
||||||
;
|
|
||||||
; Format: 4 bytes name followed by PARSE_ARG_MAXCOUNT bytes specifiers,
|
|
||||||
; followed by 3 bytes jump. fill names with zeroes
|
|
||||||
;
|
|
||||||
; When these commands are called, HL points to the first byte of the
|
|
||||||
; parsed command args.
|
|
||||||
;
|
|
||||||
; If the command is a success, it should set A to zero. If the command results
|
|
||||||
; in an error, it should set an error code in A.
|
|
||||||
;
|
|
||||||
; Extra commands: Other parts might define new commands. You can add these
|
|
||||||
; commands to your shell. First, set SHELL_EXTRA_CMD_COUNT to
|
|
||||||
; the number of extra commands to add, then add a ".dw"
|
|
||||||
; directive *just* after your '.inc "shell.asm"'. Voila!
|
|
||||||
;
|
|
||||||
|
|
||||||
; Set memory pointer to the specified address (word).
|
|
||||||
; Example: mptr 01fe
|
|
||||||
shellMptrCmd:
|
|
||||||
.db "mptr", 0b011, 0b001, 0
|
|
||||||
shellMptr:
|
|
||||||
push hl
|
|
||||||
|
|
||||||
; reminder: z80 is little-endian
|
|
||||||
ld a, (hl)
|
|
||||||
ld (SHELL_MEM_PTR+1), a
|
|
||||||
inc hl
|
|
||||||
ld a, (hl)
|
|
||||||
ld (SHELL_MEM_PTR), a
|
|
||||||
|
|
||||||
ld hl, (SHELL_MEM_PTR)
|
|
||||||
ld a, h
|
|
||||||
call printHex
|
|
||||||
ld a, l
|
|
||||||
call printHex
|
|
||||||
call printcrlf
|
|
||||||
|
|
||||||
pop hl
|
|
||||||
xor a
|
|
||||||
ret
|
|
||||||
|
|
||||||
|
|
||||||
; peek the number of bytes specified by argument where memory pointer points to
|
|
||||||
; and display their value. If 0 is specified, 0x100 bytes are peeked.
|
|
||||||
;
|
|
||||||
; Example: peek 2 (will print 2 bytes)
|
|
||||||
shellPeekCmd:
|
|
||||||
.db "peek", 0b001, 0, 0
|
|
||||||
shellPeek:
|
|
||||||
push bc
|
|
||||||
push hl
|
|
||||||
|
|
||||||
ld a, (hl)
|
|
||||||
ld b, a
|
|
||||||
ld hl, (SHELL_MEM_PTR)
|
|
||||||
.loop: ld a, (hl)
|
|
||||||
call printHex
|
|
||||||
inc hl
|
|
||||||
djnz .loop
|
|
||||||
call printcrlf
|
|
||||||
|
|
||||||
.end:
|
|
||||||
pop hl
|
|
||||||
pop bc
|
|
||||||
xor a
|
|
||||||
ret
|
|
||||||
|
|
||||||
; poke specified number of bytes where memory pointer points and set them to
|
|
||||||
; bytes typed through stdioGetC. Blocks until all bytes have been fetched.
|
|
||||||
shellPokeCmd:
|
|
||||||
.db "poke", 0b001, 0, 0
|
|
||||||
shellPoke:
|
|
||||||
push bc
|
|
||||||
push hl
|
|
||||||
|
|
||||||
ld a, (hl)
|
|
||||||
ld b, a
|
|
||||||
ld hl, (SHELL_MEM_PTR)
|
|
||||||
.loop: call stdioGetC
|
|
||||||
jr nz, .loop ; nothing typed? loop
|
|
||||||
ld (hl), a
|
|
||||||
inc hl
|
|
||||||
djnz .loop
|
|
||||||
|
|
||||||
pop hl
|
|
||||||
pop bc
|
|
||||||
xor a
|
|
||||||
ret
|
|
||||||
|
|
||||||
; Calls the routine where the memory pointer currently points. This can take two
|
|
||||||
; parameters, A and HL. The first one is a byte, the second, a word. These are
|
|
||||||
; the values that A and HL are going to be set to just before calling.
|
|
||||||
; Example: run 42 cafe
|
|
||||||
shellCallCmd:
|
|
||||||
.db "call", 0b101, 0b111, 0b001
|
|
||||||
shellCall:
|
|
||||||
push hl
|
|
||||||
push ix
|
|
||||||
|
|
||||||
; Let's recap here. At this point, we have:
|
|
||||||
; 1. The address we want to execute in (SHELL_MEM_PTR)
|
|
||||||
; 2. our A arg as the first byte of (HL)
|
|
||||||
; 2. our HL arg as (HL+1) and (HL+2)
|
|
||||||
; Ready, set, go!
|
|
||||||
ld ix, (SHELL_MEM_PTR)
|
|
||||||
ld a, (hl)
|
|
||||||
ex af, af'
|
|
||||||
inc hl
|
|
||||||
ld a, (hl)
|
|
||||||
exx
|
|
||||||
ld h, a
|
|
||||||
exx
|
|
||||||
inc hl
|
|
||||||
ld a, (hl)
|
|
||||||
exx
|
|
||||||
ld l, a
|
|
||||||
ex af, af'
|
|
||||||
call callIX
|
|
||||||
|
|
||||||
.end:
|
|
||||||
pop ix
|
|
||||||
pop hl
|
|
||||||
xor a
|
|
||||||
ret
|
|
||||||
|
|
||||||
shellIORDCmd:
|
|
||||||
.db "iord", 0b001, 0, 0
|
|
||||||
push bc
|
|
||||||
ld a, (hl)
|
|
||||||
ld c, a
|
|
||||||
in a, (c)
|
|
||||||
call printHex
|
|
||||||
xor a
|
|
||||||
pop bc
|
|
||||||
ret
|
|
||||||
|
|
||||||
shellIOWRCmd:
|
|
||||||
.db "iowr", 0b001, 0b001, 0
|
|
||||||
push bc
|
|
||||||
ld a, (hl)
|
|
||||||
ld c, a
|
|
||||||
inc hl
|
|
||||||
ld a, (hl)
|
|
||||||
out (c), a
|
|
||||||
xor a
|
|
||||||
pop bc
|
|
||||||
ret
|
|
||||||
|
|
||||||
; This table is at the very end of the file on purpose. The idea is to be able
|
|
||||||
; to graft extra commands easily after an include in the glue file.
|
|
||||||
shellCmdTbl:
|
|
||||||
.dw shellMptrCmd, shellPeekCmd, shellPokeCmd, shellCallCmd
|
|
||||||
.dw shellIORDCmd, shellIOWRCmd
|
|
||||||
|
|
@ -1,51 +0,0 @@
|
|||||||
; pgm - execute programs loaded from filesystem
|
|
||||||
;
|
|
||||||
; Implements a shell hook that searches the filesystem for a file with the same
|
|
||||||
; name as the cmd, loads that file in memory and executes it, sending the
|
|
||||||
; program a pointer to *unparsed* arguments in HL.
|
|
||||||
;
|
|
||||||
; We expect the loaded program to return a status code in A. 0 means success,
|
|
||||||
; non-zero means error. Programs should avoid having error code overlaps with
|
|
||||||
; the shell so that we know where the error comes from.
|
|
||||||
;
|
|
||||||
; *** Requirements ***
|
|
||||||
; fs
|
|
||||||
;
|
|
||||||
; *** Defines ***
|
|
||||||
; PGM_CODEADDR: Memory address where to place the code we load.
|
|
||||||
;
|
|
||||||
; *** Variables ***
|
|
||||||
.equ PGM_HANDLE PGM_RAMSTART
|
|
||||||
.equ PGM_RAMEND @+FS_HANDLE_SIZE
|
|
||||||
|
|
||||||
; Routine suitable to plug into SHELL_CMDHOOK. HL points to the full cmdline.
|
|
||||||
; which has been processed to replace the first ' ' with a null char.
|
|
||||||
pgmShellHook:
|
|
||||||
; (HL) is suitable for a direct fsFindFN call
|
|
||||||
call fsFindFN
|
|
||||||
jr nz, .noFile
|
|
||||||
; We have a file! Advance HL to args
|
|
||||||
xor a
|
|
||||||
call findchar
|
|
||||||
inc hl ; beginning of args
|
|
||||||
; Alright, ready to run!
|
|
||||||
jp .run
|
|
||||||
.noFile:
|
|
||||||
ld a, SHELL_ERR_IO_ERROR
|
|
||||||
ret
|
|
||||||
.run:
|
|
||||||
push hl ; unparsed args
|
|
||||||
ld ix, PGM_HANDLE
|
|
||||||
call fsOpen
|
|
||||||
ld hl, 0 ; addr that we read in file handle
|
|
||||||
ld de, PGM_CODEADDR ; 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
|
|
||||||
|
|
||||||
pop hl ; recall args
|
|
||||||
; ready to jump!
|
|
||||||
jp PGM_CODEADDR
|
|
@ -27,11 +27,11 @@ are other recipes related to the RC2014:
|
|||||||
* [Accessing a MicroSD card](sdcard/README.md)
|
* [Accessing a MicroSD card](sdcard/README.md)
|
||||||
* [Assembling binaries](zasm/README.md)
|
* [Assembling binaries](zasm/README.md)
|
||||||
* [Interfacing a PS/2 keyboard](ps2/README.md)
|
* [Interfacing a PS/2 keyboard](ps2/README.md)
|
||||||
* [Replace shell by a BASIC interpreter](basic/README.md)
|
|
||||||
|
|
||||||
## Recipe
|
## Recipe
|
||||||
|
|
||||||
The goal is to have the shell running and accessible through the Serial I/O.
|
The goal is to have the shell running and accessible through the Serial I/O.
|
||||||
|
To make things fun, we play with I/Os using RC2014's Digital I/O module.
|
||||||
|
|
||||||
You'll need specialized tools to write data to the AT28 EEPROM. There seems to
|
You'll need specialized tools to write data to the AT28 EEPROM. There seems to
|
||||||
be many devices around made to write in flash and EEPROM modules, but being in
|
be many devices around made to write in flash and EEPROM modules, but being in
|
||||||
@ -44,6 +44,7 @@ device I use in this recipe.
|
|||||||
* [romwrite][romwrite] and its specified dependencies
|
* [romwrite][romwrite] and its specified dependencies
|
||||||
* [GNU screen][screen]
|
* [GNU screen][screen]
|
||||||
* A FTDI-to-TTL cable to connect to the Serial I/O module of the RC2014
|
* A FTDI-to-TTL cable to connect to the Serial I/O module of the RC2014
|
||||||
|
* (Optional) RC2014's Digital I/O module
|
||||||
|
|
||||||
### Write glue.asm
|
### Write glue.asm
|
||||||
|
|
||||||
@ -62,15 +63,15 @@ Then comes the usual `di` to aoid interrupts during init, and stack setup.
|
|||||||
We set interrupt mode to 1 because that's what `acia.asm` is written around.
|
We set interrupt mode to 1 because that's what `acia.asm` is written around.
|
||||||
|
|
||||||
Then, we init ACIA, shell, enable interrupt and give control of the main loop
|
Then, we init ACIA, shell, enable interrupt and give control of the main loop
|
||||||
to `shell.asm`.
|
to the BASIC shell.
|
||||||
|
|
||||||
What comes below is actual code include from parts we want to include in our
|
What comes below is actual code include from parts we want to include in our
|
||||||
OS. As you can see, we need to tell each module where to put their variables.
|
OS. As you can see, we need to tell each module where to put their variables.
|
||||||
See `parts/README.md` for details.
|
See `apps/README.md` for details.
|
||||||
|
|
||||||
You can also see from the `SHELL_GETC` and `SHELL_PUTC` macros that the shell
|
You can also see from the `STDIO_GETC` and `STDIO_PUTC` macros that the shell
|
||||||
is decoupled from the ACIA and can get its IO from anything. See
|
is decoupled from the ACIA and can get its IO from anything. See comments in
|
||||||
`parts/README.md` for details.
|
`kernel/stdio.asm` for details.
|
||||||
|
|
||||||
### Build the image
|
### Build the image
|
||||||
|
|
||||||
@ -100,6 +101,20 @@ identify the tty bound to it (in my case, `/dev/ttyUSB0`). Then:
|
|||||||
screen /dev/ttyUSB0 115200
|
screen /dev/ttyUSB0 115200
|
||||||
|
|
||||||
Press the reset button on the RC2014 and you should see the Collapse OS prompt!
|
Press the reset button on the RC2014 and you should see the Collapse OS prompt!
|
||||||
|
See documentation in `apps/basic/README.md` for details.
|
||||||
|
|
||||||
|
For now, let's have some fun with the Digital I/O module. Type this:
|
||||||
|
|
||||||
|
```
|
||||||
|
> a=0
|
||||||
|
> 10 out 0 a
|
||||||
|
> 20 sleep 0xffff
|
||||||
|
> 30 a=a+1
|
||||||
|
> 40 goto 10
|
||||||
|
> run
|
||||||
|
```
|
||||||
|
|
||||||
|
You now have your Digital I/O lights doing a pretty dance, forever.
|
||||||
|
|
||||||
[rc2014]: https://rc2014.co.uk
|
[rc2014]: https://rc2014.co.uk
|
||||||
[romwrite]: https://github.com/hsoft/romwrite
|
[romwrite]: https://github.com/hsoft/romwrite
|
||||||
|
@ -1,10 +0,0 @@
|
|||||||
TARGET = os.bin
|
|
||||||
ZASM = ../../../tools/zasm.sh
|
|
||||||
KERNEL = ../../../kernel
|
|
||||||
APPS = ../../../apps
|
|
||||||
|
|
||||||
.PHONY: all
|
|
||||||
all: $(TARGET)
|
|
||||||
$(TARGET): glue.asm
|
|
||||||
$(ZASM) $(KERNEL) $(APPS) < $< > $@
|
|
||||||
|
|
@ -1,46 +0,0 @@
|
|||||||
# BASIC as a shell
|
|
||||||
|
|
||||||
This recipe demonstrate the replacement of the usual shell with the BASIC
|
|
||||||
interpreter supplied in Collapse OS. To make things fun, we play with I/Os
|
|
||||||
using RC2014's Digital I/O module.
|
|
||||||
|
|
||||||
## Gathering parts
|
|
||||||
|
|
||||||
* Same parts as in the base recipe
|
|
||||||
* (Optional) RC2014's Digital I/O module
|
|
||||||
|
|
||||||
The Digital I/O module is only used in the example BASIC code. If you don't
|
|
||||||
have the module, just use BASIC in another fashion.
|
|
||||||
|
|
||||||
## Build the image
|
|
||||||
|
|
||||||
As usual, building `os.bin` is a matter of running `make`. Then, you can get
|
|
||||||
that image to your EEPROM like you did in the base recipe.
|
|
||||||
|
|
||||||
## Usage
|
|
||||||
|
|
||||||
Upon boot, you'll directy be in a BASIC prompt. See documentation in
|
|
||||||
`apps/basic/README.md` for details.
|
|
||||||
|
|
||||||
For now, let's have some fun with the Digital I/O module. Type this:
|
|
||||||
|
|
||||||
```
|
|
||||||
> a=0
|
|
||||||
> 10 out 0 a
|
|
||||||
> 20 sleep 0xffff
|
|
||||||
> 30 a=a+1
|
|
||||||
> 40 goto 10
|
|
||||||
> run
|
|
||||||
```
|
|
||||||
|
|
||||||
You now have your Digital I/O lights doing a pretty dance, forever.
|
|
||||||
|
|
||||||
## Looking at the glue code
|
|
||||||
|
|
||||||
If you look at the glue code, you'll see that it's very similar to the one in
|
|
||||||
the base recipe, except that the shell includes have been replaced by the basic
|
|
||||||
includes. Those includes have been copy/pasted from `apps/basic/glue.asm` and
|
|
||||||
`USER_RAMSTART` has been replaced with `STDIO_RAMEND` so that BASIC's memory
|
|
||||||
gets placed properly (that is, right after the kernel's memory).
|
|
||||||
|
|
||||||
Simple, isn't it?
|
|
@ -1,57 +0,0 @@
|
|||||||
.equ RAMSTART 0x8000
|
|
||||||
.equ RAMEND 0xffff
|
|
||||||
.equ ACIA_CTL 0x80 ; Control and status. RS off.
|
|
||||||
.equ ACIA_IO 0x81 ; Transmit. RS on.
|
|
||||||
.equ DIGIT_IO 0x00 ; digital I/O's port
|
|
||||||
|
|
||||||
jp init
|
|
||||||
|
|
||||||
; interrupt hook
|
|
||||||
.fill 0x38-$
|
|
||||||
jp aciaInt
|
|
||||||
|
|
||||||
.inc "err.h"
|
|
||||||
.inc "ascii.h"
|
|
||||||
.inc "core.asm"
|
|
||||||
.inc "str.asm"
|
|
||||||
.equ ACIA_RAMSTART RAMSTART
|
|
||||||
.inc "acia.asm"
|
|
||||||
|
|
||||||
.equ STDIO_RAMSTART ACIA_RAMEND
|
|
||||||
.equ STDIO_GETC aciaGetC
|
|
||||||
.equ STDIO_PUTC aciaPutC
|
|
||||||
.inc "stdio.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 sp, RAMEND
|
|
||||||
im 1
|
|
||||||
|
|
||||||
call aciaInit
|
|
||||||
ei
|
|
||||||
call basInit
|
|
||||||
jp basStart
|
|
||||||
|
|
||||||
|
|
@ -1,9 +1,8 @@
|
|||||||
; classic RC2014 setup (8K ROM + 32K RAM) and a stock Serial I/O module
|
|
||||||
; The RAM module is selected on A15, so it has the range 0x8000-0xffff
|
|
||||||
.equ RAMSTART 0x8000
|
.equ RAMSTART 0x8000
|
||||||
.equ RAMEND 0xffff
|
.equ RAMEND 0xffff
|
||||||
.equ ACIA_CTL 0x80 ; Control and status. RS off.
|
.equ ACIA_CTL 0x80 ; Control and status. RS off.
|
||||||
.equ ACIA_IO 0x81 ; Transmit. RS on.
|
.equ ACIA_IO 0x81 ; Transmit. RS on.
|
||||||
|
.equ DIGIT_IO 0x00 ; digital I/O's port
|
||||||
|
|
||||||
jp init
|
jp init
|
||||||
|
|
||||||
@ -23,24 +22,36 @@ jp aciaInt
|
|||||||
.equ STDIO_PUTC aciaPutC
|
.equ STDIO_PUTC aciaPutC
|
||||||
.inc "stdio.asm"
|
.inc "stdio.asm"
|
||||||
|
|
||||||
; *** Shell ***
|
; *** 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/util.asm"
|
||||||
|
.inc "lib/ari.asm"
|
||||||
.inc "lib/parse.asm"
|
.inc "lib/parse.asm"
|
||||||
.inc "lib/args.asm"
|
.inc "lib/fmt.asm"
|
||||||
.inc "lib/stdio.asm"
|
.equ EXPR_PARSE parseLiteralOrVar
|
||||||
.equ SHELL_RAMSTART STDIO_RAMEND
|
.inc "lib/expr.asm"
|
||||||
.equ SHELL_EXTRA_CMD_COUNT 0
|
.inc "basic/util.asm"
|
||||||
.inc "shell/main.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
|
|
||||||
ei
|
ei
|
||||||
jp shellLoop
|
call basInit
|
||||||
|
jp basStart
|
||||||
|
|
||||||
|
|
||||||
|
@ -48,9 +48,9 @@ Compiling and running `hello.asm` is done very much like in
|
|||||||
Collapse OS
|
Collapse OS
|
||||||
> sdci
|
> sdci
|
||||||
> fson
|
> fson
|
||||||
> fopn 0 hello.asm
|
> fopen 0 hello.asm
|
||||||
> fnew 1 dest
|
> fnew 1 dest
|
||||||
> fopn 1 dest
|
> fopen 1 dest
|
||||||
> zasm 1 2
|
> zasm 1 2
|
||||||
> dest
|
> dest
|
||||||
Assembled from a RC2014
|
Assembled from a RC2014
|
||||||
@ -94,7 +94,7 @@ Now you can write this into your card and boot Collapse OS:
|
|||||||
> fson
|
> fson
|
||||||
> fopn 0 glue.asm
|
> fopn 0 glue.asm
|
||||||
> fnew 10 dest
|
> fnew 10 dest
|
||||||
> fopn 1 dest
|
> fopen 1 dest
|
||||||
> zasm 1 2 # This takes a while. About 7 minutes.
|
> zasm 1 2 # This takes a while. About 7 minutes.
|
||||||
> sdcf # success! sdcf flushes SD card buffers to the card.
|
> sdcf # success! sdcf flushes SD card buffers to the card.
|
||||||
|
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
CFSPACK = ../cfspack/cfspack
|
CFSPACK = ../cfspack/cfspack
|
||||||
TARGETS = shell/shell bshell/shell zasm/zasm runbin/runbin
|
TARGETS = shell/shell zasm/zasm runbin/runbin
|
||||||
KERNEL = ../../kernel
|
KERNEL = ../../kernel
|
||||||
APPS = ../../apps
|
APPS = ../../apps
|
||||||
ZASMBIN = zasm/zasm
|
ZASMBIN = zasm/zasm
|
||||||
@ -13,17 +13,11 @@ OBJS = emul.o libz80/libz80.o
|
|||||||
all: $(TARGETS) $(CFSIN_CONTENTS)
|
all: $(TARGETS) $(CFSIN_CONTENTS)
|
||||||
|
|
||||||
# -o in sync with SHELL_CODE in shell/glue.asm
|
# -o in sync with SHELL_CODE in shell/glue.asm
|
||||||
shell/shell.bin: $(APPS)/shell/glue.asm $(ZASMBIN)
|
shell/shell.bin: shell/glue.asm $(ZASMBIN)
|
||||||
$(ZASMSH) -o 07 $(KERNEL) shell/user.h $(APPS) < $(APPS)/shell/glue.asm | tee $@ > /dev/null
|
$(ZASMSH) $(KERNEL) shell/user.h $(APPS) < shell/glue.asm | tee $@ > /dev/null
|
||||||
|
|
||||||
shell/kernel-bin.h: shell/glue.asm shell/shell.bin $(ZASMBIN)
|
shell/shell-bin.h: shell/shell.bin
|
||||||
$(ZASMSH) $(KERNEL) shell/shell.bin < shell/glue.asm | ./bin2c.sh KERNEL | tee $@ > /dev/null
|
./bin2c.sh KERNEL < shell/shell.bin | tee $@ > /dev/null
|
||||||
|
|
||||||
bshell/shell.bin: bshell/glue.asm $(ZASMBIN)
|
|
||||||
$(ZASMSH) $(KERNEL) bshell/user.h $(APPS) < bshell/glue.asm | tee $@ > /dev/null
|
|
||||||
|
|
||||||
bshell/shell-bin.h: bshell/shell.bin
|
|
||||||
./bin2c.sh KERNEL < bshell/shell.bin | tee $@ > /dev/null
|
|
||||||
|
|
||||||
zasm/kernel-bin.h: zasm/kernel.bin
|
zasm/kernel-bin.h: zasm/kernel.bin
|
||||||
./bin2c.sh KERNEL < zasm/kernel.bin | tee $@ > /dev/null
|
./bin2c.sh KERNEL < zasm/kernel.bin | tee $@ > /dev/null
|
||||||
@ -31,12 +25,9 @@ zasm/kernel-bin.h: zasm/kernel.bin
|
|||||||
zasm/zasm-bin.h: zasm/zasm.bin
|
zasm/zasm-bin.h: zasm/zasm.bin
|
||||||
./bin2c.sh USERSPACE < zasm/zasm.bin | tee $@ > /dev/null
|
./bin2c.sh USERSPACE < zasm/zasm.bin | tee $@ > /dev/null
|
||||||
|
|
||||||
shell/shell: shell/shell.c $(OBJS) shell/kernel-bin.h
|
shell/shell: shell/shell.c $(OBJS) shell/shell-bin.h
|
||||||
$(CC) shell/shell.c $(OBJS) -o $@
|
$(CC) shell/shell.c $(OBJS) -o $@
|
||||||
|
|
||||||
bshell/shell: bshell/shell.c $(OBJS) bshell/shell-bin.h
|
|
||||||
$(CC) bshell/shell.c $(OBJS) -o $@
|
|
||||||
|
|
||||||
$(ZASMBIN): zasm/zasm.c $(OBJS) zasm/kernel-bin.h zasm/zasm-bin.h $(CFSPACK)
|
$(ZASMBIN): zasm/zasm.c $(OBJS) zasm/kernel-bin.h zasm/zasm-bin.h $(CFSPACK)
|
||||||
$(CC) zasm/zasm.c $(OBJS) -o $@
|
$(CC) zasm/zasm.c $(OBJS) -o $@
|
||||||
|
|
||||||
|
@ -12,11 +12,11 @@ After that, you can run `make` and it builds all tools.
|
|||||||
|
|
||||||
## shell
|
## shell
|
||||||
|
|
||||||
Running `shell/shell` runs the shell in an emulated machine. The goal of this
|
Running `shell/shell` runs the BASIC shell in an emulated machine. The goal of
|
||||||
machine is not to simulate real hardware, but rather to serve as a development
|
this machine is not to simulate real hardware, but rather to serve as a
|
||||||
platform. What we do here is we emulate the z80 part, the 64K memory space and
|
development platform. What we do here is we emulate the z80 part, the 64K
|
||||||
then hook some fake I/Os to stdin, stdout and a small storage device that is
|
memory space and then hook some fake I/Os to stdin, stdout and a small storage
|
||||||
suitable for Collapse OS's filesystem to run on.
|
device that is suitable for Collapse OS's filesystem to run on.
|
||||||
|
|
||||||
Through that, it becomes easier to develop userspace applications for Collapse
|
Through that, it becomes easier to develop userspace applications for Collapse
|
||||||
OS.
|
OS.
|
||||||
@ -25,11 +25,6 @@ We don't try to emulate real hardware to ease the development of device drivers
|
|||||||
because so far, I don't see the advantage of emulation versus running code on
|
because so far, I don't see the advantage of emulation versus running code on
|
||||||
the real thing.
|
the real thing.
|
||||||
|
|
||||||
## bshell
|
|
||||||
|
|
||||||
The `basic` app is on its way to replace the shell. It is wrapped in the z80
|
|
||||||
emulator in the same way that the shell is and interacts with `cfsin` similarly.
|
|
||||||
|
|
||||||
## zasm
|
## zasm
|
||||||
|
|
||||||
`zasm/zasm` is `apps/zasm` wrapped in an emulator. It is quite central to the
|
`zasm/zasm` is `apps/zasm` wrapped in an emulator. It is quite central to the
|
||||||
|
@ -1,178 +0,0 @@
|
|||||||
.inc "blkdev.h"
|
|
||||||
.inc "fs.h"
|
|
||||||
.inc "err.h"
|
|
||||||
.inc "ascii.h"
|
|
||||||
.equ RAMSTART 0x2000
|
|
||||||
.equ USER_CODE 0x4200
|
|
||||||
.equ STDIO_PORT 0x00
|
|
||||||
.equ FS_DATA_PORT 0x01
|
|
||||||
.equ FS_ADDR_PORT 0x02
|
|
||||||
|
|
||||||
jp init
|
|
||||||
|
|
||||||
; *** JUMP TABLE ***
|
|
||||||
jp strncmp
|
|
||||||
jp upcase
|
|
||||||
jp findchar
|
|
||||||
jp blkSelPtr
|
|
||||||
jp blkSel
|
|
||||||
jp blkSet
|
|
||||||
jp blkSeek
|
|
||||||
jp blkTell
|
|
||||||
jp blkGetB
|
|
||||||
jp blkPutB
|
|
||||||
jp fsFindFN
|
|
||||||
jp fsOpen
|
|
||||||
jp fsGetB
|
|
||||||
jp fsPutB
|
|
||||||
jp fsSetSize
|
|
||||||
jp fsOn
|
|
||||||
jp fsIter
|
|
||||||
jp fsAlloc
|
|
||||||
jp fsDel
|
|
||||||
jp fsHandle
|
|
||||||
jp printstr
|
|
||||||
jp printnstr
|
|
||||||
jp _blkGetB
|
|
||||||
jp _blkPutB
|
|
||||||
jp _blkSeek
|
|
||||||
jp _blkTell
|
|
||||||
jp printcrlf
|
|
||||||
jp stdioGetC
|
|
||||||
jp stdioPutC
|
|
||||||
jp stdioReadLine
|
|
||||||
|
|
||||||
.inc "core.asm"
|
|
||||||
.inc "str.asm"
|
|
||||||
|
|
||||||
.equ BLOCKDEV_RAMSTART RAMSTART
|
|
||||||
.equ BLOCKDEV_COUNT 4
|
|
||||||
.inc "blockdev.asm"
|
|
||||||
; List of devices
|
|
||||||
.dw fsdevGetB, fsdevPutB
|
|
||||||
.dw stdoutGetB, stdoutPutB
|
|
||||||
.dw stdinGetB, stdinPutB
|
|
||||||
.dw mmapGetB, mmapPutB
|
|
||||||
|
|
||||||
|
|
||||||
.equ MMAP_START 0xe000
|
|
||||||
.inc "mmap.asm"
|
|
||||||
|
|
||||||
.equ STDIO_RAMSTART BLOCKDEV_RAMEND
|
|
||||||
.equ STDIO_GETC emulGetC
|
|
||||||
.equ STDIO_PUTC emulPutC
|
|
||||||
.inc "stdio.asm"
|
|
||||||
|
|
||||||
.equ FS_RAMSTART STDIO_RAMEND
|
|
||||||
.equ FS_HANDLE_COUNT 2
|
|
||||||
.inc "fs.asm"
|
|
||||||
|
|
||||||
; *** BASIC ***
|
|
||||||
|
|
||||||
; RAM space used in different routines for short term processing.
|
|
||||||
.equ SCRATCHPAD_SIZE 0x20
|
|
||||||
.equ SCRATCHPAD FS_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 BFS_RAMSTART BUF_RAMEND
|
|
||||||
.inc "basic/fs.asm"
|
|
||||||
.inc "basic/blk.asm"
|
|
||||||
.equ BAS_RAMSTART BFS_RAMEND
|
|
||||||
.inc "basic/main.asm"
|
|
||||||
|
|
||||||
init:
|
|
||||||
di
|
|
||||||
; setup stack
|
|
||||||
ld sp, 0xffff
|
|
||||||
call fsInit
|
|
||||||
ld a, 0 ; select fsdev
|
|
||||||
ld de, BLOCKDEV_SEL
|
|
||||||
call blkSel
|
|
||||||
call fsOn
|
|
||||||
call basInit
|
|
||||||
ld hl, basFindCmdExtra
|
|
||||||
ld (BAS_FINDHOOK), hl
|
|
||||||
jp basStart
|
|
||||||
|
|
||||||
basFindCmdExtra:
|
|
||||||
ld hl, basFSCmds
|
|
||||||
call basFindCmd
|
|
||||||
ret z
|
|
||||||
ld hl, basBLKCmds
|
|
||||||
call basFindCmd
|
|
||||||
ret z
|
|
||||||
jp basPgmHook
|
|
||||||
|
|
||||||
emulGetC:
|
|
||||||
; Blocks until a char is returned
|
|
||||||
in a, (STDIO_PORT)
|
|
||||||
cp a ; ensure Z
|
|
||||||
ret
|
|
||||||
|
|
||||||
emulPutC:
|
|
||||||
out (STDIO_PORT), a
|
|
||||||
ret
|
|
||||||
|
|
||||||
fsdevGetB:
|
|
||||||
ld a, e
|
|
||||||
out (FS_ADDR_PORT), a
|
|
||||||
ld a, h
|
|
||||||
out (FS_ADDR_PORT), a
|
|
||||||
ld a, l
|
|
||||||
out (FS_ADDR_PORT), a
|
|
||||||
in a, (FS_ADDR_PORT)
|
|
||||||
or a
|
|
||||||
ret nz
|
|
||||||
in a, (FS_DATA_PORT)
|
|
||||||
cp a ; ensure Z
|
|
||||||
ret
|
|
||||||
|
|
||||||
fsdevPutB:
|
|
||||||
push af
|
|
||||||
ld a, e
|
|
||||||
out (FS_ADDR_PORT), a
|
|
||||||
ld a, h
|
|
||||||
out (FS_ADDR_PORT), a
|
|
||||||
ld a, l
|
|
||||||
out (FS_ADDR_PORT), a
|
|
||||||
in a, (FS_ADDR_PORT)
|
|
||||||
cp 2 ; only A > 1 means error
|
|
||||||
jr nc, .error ; A >= 2
|
|
||||||
pop af
|
|
||||||
out (FS_DATA_PORT), a
|
|
||||||
cp a ; ensure Z
|
|
||||||
ret
|
|
||||||
.error:
|
|
||||||
pop af
|
|
||||||
jp unsetZ ; returns
|
|
||||||
|
|
||||||
.equ STDOUT_HANDLE FS_HANDLES
|
|
||||||
|
|
||||||
stdoutGetB:
|
|
||||||
ld ix, STDOUT_HANDLE
|
|
||||||
jp fsGetB
|
|
||||||
|
|
||||||
stdoutPutB:
|
|
||||||
ld ix, STDOUT_HANDLE
|
|
||||||
jp fsPutB
|
|
||||||
|
|
||||||
.equ STDIN_HANDLE FS_HANDLES+FS_HANDLE_SIZE
|
|
||||||
|
|
||||||
stdinGetB:
|
|
||||||
ld ix, STDIN_HANDLE
|
|
||||||
jp fsGetB
|
|
||||||
|
|
||||||
stdinPutB:
|
|
||||||
ld ix, STDIN_HANDLE
|
|
||||||
jp fsPutB
|
|
@ -1,191 +0,0 @@
|
|||||||
#include <stdint.h>
|
|
||||||
#include <stdio.h>
|
|
||||||
#include <termios.h>
|
|
||||||
#include "../emul.h"
|
|
||||||
#include "shell-bin.h"
|
|
||||||
|
|
||||||
/* Collapse OS shell with filesystem
|
|
||||||
*
|
|
||||||
* On startup, if "cfsin" directory exists, it packs it as a afke block device
|
|
||||||
* and loads it in. Upon halting, unpcks the contents of that block device in
|
|
||||||
* "cfsout" directory.
|
|
||||||
*
|
|
||||||
* Memory layout:
|
|
||||||
*
|
|
||||||
* 0x0000 - 0x3fff: ROM code from shell.asm
|
|
||||||
* 0x4000 - 0x4fff: Kernel memory
|
|
||||||
* 0x5000 - 0xffff: Userspace
|
|
||||||
*
|
|
||||||
* I/O Ports:
|
|
||||||
*
|
|
||||||
* 0 - stdin / stdout
|
|
||||||
* 1 - Filesystem blockdev data read/write. Reads and write data to the address
|
|
||||||
* previously selected through port 2
|
|
||||||
*/
|
|
||||||
|
|
||||||
//#define DEBUG
|
|
||||||
#define MAX_FSDEV_SIZE 0x20000
|
|
||||||
|
|
||||||
// in sync with glue.asm
|
|
||||||
#define RAMSTART 0x2000
|
|
||||||
#define STDIO_PORT 0x00
|
|
||||||
#define FS_DATA_PORT 0x01
|
|
||||||
// Controls what address (24bit) the data port returns. To select an address,
|
|
||||||
// this port has to be written to 3 times, starting with the MSB.
|
|
||||||
// Reading this port returns an out-of-bounds indicator. Meaning:
|
|
||||||
// 0 means addr is within bounds
|
|
||||||
// 1 means that we're equal to fsdev size (error for reading, ok for writing)
|
|
||||||
// 2 means more than fsdev size (always invalid)
|
|
||||||
// 3 means incomplete addr setting
|
|
||||||
#define FS_ADDR_PORT 0x02
|
|
||||||
|
|
||||||
static uint8_t fsdev[MAX_FSDEV_SIZE] = {0};
|
|
||||||
static uint32_t fsdev_size = 0;
|
|
||||||
static uint32_t fsdev_ptr = 0;
|
|
||||||
// 0 = idle, 1 = received MSB (of 24bit addr), 2 = received middle addr
|
|
||||||
static int fsdev_addr_lvl = 0;
|
|
||||||
static int running;
|
|
||||||
|
|
||||||
static uint8_t iord_stdio()
|
|
||||||
{
|
|
||||||
int c = getchar();
|
|
||||||
if (c == EOF) {
|
|
||||||
running = 0;
|
|
||||||
}
|
|
||||||
return (uint8_t)c;
|
|
||||||
}
|
|
||||||
|
|
||||||
static uint8_t iord_fsdata()
|
|
||||||
{
|
|
||||||
if (fsdev_addr_lvl != 0) {
|
|
||||||
fprintf(stderr, "Reading FSDEV in the middle of an addr op (%d)\n", fsdev_ptr);
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
if (fsdev_ptr < fsdev_size) {
|
|
||||||
#ifdef DEBUG
|
|
||||||
fprintf(stderr, "Reading FSDEV at offset %d\n", fsdev_ptr);
|
|
||||||
#endif
|
|
||||||
return fsdev[fsdev_ptr];
|
|
||||||
} else {
|
|
||||||
// don't warn when ==, we're not out of bounds, just at the edge.
|
|
||||||
if (fsdev_ptr > fsdev_size) {
|
|
||||||
fprintf(stderr, "Out of bounds FSDEV read at %d\n", fsdev_ptr);
|
|
||||||
}
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static uint8_t iord_fsaddr()
|
|
||||||
{
|
|
||||||
if (fsdev_addr_lvl != 0) {
|
|
||||||
return 3;
|
|
||||||
} else if (fsdev_ptr > fsdev_size) {
|
|
||||||
fprintf(stderr, "Out of bounds FSDEV addr request at %d / %d\n", fsdev_ptr, fsdev_size);
|
|
||||||
return 2;
|
|
||||||
} else if (fsdev_ptr == fsdev_size) {
|
|
||||||
return 1;
|
|
||||||
} else {
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static void iowr_stdio(uint8_t val)
|
|
||||||
{
|
|
||||||
if (val == 0x04) { // CTRL+D
|
|
||||||
running = 0;
|
|
||||||
} else {
|
|
||||||
putchar(val);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static void iowr_fsdata(uint8_t val)
|
|
||||||
{
|
|
||||||
if (fsdev_addr_lvl != 0) {
|
|
||||||
fprintf(stderr, "Writing to FSDEV in the middle of an addr op (%d)\n", fsdev_ptr);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (fsdev_ptr < fsdev_size) {
|
|
||||||
#ifdef DEBUG
|
|
||||||
fprintf(stderr, "Writing to FSDEV (%d)\n", fsdev_ptr);
|
|
||||||
#endif
|
|
||||||
fsdev[fsdev_ptr] = val;
|
|
||||||
} else if ((fsdev_ptr == fsdev_size) && (fsdev_ptr < MAX_FSDEV_SIZE)) {
|
|
||||||
// We're at the end of fsdev, grow it
|
|
||||||
fsdev[fsdev_ptr] = val;
|
|
||||||
fsdev_size++;
|
|
||||||
#ifdef DEBUG
|
|
||||||
fprintf(stderr, "Growing FSDEV (%d)\n", fsdev_ptr);
|
|
||||||
#endif
|
|
||||||
} else {
|
|
||||||
fprintf(stderr, "Out of bounds FSDEV write at %d\n", fsdev_ptr);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static void iowr_fsaddr(uint8_t val)
|
|
||||||
{
|
|
||||||
if (fsdev_addr_lvl == 0) {
|
|
||||||
fsdev_ptr = val << 16;
|
|
||||||
fsdev_addr_lvl = 1;
|
|
||||||
} else if (fsdev_addr_lvl == 1) {
|
|
||||||
fsdev_ptr |= val << 8;
|
|
||||||
fsdev_addr_lvl = 2;
|
|
||||||
} else {
|
|
||||||
fsdev_ptr |= val;
|
|
||||||
fsdev_addr_lvl = 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
int main()
|
|
||||||
{
|
|
||||||
// Setup fs blockdev
|
|
||||||
FILE *fp = popen("../cfspack/cfspack cfsin", "r");
|
|
||||||
if (fp != NULL) {
|
|
||||||
printf("Initializing filesystem\n");
|
|
||||||
int i = 0;
|
|
||||||
int c = fgetc(fp);
|
|
||||||
while (c != EOF) {
|
|
||||||
fsdev[i] = c & 0xff;
|
|
||||||
i++;
|
|
||||||
c = fgetc(fp);
|
|
||||||
}
|
|
||||||
fsdev_size = i;
|
|
||||||
pclose(fp);
|
|
||||||
} else {
|
|
||||||
printf("Can't initialize filesystem. Leaving blank.\n");
|
|
||||||
}
|
|
||||||
|
|
||||||
// Turn echo off: the shell takes care of its own echoing.
|
|
||||||
struct termios termInfo;
|
|
||||||
if (tcgetattr(0, &termInfo) == -1) {
|
|
||||||
printf("Can't setup terminal.\n");
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
termInfo.c_lflag &= ~ECHO;
|
|
||||||
termInfo.c_lflag &= ~ICANON;
|
|
||||||
tcsetattr(0, TCSAFLUSH, &termInfo);
|
|
||||||
|
|
||||||
|
|
||||||
Machine *m = emul_init();
|
|
||||||
m->ramstart = RAMSTART;
|
|
||||||
m->iord[STDIO_PORT] = iord_stdio;
|
|
||||||
m->iord[FS_DATA_PORT] = iord_fsdata;
|
|
||||||
m->iord[FS_ADDR_PORT] = iord_fsaddr;
|
|
||||||
m->iowr[STDIO_PORT] = iowr_stdio;
|
|
||||||
m->iowr[FS_DATA_PORT] = iowr_fsdata;
|
|
||||||
m->iowr[FS_ADDR_PORT] = iowr_fsaddr;
|
|
||||||
// initialize memory
|
|
||||||
for (int i=0; i<sizeof(KERNEL); i++) {
|
|
||||||
m->mem[i] = KERNEL[i];
|
|
||||||
}
|
|
||||||
// Run!
|
|
||||||
running = 1;
|
|
||||||
|
|
||||||
while (running && emul_step());
|
|
||||||
|
|
||||||
printf("Done!\n");
|
|
||||||
termInfo.c_lflag |= ECHO;
|
|
||||||
termInfo.c_lflag |= ICANON;
|
|
||||||
tcsetattr(0, TCSAFLUSH, &termInfo);
|
|
||||||
emul_printdebug();
|
|
||||||
return 0;
|
|
||||||
}
|
|
@ -1,35 +0,0 @@
|
|||||||
.equ SHELL_RAMSTART 0x4100
|
|
||||||
.equ USER_CODE 0x4200 ; in sync with glue.asm
|
|
||||||
|
|
||||||
; *** JUMP TABLE ***
|
|
||||||
.equ strncmp 0x03
|
|
||||||
.equ upcase @+3
|
|
||||||
.equ findchar @+3
|
|
||||||
.equ blkSelPtr @+3
|
|
||||||
.equ blkSel @+3
|
|
||||||
.equ blkSet @+3
|
|
||||||
.equ blkSeek @+3
|
|
||||||
.equ blkTell @+3
|
|
||||||
.equ blkGetB @+3
|
|
||||||
.equ blkPutB @+3
|
|
||||||
.equ fsFindFN @+3
|
|
||||||
.equ fsOpen @+3
|
|
||||||
.equ fsGetB @+3
|
|
||||||
.equ fsPutB @+3
|
|
||||||
.equ fsSetSize @+3
|
|
||||||
.equ fsOn @+3
|
|
||||||
.equ fsIter @+3
|
|
||||||
.equ fsAlloc @+3
|
|
||||||
.equ fsDel @+3
|
|
||||||
.equ fsHandle @+3
|
|
||||||
.equ printstr @+3
|
|
||||||
.equ printnstr @+3
|
|
||||||
.equ _blkGetB @+3
|
|
||||||
.equ _blkPutB @+3
|
|
||||||
.equ _blkSeek @+3
|
|
||||||
.equ _blkTell @+3
|
|
||||||
.equ printcrlf @+3
|
|
||||||
.equ stdioGetC @+3
|
|
||||||
.equ stdioPutC @+3
|
|
||||||
.equ stdioReadLine @+3
|
|
||||||
|
|
@ -1,17 +1,9 @@
|
|||||||
; Last check:
|
|
||||||
; Kernel size: 0x619
|
|
||||||
; Kernel RAM usage: 0x66
|
|
||||||
; Shell size: 0x411
|
|
||||||
; Shell RAM usage: 0x11
|
|
||||||
|
|
||||||
.inc "blkdev.h"
|
.inc "blkdev.h"
|
||||||
.inc "fs.h"
|
.inc "fs.h"
|
||||||
.inc "err.h"
|
.inc "err.h"
|
||||||
.inc "ascii.h"
|
.inc "ascii.h"
|
||||||
.equ RAMSTART 0x4000
|
.equ RAMSTART 0x2000
|
||||||
; 0x100 - 0x66 gives us a nice space for the stack.
|
.equ USER_CODE 0x4200
|
||||||
.equ KERNEL_RAMEND 0x4100
|
|
||||||
.equ SHELL_CODE 0x0700
|
|
||||||
.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
|
||||||
@ -75,16 +67,52 @@
|
|||||||
.equ FS_HANDLE_COUNT 2
|
.equ FS_HANDLE_COUNT 2
|
||||||
.inc "fs.asm"
|
.inc "fs.asm"
|
||||||
|
|
||||||
|
; *** BASIC ***
|
||||||
|
|
||||||
|
; RAM space used in different routines for short term processing.
|
||||||
|
.equ SCRATCHPAD_SIZE 0x20
|
||||||
|
.equ SCRATCHPAD FS_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 BFS_RAMSTART BUF_RAMEND
|
||||||
|
.inc "basic/fs.asm"
|
||||||
|
.inc "basic/blk.asm"
|
||||||
|
.equ BAS_RAMSTART BFS_RAMEND
|
||||||
|
.inc "basic/main.asm"
|
||||||
|
|
||||||
init:
|
init:
|
||||||
di
|
di
|
||||||
; setup stack
|
; setup stack
|
||||||
ld sp, KERNEL_RAMEND
|
ld sp, 0xffff
|
||||||
call fsInit
|
call fsInit
|
||||||
ld a, 0 ; select fsdev
|
ld a, 0 ; select fsdev
|
||||||
ld de, BLOCKDEV_SEL
|
ld de, BLOCKDEV_SEL
|
||||||
call blkSel
|
call blkSel
|
||||||
call fsOn
|
call fsOn
|
||||||
call SHELL_CODE
|
call basInit
|
||||||
|
ld hl, basFindCmdExtra
|
||||||
|
ld (BAS_FINDHOOK), hl
|
||||||
|
jp basStart
|
||||||
|
|
||||||
|
basFindCmdExtra:
|
||||||
|
ld hl, basFSCmds
|
||||||
|
call basFindCmd
|
||||||
|
ret z
|
||||||
|
ld hl, basBLKCmds
|
||||||
|
call basFindCmd
|
||||||
|
ret z
|
||||||
|
jp basPgmHook
|
||||||
|
|
||||||
emulGetC:
|
emulGetC:
|
||||||
; Blocks until a char is returned
|
; Blocks until a char is returned
|
||||||
@ -148,6 +176,3 @@ stdinGetB:
|
|||||||
stdinPutB:
|
stdinPutB:
|
||||||
ld ix, STDIN_HANDLE
|
ld ix, STDIN_HANDLE
|
||||||
jp fsPutB
|
jp fsPutB
|
||||||
|
|
||||||
.fill SHELL_CODE-$
|
|
||||||
.bin "shell.bin"
|
|
||||||
|
@ -2,7 +2,7 @@
|
|||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
#include <termios.h>
|
#include <termios.h>
|
||||||
#include "../emul.h"
|
#include "../emul.h"
|
||||||
#include "kernel-bin.h"
|
#include "shell-bin.h"
|
||||||
|
|
||||||
/* Collapse OS shell with filesystem
|
/* Collapse OS shell with filesystem
|
||||||
*
|
*
|
||||||
@ -26,8 +26,8 @@
|
|||||||
//#define DEBUG
|
//#define DEBUG
|
||||||
#define MAX_FSDEV_SIZE 0x20000
|
#define MAX_FSDEV_SIZE 0x20000
|
||||||
|
|
||||||
// in sync with shell.asm
|
// in sync with glue.asm
|
||||||
#define RAMSTART 0x4000
|
#define RAMSTART 0x2000
|
||||||
#define STDIO_PORT 0x00
|
#define STDIO_PORT 0x00
|
||||||
#define FS_DATA_PORT 0x01
|
#define FS_DATA_PORT 0x01
|
||||||
// Controls what address (24bit) the data port returns. To select an address,
|
// Controls what address (24bit) the data port returns. To select an address,
|
||||||
|
@ -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
|
||||||
|
Loading…
Reference in New Issue
Block a user