mirror of
https://github.com/hsoft/collapseos.git
synced 2024-11-26 01:48:06 +11:00
Compare commits
4 Commits
585e9f3b6e
...
aad8efeff7
Author | SHA1 | Date | |
---|---|---|---|
|
aad8efeff7 | ||
|
b7d4860acf | ||
|
7761cebb0a | ||
|
9602f9b983 |
@ -103,3 +103,23 @@ stored where specified. For example, `input x` stores the result of the
|
||||
evaluation in variable `x`. Before the variable name, a quoted string literal
|
||||
can be specified. In that case, that string will be printed as-is just before
|
||||
the prompt.
|
||||
|
||||
**peek/deek**: Put the value at specified memory address into specified
|
||||
variable. peek is for a single byte, deek is for a word (little endian). For
|
||||
example, `peek 42 a` puts the byte value contained in memory address 0x002a
|
||||
into variable `a`. `deek 42 a` 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
|
||||
address. For example, `poke 42 0x102+0x40` puts `0x42` in memory address
|
||||
0x2a (MSB is ignored) and `doke 42 0x102+0x40` does the same as poke, but also
|
||||
puts `0x01` in memory address 0x2b.
|
||||
|
||||
**in**: Same thing as `peek`, but for a I/O port. `in 42 a` generates an 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
|
||||
output I/O on port 42 with value 3.
|
||||
|
||||
**sleep**: Sleep a number of "units" specified by the supplied expression. A
|
||||
"unit" depends on the CPU clock speed. At 4MHz, it is roughly 8 microseconds.
|
||||
|
@ -56,17 +56,17 @@ basCallCmd:
|
||||
; let's see if it's a variable assignment.
|
||||
call varTryAssign
|
||||
ret z ; Done!
|
||||
; Second, get cmd length
|
||||
call fnWSIdx
|
||||
cp 7
|
||||
jp nc, unsetZ ; Too long, can't possibly fit anything.
|
||||
; A contains whitespace IDX, save it in B
|
||||
ld b, a
|
||||
ex de, hl
|
||||
push de ; --> lvl 1.
|
||||
ld de, SCRATCHPAD
|
||||
call rdWord
|
||||
; cmdname to find in (DE)
|
||||
; How lucky, we have a legitimate use of "ex (sp), hl"! We have the
|
||||
; cmd table in the stack, which we want in HL and we have the rest of
|
||||
; the cmdline in (HL), which we want in the stack!
|
||||
ex (sp), hl
|
||||
inc hl \ inc hl
|
||||
.loop:
|
||||
ld a, b ; whitespace IDX
|
||||
call strncmp
|
||||
call strcmp
|
||||
jr z, .found
|
||||
ld a, 8
|
||||
call addHL
|
||||
@ -74,15 +74,14 @@ basCallCmd:
|
||||
cp 0xff
|
||||
jr nz, .loop
|
||||
; not found
|
||||
pop hl ; <-- lvl 1
|
||||
jp unsetZ
|
||||
.found:
|
||||
dec hl \ dec hl
|
||||
call intoHL
|
||||
push hl \ pop ix
|
||||
; Bring back command string from DE to HL
|
||||
ex de, hl
|
||||
ld a, b ; cmd's length
|
||||
call addHL
|
||||
; Bring back rest of the command string from the stack
|
||||
pop hl ; <-- lvl 1
|
||||
call rdSep
|
||||
jp (ix)
|
||||
|
||||
@ -267,6 +266,89 @@ basINPUT:
|
||||
cp a ; ensure Z
|
||||
ret
|
||||
|
||||
basPEEK:
|
||||
call basDEEK
|
||||
ret nz
|
||||
ld d, 0
|
||||
call varAssign
|
||||
cp a ; ensure Z
|
||||
ret
|
||||
|
||||
basPOKE:
|
||||
call rdExpr
|
||||
ret nz
|
||||
; peek address in IX. Save it for later
|
||||
push ix ; --> lvl 1
|
||||
call rdSep
|
||||
call rdExpr
|
||||
push ix \ pop hl
|
||||
pop ix ; <-- lvl 1
|
||||
ret nz
|
||||
; Poke!
|
||||
ld (ix), l
|
||||
ret
|
||||
|
||||
basDEEK:
|
||||
call rdExpr
|
||||
ret nz
|
||||
; peek address in IX. Let's peek and put result in DE
|
||||
ld e, (ix)
|
||||
ld d, (ix+1)
|
||||
call rdSep
|
||||
ld a, (hl)
|
||||
call varChk
|
||||
ret nz ; not in variable range
|
||||
; All good assign
|
||||
call varAssign
|
||||
cp a ; ensure Z
|
||||
ret
|
||||
|
||||
basDOKE:
|
||||
call basPOKE
|
||||
ld (ix+1), h
|
||||
ret
|
||||
|
||||
basOUT:
|
||||
call rdExpr
|
||||
ret nz
|
||||
; out address in IX. Save it for later
|
||||
push ix ; --> lvl 1
|
||||
call rdSep
|
||||
call rdExpr
|
||||
push ix \ pop hl
|
||||
pop bc ; <-- lvl 1
|
||||
ret nz
|
||||
; Out!
|
||||
out (c), l
|
||||
cp a ; ensure Z
|
||||
ret
|
||||
|
||||
basIN:
|
||||
call rdExpr
|
||||
ret nz
|
||||
push ix \ pop bc
|
||||
ld d, 0
|
||||
in e, (c)
|
||||
call rdSep
|
||||
ld a, (hl)
|
||||
call varChk
|
||||
ret nz ; not in variable range
|
||||
; All good assign
|
||||
call varAssign
|
||||
cp a ; ensure Z
|
||||
ret
|
||||
|
||||
basSLEEP:
|
||||
call rdExpr
|
||||
ret nz
|
||||
push ix \ pop hl
|
||||
.loop:
|
||||
ld a, h ; 4T
|
||||
or l ; 4T
|
||||
ret z ; 5T
|
||||
dec hl ; 6T
|
||||
jr .loop ; 12T
|
||||
|
||||
; direct only
|
||||
basCmds1:
|
||||
.dw basBYE
|
||||
@ -285,4 +367,18 @@ basCmds2:
|
||||
.db "if", 0, 0, 0, 0
|
||||
.dw basINPUT
|
||||
.db "input", 0
|
||||
.dw basPEEK
|
||||
.db "peek", 0, 0
|
||||
.dw basPOKE
|
||||
.db "poke", 0, 0
|
||||
.dw basDEEK
|
||||
.db "deek", 0, 0
|
||||
.dw basDOKE
|
||||
.db "doke", 0, 0
|
||||
.dw basOUT
|
||||
.db "out", 0, 0, 0
|
||||
.dw basIN
|
||||
.db "in", 0, 0, 0, 0
|
||||
.dw basSLEEP
|
||||
.db "sleep", 0
|
||||
.db 0xff, 0xff, 0xff ; end of table
|
||||
|
@ -32,30 +32,6 @@ rdSep:
|
||||
inc a ; unset Z
|
||||
ret
|
||||
|
||||
; Find the first whitespace in (HL) and returns its index in A
|
||||
; Sets Z if whitespace is found, unset if end of string was found.
|
||||
; In the case where no whitespace was found, A returns the length of the string.
|
||||
fnWSIdx:
|
||||
push hl
|
||||
push bc
|
||||
ld b, 0
|
||||
.loop:
|
||||
ld a, (hl)
|
||||
call isSep
|
||||
jr z, .found
|
||||
or a
|
||||
jr z, .eos
|
||||
inc hl
|
||||
inc b
|
||||
jr .loop
|
||||
.eos:
|
||||
inc a ; unset Z
|
||||
.found: ; Z already set from isSep
|
||||
ld a, b
|
||||
pop bc
|
||||
pop hl
|
||||
ret
|
||||
|
||||
; Advance HL to the next separator or to the end of string.
|
||||
toSep:
|
||||
ld a, (hl)
|
||||
@ -85,3 +61,15 @@ rdWord:
|
||||
pop de
|
||||
pop af
|
||||
ret
|
||||
|
||||
; Read word from HL in SCRATCHPAD and then intepret that word as an expression.
|
||||
; Put the result in IX.
|
||||
; Z for success.
|
||||
rdExpr:
|
||||
ld de, SCRATCHPAD
|
||||
call rdWord
|
||||
push hl
|
||||
ex de, hl
|
||||
call parseExpr
|
||||
pop hl
|
||||
ret
|
||||
|
@ -27,6 +27,7 @@ are other recipes related to the RC2014:
|
||||
* [Accessing a MicroSD card](sdcard/README.md)
|
||||
* [Assembling binaries](zasm/README.md)
|
||||
* [Interfacing a PS/2 keyboard](ps2/README.md)
|
||||
* [Replace shell by a BASIC interpreter](basic/README.md)
|
||||
|
||||
## Recipe
|
||||
|
||||
|
10
recipes/rc2014/basic/Makefile
Normal file
10
recipes/rc2014/basic/Makefile
Normal file
@ -0,0 +1,10 @@
|
||||
TARGET = os.bin
|
||||
ZASM = ../../../tools/zasm.sh
|
||||
KERNEL = ../../../kernel
|
||||
APPS = ../../../apps
|
||||
|
||||
.PHONY: all
|
||||
all: $(TARGET)
|
||||
$(TARGET): glue.asm
|
||||
$(ZASM) $(KERNEL) $(APPS) < $< > $@
|
||||
|
46
recipes/rc2014/basic/README.md
Normal file
46
recipes/rc2014/basic/README.md
Normal file
@ -0,0 +1,46 @@
|
||||
# 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?
|
56
recipes/rc2014/basic/glue.asm
Normal file
56
recipes/rc2014/basic/glue.asm
Normal file
@ -0,0 +1,56 @@
|
||||
.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
|
||||
jp basStart
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user