1
0
mirror of https://github.com/hsoft/collapseos.git synced 2024-11-26 05:28:06 +11:00

Compare commits

...

9 Commits

Author SHA1 Message Date
James Stanley
1b8b113536 Fix filesystem in shell emulator
We now treat the block device as fixed-size rather than trying to grow it in response
to kernel activity.

Previously, if you tried to create 2 files in a row, only the first 1 would actually appear,
because the device only ever got larger when a byte was written immediately past the end of
the device.

Now we just let the kernel write bytes to the disk anywhere, so new files can be created even
when the previous file is not completely full.

Also, fix buffer overrun in reading filesystem image, and use a more idiomatic fgetc loop.
2019-12-12 16:58:17 -05:00
Virgil Dupras
948a06cb41 tools/tests: add missing doc about shell tests 2019-12-12 16:31:52 -05:00
Virgil Dupras
4f7a05e3b7 core: remove cpHLDE
It wasn't used much, so I replaced its use in the kernel with direct code
and moved the routine in apps/ed, the only other place where it was used.
2019-12-12 15:53:14 -05:00
Virgil Dupras
c002c69208 Include tools/tests/shell/test.cfs in repo
The order in which cfspack includes its file depend on the platform. To have
tests that reliably pass, test.cfs has to be committed in.
2019-12-12 14:49:09 -05:00
Virgil Dupras
9ab292a6d5 Add shell automated tests 2019-12-12 14:32:47 -05:00
Virgil Dupras
43f4c5200e basic: don't choke on ':' in '"' literals 2019-12-12 12:22:38 -05:00
Virgil Dupras
5b155a5c15 tools: use BASIC's new while loop
Also, increase STDIO_BUFSIZE to 0x40 so that those while loops work.
2019-12-12 12:04:56 -05:00
Virgil Dupras
3db38b0d89 basic: add while command 2019-12-12 11:17:10 -05:00
Virgil Dupras
51c977f2ed basic: allow multiple commands on the same line 2019-12-12 10:51:13 -05:00
41 changed files with 349 additions and 168 deletions

View File

@ -88,13 +88,22 @@ etc.) always to so in variable `A`.
Another is that whenever a number is expected, expressions, including the ones
with variables in it, work fine.
### One-liners
The `:` character, when not inside a `""` literal, allows you to cram more than
one instruction on the same line.
Things are special with `if`. All commands following a `if` are bound to that
`if`'s condition. `if 0 foo:bar` doesn't execute `bar`.
Another special thing is `goto`. A `goto` followed by `:` will have the commands
following the `:` before the goto occurs.
### Commands
There are two types of commands: normal and direct-only. The latter can only
be invoked in direct mode, not through a code listing.
`bye`: Direct-only. Quits BASIC
`list`: Direct-only. Prints all lines in the code listing, prefixing them
with their associated line number.
@ -117,11 +126,14 @@ specified as an argument. Errors out if line doesn't exist. Argument can be
an expression. If invoked in direct mode, `run` must be called to actually
run the line (followed by the next, and so on).
`if <cond> <cmd>`: If specified condition is true, execute the rest of the
`if <cond> <cmds>`: If specified condition is true, execute the rest of the
line. Otherwise, do nothing. For example, `if 2>1 print 12` prints `12` and `if
2<1 print 12` does nothing. The argument for this command is a "thruth
expression".
`while <cond> <cmds>`: As long as specified condition is true, execute specified
commands repeatedly.
`input [<prompt>]`: Prompts the user for a numerical value and puts that
value in `A`. The prompted value is evaluated as an expression and then stored.
The command takes an optional string literal parameter. If present, that string

View File

@ -46,7 +46,7 @@ basLDBAS:
call parseDecimal
jr nz, .notANumber
push ix \ pop de
call toSep
call toSepOrEnd
call rdSep
call bufAdd
pop hl ; <-- lvl 1

View File

@ -41,14 +41,14 @@ basLoop:
call parseDecimal
jr z, .number
ld de, basCmds1
call basCallCmd
call basCallCmds
jr z, basLoop
; Error
call basERR
jr basLoop
.number:
push ix \ pop de
call toSep
call toSepOrEnd
call rdSep
call bufAdd
jp nz, basERR
@ -110,6 +110,27 @@ basCallCmd:
call rdSep
jp (ix)
; Call a series of ':'-separated commands in (HL) using cmd table in (DE).
; Stop processing as soon as one command unsets Z.
basCallCmds:
; Commands are not guaranteed at all to preserve HL and DE, so we
; preserve them ourselves here.
push hl ; --> lvl 1
push de ; --> lvl 2
call basCallCmd
pop de ; <-- lvl 2
pop hl ; <-- lvl 1
ret nz
call toEnd
ret z ; no more cmds
; we met a ':', we have more cmds
inc hl
call basCallCmds
; move the the end of the string so that we don't run cmds following a
; ':' twice.
call strskip
ret
basERR:
ld hl, .sErr
call printstr
@ -154,7 +175,7 @@ basRUN:
call bufStr
ld de, basCmds2
push ix ; --> lvl 1
call basCallCmd
call basCallCmds
pop ix ; <-- lvl 1
jp nz, .err
call .maybeGOTO
@ -246,22 +267,49 @@ basGOTO:
ld (BAS_PNEXTLN), de
ret
basIF:
; evaluate truth condition at (HL) and set A to its value
; Z for success (but not truth!)
_basEvalCond:
push hl ; --> lvl 1. original arg
ld de, SCRATCHPAD
call rdWord
ex de, hl
call parseTruth
pop hl ; <-- lvl 1. restore
ret nz
ret
basIF:
call _basEvalCond
ret nz ; error
or a
ret z
; expr is true, execute next
; (HL) back to beginning of args, skip to next arg
call toSep
call toSepOrEnd
call rdSep
ret nz
ld de, basCmds2
jp basCallCmd
jp basCallCmds
basWHILE:
push hl ; --> lvl 1
call _basEvalCond
jr nz, .stop ; error
or a
jr z, .stop
ret z
; expr is true, execute next
; (HL) back to beginning of args, skip to next arg
call toSepOrEnd
call rdSep
ret nz
ld de, basCmds2
call basCallCmds
pop hl ; <-- lvl 1
jr basWHILE
.stop:
pop hl ; <-- lvl 1
ret
basINPUT:
; If our first arg is a string literal, spit it
@ -456,6 +504,8 @@ basCmds2:
.dw basGOTO
.db "if", 0
.dw basIF
.db "while", 0
.dw basWHILE
.db "input", 0
.dw basINPUT
.db "peek", 0

View File

@ -1,10 +1,16 @@
; Sets Z is A is ' ' or '\t' (whitespace), or ',' (arg sep)
; Whether A is a separator or end-of-string (null or ':')
isSepOrEnd:
or a
ret z
cp ':'
ret z
; continue to isSep
; Sets Z is A is ' ' or '\t' (whitespace)
isSep:
cp ' '
ret z
cp 0x09
ret z
cp ','
ret
; Expect at least one whitespace (0x20, 0x09) at (HL), and then advance HL
@ -23,8 +29,8 @@ rdSep:
ld a, (hl)
call isSep
jr z, .loop
or a ; cp 0
jp z, .fail
call isSepOrEnd
jp z, .fail ; unexpected EOL. fail
cp a ; ensure Z
ret
.fail:
@ -33,12 +39,28 @@ rdSep:
ret
; Advance HL to the next separator or to the end of string.
toSep:
toSepOrEnd:
ld a, (hl)
call isSep
call isSepOrEnd
ret z
inc hl
jr toSep
jr toSepOrEnd
; Advance HL to the end of the line, that is, either a null terminating char
; or the ':'.
; Sets Z if we met a null char, unset if we met a ':'
toEnd:
ld a, (hl)
or a
ret z
cp ':'
jr z, .havesep
inc hl
call skipQuoted
jr toEnd
.havesep:
inc a ; unset Z
ret
; Read (HL) until the next separator and copy it in (DE)
; DE is preserved, but HL is advanced to the end of the read word.
@ -47,9 +69,7 @@ rdWord:
push de
.loop:
ld a, (hl)
call isSep
jr z, .stop
or a
call isSepOrEnd
jr z, .stop
ld (de), a
inc hl

View File

@ -11,5 +11,22 @@ spitQuoted:
inc hl
cp '"'
ret z
or a
ret z
call stdioPutC
jr .loop
; Same as spitQuoted, but without the spitting
skipQuoted:
ld a, (hl)
cp '"'
ret nz
inc hl
.loop:
ld a, (hl)
inc hl
cp '"'
ret z
or a
ret z
jr .loop

View File

@ -33,6 +33,7 @@
.inc "core.asm"
.inc "lib/util.asm"
.inc "lib/parse.asm"
.inc "ed/util.asm"
.equ IO_RAMSTART USER_RAMSTART
.inc "ed/io.asm"
.equ BUF_RAMSTART IO_RAMEND

8
apps/ed/util.asm Normal file
View File

@ -0,0 +1,8 @@
; Compare HL with DE and sets Z and C in the same way as a regular cp X where
; HL is A and DE is X.
cpHLDE:
push hl
or a ;reset carry flag
sbc hl, de ;There is no 'sub hl, de', so we must use sbc
pop hl
ret

View File

@ -52,21 +52,12 @@ Let's try an example: You glue yourself a Collapse OS with a mmap starting at
could do to copy memory around:
> m=0xe000
> 10 getc
> 20 poke m a
> 30 m=m+1
> 40 if m<0xe004 goto 10
> run
> while m<0xe004 getc:poke m a:m=m+1
[enter "abcd"]
> bsel 3
> clear
> 10 getb
> 20 puth a
> run
61> run
62> run
63> run
64> bseek 2
> run
63> run
> i=0
> while i<4 getb:puth a:i=i+1
61626364> bseek 2
> getb:puth a
63> getb:puth a
64>

View File

@ -21,9 +21,7 @@ increase a number at memory address `0xa100`. First, compile it:
Now, we'll send that code to address `0xa000`:
> m=0xa000
> 10 getc
> 20 poke m a
> 30 if m<0xa008 goto 10
> while m<0xa008 getc:poke m a:m=m+1
(resulting binary is 8 bytes long)
Now, at this point, it's a bit delicate. To pipe your binary to your serial

View File

@ -57,15 +57,6 @@ intoIX:
pop ix
ret
; Compare HL with DE and sets Z and C in the same way as a regular cp X where
; HL is A and DE is X.
cpHLDE:
push hl
or a ;reset carry flag
sbc hl, de ;There is no 'sub hl, de', so we must use sbc
pop hl
ret
; Write the contents of HL in (DE)
; de and hl are preserved, so no pushing/popping necessary
writeHLinDE:

View File

@ -413,17 +413,17 @@ fsPlaceH:
; Sets Z according to whether HL is within bounds for file handle at (IX), that
; is, if it is smaller than file size.
fsWithinBounds:
push de
; file size
ld e, (ix+4)
ld d, (ix+5)
call cpHLDE
pop de
jr nc, .outOfBounds ; HL >= DE
ld a, h
cp (ix+5)
jr c, .within ; H < (IX+5)
jp nz, unsetZ ; H > (IX+5)
; H == (IX+5)
ld a, l
cp (ix+4)
jp nc, unsetZ ; L >= (IX+4)
.within:
cp a ; ensure Z
ret
.outOfBounds:
jp unsetZ ; returns
; Set size of file handle (IX) to value in HL.
; This writes directly in handle's metadata.

View File

@ -13,8 +13,10 @@
_mmapAddr:
push de
ld de, MMAP_LEN
call cpHLDE
or a ; reset carry flag
sbc hl, de
jr nc, .outOfBounds ; HL >= DE
add hl, de ; old HL value
ld de, MMAP_START
add hl, de
cp a ; ensure Z

View File

@ -27,7 +27,7 @@
; *** Consts ***
; Size of the readline buffer. If a typed line reaches this size, the line is
; flushed immediately (same as pressing return).
.equ STDIO_BUFSIZE 0x20
.equ STDIO_BUFSIZE 0x40
; *** Variables ***
; Line buffer. We read types chars into this buffer until return is pressed

View File

@ -40,7 +40,7 @@ jp aciaInt
; *** BASIC ***
; RAM space used in different routines for short term processing.
.equ SCRATCHPAD_SIZE 0x20
.equ SCRATCHPAD_SIZE STDIO_BUFSIZE
.equ SCRATCHPAD AT28W_RAMEND
.inc "lib/util.asm"
.inc "lib/ari.asm"

View File

@ -25,7 +25,7 @@ jp aciaInt
; *** BASIC ***
; RAM space used in different routines for short term processing.
.equ SCRATCHPAD_SIZE 0x20
.equ SCRATCHPAD_SIZE STDIO_BUFSIZE
.equ SCRATCHPAD STDIO_RAMEND
.inc "lib/util.asm"
.inc "lib/ari.asm"

View File

@ -24,7 +24,7 @@ jp init
; *** BASIC ***
; RAM space used in different routines for short term processing.
.equ SCRATCHPAD_SIZE 0x20
.equ SCRATCHPAD_SIZE STDIO_BUFSIZE
.equ SCRATCHPAD STDIO_RAMEND
.inc "lib/util.asm"
.inc "lib/ari.asm"

View File

@ -48,7 +48,7 @@ jp aciaInt
; *** BASIC ***
; RAM space used in different routines for short term processing.
.equ SCRATCHPAD_SIZE 0x20
.equ SCRATCHPAD_SIZE STDIO_BUFSIZE
.equ SCRATCHPAD FS_RAMEND
.inc "lib/util.asm"
.inc "lib/ari.asm"

View File

@ -1,10 +1,10 @@
; 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
; Kernel RAMEND last check: 0x98f3
; Kernel RAMEND last check: 0x9933
; We allocate at least 0x100 bytes for the stack, which is why we have this
; threshold.
.equ RAMEND 0x9a00
.equ RAMEND 0x9b00
.equ USER_CODE RAMEND ; in sync with user.h
.equ ACIA_CTL 0x80 ; Control and status. RS off.
.equ ACIA_IO 0x81 ; Transmit. RS on.
@ -67,7 +67,7 @@ jp aciaInt
; *** BASIC ***
; RAM space used in different routines for short term processing.
.equ SCRATCHPAD_SIZE 0x20
.equ SCRATCHPAD_SIZE STDIO_BUFSIZE
.equ SCRATCHPAD FS_RAMEND
.inc "lib/util.asm"
.inc "lib/ari.asm"

View File

@ -1,4 +1,4 @@
.org 0x9a00
.org 0x9b00
; *** JUMP TABLE ***
.equ strncmp 0x03

View File

@ -27,7 +27,7 @@
; *** BASIC ***
; RAM space used in different routines for short term processing.
.equ SCRATCHPAD_SIZE 0x20
.equ SCRATCHPAD_SIZE STDIO_BUFSIZE
.equ SCRATCHPAD STDIO_RAMEND
.inc "lib/util.asm"
.inc "lib/ari.asm"

View File

@ -29,7 +29,7 @@
; *** BASIC ***
; RAM space used in different routines for short term processing.
.equ SCRATCHPAD_SIZE 0x20
.equ SCRATCHPAD_SIZE STDIO_BUFSIZE
.equ SCRATCHPAD STDIO_RAMEND
.inc "lib/util.asm"
.inc "lib/ari.asm"

View File

@ -77,7 +77,7 @@
; *** BASIC ***
; RAM space used in different routines for short term processing.
.equ SCRATCHPAD_SIZE 0x20
.equ SCRATCHPAD_SIZE STDIO_BUFSIZE
.equ SCRATCHPAD FS_RAMEND
.inc "lib/util.asm"
.inc "lib/ari.asm"

View File

@ -40,7 +40,7 @@
; *** BASIC ***
; RAM space used in different routines for short term processing.
.equ SCRATCHPAD_SIZE 0x20
.equ SCRATCHPAD_SIZE STDIO_BUFSIZE
.equ SCRATCHPAD STDIO_RAMEND
.inc "lib/util.asm"
.inc "lib/ari.asm"

View File

@ -21,17 +21,18 @@ int main(int argc, char **argv)
}
int fd = open(argv[1], O_RDWR|O_NOCTTY);
char s[3];
char s[0x30];
sendcmdp(fd, "i=0");
sprintf(s, "while i<0x%04x getb:puth a:i=i+1", bytecount);
sendcmd(fd, s);
for (int i=0; i<bytecount; i++) {
sendcmd(fd, "getb");
read(fd, s, 2); // read prompt
sendcmd(fd, "puth a");
read(fd, s, 2); // read hex pair
s[2] = 0; // null terminate
unsigned char c = strtol(s, NULL, 16);
putchar(c);
read(fd, s, 2); // read prompt
}
read(fd, s, 2); // read prompt
return 0;
}

View File

@ -25,6 +25,16 @@ 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
the real thing.
By default, the shell initialized itself with a CFS device containing the
contents of `cfsin/` at launch (it's packed on the fly). You can specify an
alternate CFS device file (it has to be packaed already) through the `-f` flag.
By default, the shell runs interactively, but you can also pipe contents through
stdin instead. The contents will be interpreted exactly as if you had typed it
yourself and the result will be spit in stdout (it includes your typed in
contents because the Collapse OS console echoes back every character that is
sent to it.). This feature is useful for automated tests in `tools/tests/shell`.
## zasm
`zasm/zasm` is `apps/zasm` wrapped in an emulator. It is quite central to the

View File

@ -70,7 +70,7 @@
; *** BASIC ***
; RAM space used in different routines for short term processing.
.equ SCRATCHPAD_SIZE 0x20
.equ SCRATCHPAD_SIZE STDIO_BUFSIZE
.equ SCRATCHPAD FS_RAMEND
.inc "lib/util.asm"
.inc "lib/ari.asm"

View File

@ -1,5 +1,6 @@
#include <stdint.h>
#include <stdio.h>
#include <unistd.h>
#include <termios.h>
#include "../emul.h"
#include "shell-bin.h"
@ -40,7 +41,6 @@
#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;
@ -61,16 +61,13 @@ static uint8_t iord_fsdata()
fprintf(stderr, "Reading FSDEV in the middle of an addr op (%d)\n", fsdev_ptr);
return 0;
}
if (fsdev_ptr < fsdev_size) {
if (fsdev_ptr < MAX_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;
}
}
@ -79,11 +76,9 @@ 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);
} else if (fsdev_ptr >= MAX_FSDEV_SIZE) {
fprintf(stderr, "Out of bounds FSDEV addr request at %d / %d\n", fsdev_ptr, MAX_FSDEV_SIZE);
return 2;
} else if (fsdev_ptr == fsdev_size) {
return 1;
} else {
return 0;
}
@ -104,18 +99,11 @@ static void iowr_fsdata(uint8_t val)
fprintf(stderr, "Writing to FSDEV in the middle of an addr op (%d)\n", fsdev_ptr);
return;
}
if (fsdev_ptr < fsdev_size) {
if (fsdev_ptr < MAX_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);
}
@ -135,27 +123,52 @@ static void iowr_fsaddr(uint8_t val)
}
}
int main()
int main(int argc, char *argv[])
{
// 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);
FILE *fp = NULL;
while (1) {
int c = getopt(argc, argv, "f:");
if (c < 0) {
break;
}
switch (c) {
case 'f':
fp = fopen(optarg, "r");
if (fp == NULL) {
fprintf(stderr, "Can't open %s\n", optarg);
return 1;
}
break;
default:
fprintf(stderr, "Usage: shell [-f fsdev]\n");
return 1;
}
}
// Setup fs blockdev
if (fp == NULL) {
fp = popen("../cfspack/cfspack cfsin", "r");
if (fp == NULL) {
fprintf(stderr, "Can't initialize filesystem. Leaving blank.\n");
}
}
if (fp != NULL) {
fprintf(stderr, "Initializing filesystem\n");
int i = 0;
int c;
while ((c = fgetc(fp)) != EOF && i < MAX_FSDEV_SIZE) {
fsdev[i++] = c & 0xff;
}
if (i == MAX_FSDEV_SIZE) {
fprintf(stderr, "Filesytem image too large.\n");
return 1;
}
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.
bool tty = isatty(fileno(stdin));
struct termios termInfo;
if (tty) {
// Turn echo off: the shell takes care of its own echoing.
if (tcgetattr(0, &termInfo) == -1) {
printf("Can't setup terminal.\n");
return 1;
@ -163,6 +176,7 @@ int main()
termInfo.c_lflag &= ~ECHO;
termInfo.c_lflag &= ~ICANON;
tcsetattr(0, TCSAFLUSH, &termInfo);
}
Machine *m = emul_init();
@ -182,10 +196,12 @@ int main()
while (running && emul_step());
if (tty) {
printf("Done!\n");
termInfo.c_lflag |= ECHO;
termInfo.c_lflag |= ICANON;
tcsetattr(0, TCSAFLUSH, &termInfo);
emul_printdebug();
}
return 0;
}

View File

@ -28,22 +28,18 @@ int main(int argc, char **argv)
}
int fd = open(argv[1], O_RDWR|O_NOCTTY);
char s[0x20];
char s[0x30];
sprintf(s, "m=0x%04x", memptr);
sendcmdp(fd, s);
sprintf(s, "while m<0x%04x peek m:puth a:m=m+1", memptr+bytecount);
sendcmd(fd, s);
read(fd, s, 2); // read prompt
for (int i=0; i<bytecount; i++) {
sendcmd(fd, "peek m");
read(fd, s, 2); // read prompt
sendcmd(fd, "puth a");
read(fd, s, 2); // read hex pair
s[2] = 0; // null terminate
unsigned char c = strtol(s, NULL, 16);
putchar(c);
read(fd, s, 2); // read prompt
sendcmd(fd, "m=m+1");
read(fd, s, 2); // read prompt
}
read(fd, s, 2); // read prompt
return 0;
}

View File

@ -1,7 +1,16 @@
EMULDIR = ../emul
CFSPACK = ../cfspack/cfspack
.PHONY: run
run:
$(MAKE) -C $(EMULDIR) zasm/zasm runbin/runbin
$(MAKE) -C $(EMULDIR) zasm/zasm runbin/runbin shell/shell
cd unit && ./runtests.sh
cd zasm && ./runtests.sh
cd shell && ./runtests.sh
$(CFSPACK):
$(MAKE) -C ../cfspack
.PHONY: cfs
cfs: $(CFSPACK)
$(CFSPACK) shell/cfsin > shell/test.cfs

View File

@ -40,3 +40,10 @@ However, there are tricks.
1. Run `unit/runtests.sh <name of file to test>` to target a specific test unit.
2. Insert a `halt` to see the value of `A` at any given moment: it will be your
reported error code (if 0, runbin will report a success).
## shell
Those tests are in the form of shell "replay" files. Every ".replay" file in
this folder contains the contents to type in the shell. That contents is piped
through the shell and the output is then compared with the corresponding
".expected" file. If they match exactly, the test passes.

View File

@ -0,0 +1 @@
Hello Bar!

View File

@ -0,0 +1 @@
Hello Foo!

View File

@ -0,0 +1,5 @@
Collapse OS
> fls
bar
foo
>

View File

@ -0,0 +1 @@
fls

View File

@ -0,0 +1,4 @@
Collapse OS
> print 42
42
>

View File

@ -0,0 +1 @@
print 42

28
tools/tests/shell/runtests.sh Executable file
View File

@ -0,0 +1,28 @@
#!/bin/sh -e
EMULDIR=../../emul
SHELL=../../emul/shell/shell
replay() {
fn=$1
replayfn=${fn%.*}.expected
ACTUAL=$("${SHELL}" -f test.cfs < "${fn}" 2> /dev/null)
EXPECTED=$(cat ${replayfn})
if [ "$ACTUAL" = "$EXPECTED" ]; then
echo ok
else
echo different. Whole output:
echo "${ACTUAL}"
exit 1
fi
}
if [ ! -z $1 ]; then
replay $1
exit 0
fi
for fn in *.replay; do
echo "Replaying ${fn}"
replay "${fn}"
done

BIN
tools/tests/shell/test.cfs Normal file

Binary file not shown.

View File

@ -5,8 +5,6 @@
jp test
.inc "core.asm"
dummyLabel:
testNum: .db 1
@ -48,30 +46,11 @@ test:
; test that "@" is updated by a .org directive
ld hl, AFTER_ORG
ld de, 0x1234
call cpHLDE
or a ; clear carry
sbc hl, de
jp nz, fail
call nexttest
; *** cpHLDE ***
ld hl, 0x42
ld de, 0x42
call cpHLDE
jp nz, fail
jp c, fail
call nexttest
ld de, 0x4242
call cpHLDE
jp z, fail
jp nc, fail
call nexttest
ld hl, 0x4243
call cpHLDE
jp z, fail
jp c, fail
call nexttest
; success
xor a
halt

View File

@ -0,0 +1,42 @@
jp test
.inc "ed/util.asm"
test:
ld sp, 0xffff
; *** cpHLDE ***
ld hl, 0x42
ld de, 0x42
call cpHLDE
jp nz, fail
jp c, fail
call nexttest
ld de, 0x4242
call cpHLDE
jp z, fail
jp nc, fail
call nexttest
ld hl, 0x4243
call cpHLDE
jp z, fail
jp c, fail
call nexttest
; success
xor a
halt
testNum: .db 1
nexttest:
ld a, (testNum)
inc a
ld (testNum), a
ret
fail:
ld a, (testNum)
halt

View File

@ -5,10 +5,8 @@
#include "common.h"
/* Push specified file to specified device **running the BASIC shell** and verify
/* Push specified file to specified device running the BASIC shell and verify
* that the sent contents is correct.
*
* Note: running this will clear the current BASIC listing on the other side.
*/
int main(int argc, char **argv)
@ -38,20 +36,12 @@ int main(int argc, char **argv)
}
rewind(fp);
int fd = open(argv[1], O_RDWR|O_NOCTTY);
char s[0x20];
char s[0x40];
sprintf(s, "m=0x%04x", memptr);
sendcmdp(fd, s);
sprintf(s, "while m<0x%04x getc:puth a:poke m a:m=m+1", memptr+bytecount);
sendcmd(fd, s);
// Send program
sendcmdp(fd, "clear");
sendcmdp(fd, "1 getc");
sendcmdp(fd, "2 puth a");
sendcmdp(fd, "3 poke m a");
sendcmdp(fd, "4 m=m+1");
sprintf(s, "5 if m<0x%04x goto 1", memptr+bytecount);
sendcmdp(fd, s);
sendcmd(fd, "run");
int returncode = 0;
while (fread(s, 1, 1, fp)) {
putchar('.');