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

Compare commits

...

4 Commits

Author SHA1 Message Date
Virgil Dupras
aad8efeff7 recipes/rc2014/basic: new recipe 2019-11-23 21:22:31 -05:00
Virgil Dupras
b7d4860acf basic: add in/out commands
Also, fixed the cmd matching algo to not accept partial matches. For example,
to stop matching "input" when the command was "in".
2019-11-23 20:38:56 -05:00
Virgil Dupras
7761cebb0a basic: add sleep command
also fix broken "peek"
2019-11-23 17:07:14 -05:00
Virgil Dupras
9602f9b983 basic: add peek/poke/deek/doke commands 2019-11-23 16:09:34 -05:00
7 changed files with 254 additions and 37 deletions

View File

@ -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.

View File

@ -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

View File

@ -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

View File

@ -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

View 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) < $< > $@

View 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?

View 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