commit 99bd77d966f667d536fc91379cc59e5481aa4144 Author: Joerg Deckert Date: Tue Mar 25 10:40:35 2014 +0100 Initial checkin of spinix (original version 1.33) diff --git a/build_bin b/build_bin new file mode 100644 index 0000000..166fe9f --- /dev/null +++ b/build_bin @@ -0,0 +1,5 @@ +cd src +bstc -b -O cgru $1.spin +cp $1.binary ../root/bin/$1 +rm $1.binary +cd .. diff --git a/build_boot b/build_boot new file mode 100644 index 0000000..14f1593 --- /dev/null +++ b/build_boot @@ -0,0 +1,6 @@ +# Build the the boot program +cd src +bstc -b -O cgru boot.spin +cp boot.binary .. +rm boot.binary +cd .. diff --git a/build_c b/build_c new file mode 100644 index 0000000..6303260 --- /dev/null +++ b/build_c @@ -0,0 +1,37 @@ + +# Build filetest +cd filetest +cp ../src/spinix.* . +make clean +make +make binary +cd .. + +# Build fibo +cd cdemos +cd fibo +cp ../../src/spinix.* . +make clean +./buildit +cd ../.. + +# Build the dhrystone benchmark +cd cdemos +cd dry +cp ../../src/spinix.* . +make clean +./buildit +cd ../.. + +# Build a simple hello world program +cd cdemos +cd hello +cp ../../src/spinix.* . +make clean +./buildit +cd ../.. + +# Build the chess program +cd chess +./buildit +cd .. diff --git a/build_spinix b/build_spinix new file mode 100644 index 0000000..ded247c --- /dev/null +++ b/build_spinix @@ -0,0 +1,81 @@ +# Clean directories and remake them +rm -rf root +mkdir root +mkdir root/bin +mkdir root/tmp +mkdir root/manpages +mkdir root/devel +mkdir root/demos +mkdir root/forth +mkdir root/scripts + +# Build the boot program +./build_boot + +# Copy license and startup script to root +cp license.txt root +cp src/_shellrc root/bin + +# Generate _sysparm and copy to root +cd src +date +%s >_sysparm +echo -6 3 0 >>_sysparm +echo 0 0 0 >>_sysparm +echo "GPWD /" >>_sysparm +echo "LSCRIPT_FILE /bin/_shellrc" >>_sysparm +echo "P# 0" >>_sysparm +cd .. +cp src/_sysparm root + +# Build the bin apps +./build_src + +# Copy the devel files +cp devel/* root/devel + +# Copy the man pages +cp manpages/* root/manpages + +# Build lerner +cd lerner +cp ../src/sysdefs.spin . +cp ../src/exit.spin . +bstc -b -O cgru lerner.spin +cp lerner.binary ../root/demos/lerner +rm *.binary +cd .. + +# Build vga512 +cd vga512 +cp ../src/sysdefs.spin . +cp ../src/exit.spin . +bstc -b VGA_512x384_Bitmap_Demo.spin +cp VGA_512x384_Bitmap_Demo.binary ../root/demos/vga512 +rm *.binary +cd .. + +# Move vgatdemo from bin to demos +mv root/bin/vgatdemo root/demos + +# Copy the C demos +cp cdemos/dry/dry.binary root/demos/dry +cp cdemos/fibo/fibo.binary root/demos/fibo +cp cdemos/hello/hello.binary root/demos/hello + +# Copy C filetest program +cp filetest/filetest.binary root/demos/filetest + +# Copy C chess program +cp chess/chess.binary root/demos/chess + +# Build pfth and copy the Forth files +cd pfth +bstc -b ospfth.spin +cp ospfth.binary ../root/bin/pfth +cp *.fth ../root/forth +cp *.txt ../root/forth +mv ../root/forth/_startup.fth ../root +cd .. + +# Copy the script files +cp scripts/* root/scripts diff --git a/build_src b/build_src new file mode 100644 index 0000000..eb28fb9 --- /dev/null +++ b/build_src @@ -0,0 +1,61 @@ +# Build the bin apps +./build_bin alias +./build_bin cat +./build_bin chmod +./build_bin config +./build_bin cp +./build_bin date +./build_bin declare +./build_bin diag +./build_bin diff +./build_bin dos2unix +./build_bin ed +./build_bin export +./build_bin ted +./build_bin grep +./build_bin halt +./build_bin head +./build_bin history +./build_bin let +./build_bin linefeed +./build_bin ls +./build_bin man +./build_bin mkdir +./build_bin more +./build_bin mv +./build_bin od +./build_bin printenv +./build_bin rb +./build_bin reboot +./build_bin rm +./build_bin sb +./build_bin set +./build_bin shell +./build_bin spasm +./build_bin spinit +./build_bin splink +./build_bin tail +./build_bin tar +./build_bin test +./build_bin touch +./build_bin unalias +./build_bin unix2dos +./build_bin unset +./build_bin vi +./build_bin vgatdemo +./build_bin wc + +# Copy the shell scripts +cp src/spc root/bin + +# Copy text files +cp src/*.txt root/bin + +# Build files for spasm +cd src +grep PUB clibsd.spin >../root/devel/clibsd.spn +cp ../root/devel/clibsd.spn ../root/devel/clibsd.spa +bstc -b clibsd.spin +cp clibsd.binary ../root/devel/clibsd.bin +rm clibsd.binary +cd .. diff --git a/cdemos/common/common.mk b/cdemos/common/common.mk new file mode 100644 index 0000000..99a92d7 --- /dev/null +++ b/cdemos/common/common.mk @@ -0,0 +1,128 @@ +# ######################################################### +# This makefile fragment builds LMM/XMM/XMMC demo programs +# +# To use it, define: +# PROPLIB to be the path to this directory +# NAME to be the name of project +# - this is used to create the final program $(NAME).elf +# OBJS to be the object files needed for the project +# MODEL to lmm, xmm, or xmmc +# CFLAGS to be desired CFLAGS +# +# Then set up a default "all" target (normally this will be +# all: $(NAME).elf +# and finally +# include $(PROPLIB)/demo.mk +# +# Copyright (c) 2011 Parallax Inc. +# All rights MIT licensed +# ######################################################### + +# where we installed the propeller binaries and libraries +PREFIX = /opt/parallax + +ifndef MODEL +MODEL=lmm +endif + +ifndef BOARD +BOARD=$(PROPELLER_LOAD_BOARD) +endif + +ifneq ($(BOARD),) +BOARDFLAG=-b$(BOARD) +endif + +ifneq ($(CHIP),) +CHIPFLAG = -m$(CHIP) +endif + +CFLAGS_NO_MODEL := $(CFLAGS) $(CHIPFLAG) +CFLAGS += -m$(MODEL) $(CHIPFLAG) +CXXFLAGS += $(CFLAGS) +LDFLAGS += $(CFLAGS) -fno-exceptions -fno-rtti + +ifneq ($(LDSCRIPT),) +LDFLAGS += -T $(LDSCRIPT) +endif + +# basic gnu tools +CC = propeller-elf-gcc +CXX = propeller-elf-g++ +LD = propeller-elf-ld +AS = propeller-elf-as +AR = propeller-elf-ar +OBJCOPY = propeller-elf-objcopy +LOADER = propeller-load +LOADER2 = p2load + +# BSTC program +BSTC=bstc +SPINDIR=. + +ifneq ($(NAME),) +$(NAME).elf: $(OBJS) + $(CC) $(LDFLAGS) -o $@ $(OBJS) $(LIBS) +endif + +ifneq ($(LIBNAME),) +lib$(LIBNAME).a: $(OBJS) + $(AR) rs $@ $(OBJS) +endif + +%.o: %.c + $(CC) $(CFLAGS) -o $@ -c $< + +%.o: %.cpp + $(CC) $(CXXFLAGS) -o $@ -c $< + +%.o: %.s + $(CC) -o $@ -c $< + +# +# a .cog program is an object file that contains code intended to +# run in a COG separate from the main program; i.e., it's a COG +# driver that the linker will place in the .text section. +# +%.cog: %.c + $(CC) $(CFLAGS_NO_MODEL) -mcog -r -o $@ $< + $(OBJCOPY) --localize-text --rename-section .text=$@ $@ + +%.cog: %.cogc + $(CC) $(CFLAGS_NO_MODEL) -mcog -xc -r -o $@ $< + $(OBJCOPY) --localize-text --rename-section .text=$@ $@ + +# +# a .ecog program is an object file that contains code intended to +# run in a COG separate from the main program; i.e., it's a COG +# driver that the linker will place in the .drivers section which +# gets loaded to high EEPROM space above 0x8000. +# +%.ecog: %.c + $(CC) $(CFLAGS_NO_MODEL) -mcog -r -o $@ $< + $(OBJCOPY) --localize-text --rename-section .text=$@ $@ + +%.ecog: %.ecogc + $(CC) $(CFLAGS_NO_MODEL) -mcog -xc -r -o $@ $< + $(OBJCOPY) --localize-text --rename-section .text=$@ $@ + +%.binary: %.elf + $(LOADER) -s $< + +%.dat: $(SPINDIR)/%.spin + $(BSTC) -Ox -c -o $(basename $@) $< + +%_firmware.o: %.dat + $(OBJCOPY) -I binary -B propeller -O $(CC) $< $@ + +clean: + rm -f *.o *.elf *.a *.cog *.ecog *.binary + +# +# how to run +run: $(NAME).elf + $(LOADER) $(BOARDFLAG) $(NAME).elf -r -t + +run2: $(NAME).elf + $(LOADER2) $(NAME).elf -t +# diff --git a/cdemos/dry/Makefile b/cdemos/dry/Makefile new file mode 100644 index 0000000..5632032 --- /dev/null +++ b/cdemos/dry/Makefile @@ -0,0 +1,56 @@ +# ######################################################### +# This makefile builds the dhrystone program +# +# At the moment only the LMM model works. +# +# To build with LMM model, enter $ make clean; make +# To build with XMM model, enter $ make clean; make MODEL=xmm +# To build with XMMC model, enter $ make clean; make MODEL=xmmc +# +# Copyright (c) 2011 Parallax, Inc. +# All rights MIT licensed +# ######################################################### + +# Common make +COMMON = ../common + +ifndef OPT +OPT=-O0 +endif + +ifndef PASSES +PASSES=3000 +endif + +# +# objects for this program +# + +NAME = dry +OBJS = dry1.o dry2.o spinix.o + +# default build target +all: $(NAME).elf + +# flags to use +# MSC_CLOCK selects the clock() function for timing +# printf=__simple_printf makes sure we use the small printf +# -DINTEGER_ONLY removes floating point + +DEFINES = -Dprintf=__simple_printf -DMSC_CLOCK -DINTEGER_ONLY -DFIXED_NUMBER_OF_PASSES=$(PASSES) +CFLAGS = $(OPT) $(DEFINES) + +# +# include the fragment that defines most of everything +# +include $(COMMON)/common.mk + +dry1.o: dry.c + $(CC) $(CFLAGS) -o $@ -c $< + +dry2.o: dry.c + $(CC) $(CFLAGS) -DPASS2 -o $@ -c $< + +spinix.o: spinix.c + $(CC) $(CFLAGS) -o $@ -c $< + diff --git a/cdemos/dry/buildit b/cdemos/dry/buildit new file mode 100644 index 0000000..809f279 --- /dev/null +++ b/cdemos/dry/buildit @@ -0,0 +1,2 @@ +make +propeller-load -s -D clkfreq=1347436867 -D clkmode=98 dry.elf diff --git a/cdemos/dry/dry.c b/cdemos/dry/dry.c new file mode 100644 index 0000000..8377dec --- /dev/null +++ b/cdemos/dry/dry.c @@ -0,0 +1,1053 @@ +#include +#include +#include +#include "spinix.h" + +#ifdef NOTDEFINED /* To compile and run this file, say "sh dry.c" */ + case $0 in + *.c) ;; + sh) echo 'Use "sh dry.c", not "sh < dry.c"' >&2; exit 1;; + *) echo 'Filename must end in ".c"' >&2; exit 1;; + esac + + echo "${CC=cc} -c ${CFLAGS} $0 -o dry1.o" + ${CC} -c ${CFLAGS} $0 -o dry1.o || exit 1 + echo "${CC} -DPASS2 ${CFLAGS} $0 dry1.o ${LFLAGS} -o dry2" + ${CC} -DPASS2 ${CFLAGS} $0 dry1.o ${LFLAGS} -o dry2 || exit 1 + ./dry2 ${1-50000} 2>/dev/null + echo "${CC=cc} -c -DREG ${CFLAGS} $0 -o dry1.o" + ${CC} -c -DREG ${CFLAGS} $0 -o dry1.o || exit 1 + echo "${CC} -DPASS2 -DREG ${CFLAGS} $0 dry1.o ${LFLAGS} -o dry2nr" + ${CC} -DPASS2 -DREG ${CFLAGS} $0 dry1.o ${LFLAGS} -o dry2nr || exit 1 + ./dry2nr ${1-50000} 2>/dev/null + echo "${CC=cc} -c -O ${CFLAGS} $0 -o dry1.o" + ${CC} -c -O ${CFLAGS} $0 -o dry1.o || exit 1 + echo "${CC} -DPASS2 -O ${CFLAGS} $0 dry1.o ${LFLAGS} -o dry2o" + ${CC} -DPASS2 -O ${CFLAGS} $0 dry1.o ${LFLAGS} -o dry2o || exit 1 + ./dry2o ${1-50000} 2>/dev/null + rm -f dry1.o + + exit 0 +#endif + +/****************** "DHRYSTONE" Benchmark Program ***************************/ +#define Version "C, Version 2.2" +/* File: dhry_1.c (part 2 of 3) + * Author: Reinhold P. Weicker + * Siemens Nixdorf, Paderborn/Germany + * weicker@specbench.org + * Date: May 25, 1988 + * Modified: Steven Pemberton, CWI, Amsterdam; Steven.Pemberton@cwi.nl + * Date: October, 1993; March 1995 + * Included both files into one source, that gets compiled + * in two passes. Made program auto-compiling, and auto-running, + * and generally made it much easier to use. + * + * Original Version (in Ada) published in + * "Communications of the ACM" vol. 27., no. 10 (Oct. 1984), + * pp. 1013 - 1030, together with the statistics + * on which the distribution of statements etc. is based. + * + * In this C version, the following C library functions are used: + * - strcpy, strcmp (inside the measurement loop) + * - printf, scanf (outside the measurement loop) + * In addition, Berkeley UNIX system calls "times ()" or "time ()" + * are used for execution time measurement. For measurements + * on other systems, these calls have to be changed. + * + * Collection of Results: + * Reinhold Weicker (address see above) and + * + * Rick Richardson + * PC Research. Inc. + * 94 Apple Orchard Drive + * Tinton Falls, NJ 07724 + * Phone: (201) 389-8963 (9-17 EST) + * Usenet: ...!uunet!pcrat!rick + * + * Please send results to Rick Richardson and/or Reinhold Weicker. + * Complete information should be given on hardware and software used. + * Hardware information includes: Machine type, CPU, type and size + * of caches; for microprocessors: clock frequency, memory speed + * (number of wait states). + * Software information includes: Compiler (and runtime library) + * manufacturer and version, compilation switches, OS version. + * The Operating System version may give an indication about the compiler; + * Dhrystone itself performs no OS calls in the measurement loop. + * + * The complete output generated by the program should be mailed + * such that at least some checks for correctness can be made. + * + *************************************************************************** + * + * Defines: The following "Defines" are possible: + * -DREG (default: Not defined) + * As an approximation to what an average C programmer + * might do, causes the "register" storage class to be applied + * - for local variables, if they are used (dynamically) + * five or more times + * - for parameters if they are used (dynamically) + * six or more times + * Note that an optimal "register" strategy is + * compiler-dependent, and that "register" declarations + * do not necessarily lead to faster execution. + * -DNOSTRUCTASSIGN (default: Not defined) + * Define if the C compiler does not support + * assignment of structures. + * -DNOENUMS (default: Not defined) + * Define if the C compiler does not support + * enumeration types. + * -DTIMES (default) + * -DTIME + * The "times" function of UNIX (returning process times) + * or the "time" function (returning wallclock time) + * is used for measurement. + * For single user machines, "time ()" is adequate. For + * multi-user machines where you cannot get single-user + * access, use the "times ()" function. If you have + * neither, use a stopwatch in the dead of night. + * "printf"s are provided marking the points "Start Timer" + * and "Stop Timer". DO NOT use the UNIX "time(1)" + * command, as this will measure the total time to + * run this program, which will (erroneously) include + * the time to allocate storage (malloc) and to perform + * the initialization. + * -DHZ=nnn + * In Berkeley UNIX, the function "times" returns process + * time in 1/HZ seconds, with HZ = 60 for most systems. + * CHECK YOUR SYSTEM DESCRIPTION BEFORE YOU JUST APPLY + * A VALUE. + * + *************************************************************************** + * + * History: Version C/2.1 was made for two reasons: + * + * 1) There was an obvious need for a common C version of + * Dhrystone, since C is at present the most popular system + * programming language for the class of processors + * (microcomputers, minicomputers) where Dhrystone is used most. + * There should be, as far as possible, only one C version of + * Dhrystone such that results can be compared without + * restrictions. In the past, the C versions distributed + * by Rick Richardson (Version 1.1) and by Reinhold Weicker + * had small (though not significant) differences. + * + * 2) As far as it is possible without changes to the Dhrystone + * statistics, optimizing compilers should be prevented from + * removing significant statements. + * + * This C version has been developed in cooperation with + * Rick Richardson (Tinton Falls, NJ), it incorporates many + * ideas from the "Version 1.1" distributed previously by + * him over the UNIX network Usenet. + * I also thank Chaim Benedelac (National Semiconductor), + * David Ditzel (SUN), Earl Killian and John Mashey (MIPS), + * Alan Smith and Rafael Saavedra-Barrera (UC at Berkeley) + * for their help with comments on earlier versions of the + * benchmark. + * + * Changes: In the initialization part, this version follows mostly + * Rick Richardson's version distributed via Usenet, not the + * version distributed earlier via floppy disk by Reinhold Weicker. + * As a concession to older compilers, names have been made + * unique within the first 8 characters. + * Inside the measurement loop, this version follows the + * version previously distributed by Reinhold Weicker. + * + * At several places in the benchmark, code has been added, + * but within the measurement loop only in branches that + * are not executed. The intention is that optimizing compilers + * should be prevented from moving code out of the measurement + * loop, or from removing code altogether. Since the statements + * that are executed within the measurement loop have NOT been + * changed, the numbers defining the "Dhrystone distribution" + * (distribution of statements, operand types and locality) + * still hold. Except for sophisticated optimizing compilers, + * execution times for this version should be the same as + * for previous versions. + * + * Since it has proven difficult to subtract the time for the + * measurement loop overhead in a correct way, the loop check + * has been made a part of the benchmark. This does have + * an impact - though a very minor one - on the distribution + * statistics which have been updated for this version. + * + * All changes within the measurement loop are described + * and discussed in the companion paper "Rationale for + * Dhrystone version 2". + * + * Because of the self-imposed limitation that the order and + * distribution of the executed statements should not be + * changed, there are still cases where optimizing compilers + * may not generate code for some statements. To a certain + * degree, this is unavoidable for small synthetic benchmarks. + * Users of the benchmark are advised to check code listings + * whether code is generated for all statements of Dhrystone. + * + * Version 2.1 is identical to version 2.0 distributed via + * the UNIX network Usenet in March 1988 except that it corrects + * some minor deficiencies that were found by users of version 2.0. + * The only change within the measurement loop is that a + * non-executed "else" part was added to the "if" statement in + * Func_3, and a non-executed "else" part removed from Proc_3. + * + * Version C/2.2, Steven Pemberton, October 1993 + * Functionally, identical to version 2.2; the changes are in + * how you compile and use it: + * - Everything is in one file now, but compiled in 2 passes + * - Compile (and run) by running the file through the shell: 'sh dhry.c" + * - Uses the system definition of HZ if one can be found + * - HZ must be defined, otherwise it won't compile (no defaults here) + * - The (uninteresting) output is printed to stderr (dhry2 > /dev/null) + * - The number of loops is passed as a parameter, rather than read + * (dhry2 500000) + * - If the number of loops is insufficient to get a good result, + * it repeats it with loops*10 until it is enough (rather than just + * stopping) + * - Output says which sort of clock it is using, and the HZ value + * - You can use -DREG instead of the -DREG=register of previous versions + * - Some stylistic cleanups. + * + *************************************************************************** + * + * Compilation model and measurement (IMPORTANT): + * + * The following "ground rules" apply for measurements: + * - Separate compilation + * - No procedure merging + * - Otherwise, compiler optimizations are allowed but should be indicated + * - Default results are those without register declarations + * See the companion paper "Rationale for Dhrystone Version 2" for a more + * detailed discussion of these ground rules. + * + * For 16-Bit processors (e.g. 80186, 80286), times for all compilation + * models ("small", "medium", "large" etc.) should be given if possible, + * together with a definition of these models for the compiler system used. + * + ************************************************************************** + * + * Dhrystone (C version) statistics: + * + * [Comment from the first distribution, updated for version 2. + * Note that because of language differences, the numbers are slightly + * different from the Ada version.] + * + * The following program contains statements of a high level programming + * language (here: C) in a distribution considered representative: + * + * assignments 52 (51.0 %) + * control statements 33 (32.4 %) + * procedure, function calls 17 (16.7 %) + * + * 103 statements are dynamically executed. The program is balanced with + * respect to the three aspects: + * + * - statement type + * - operand type + * - operand locality + * operand global, local, parameter, or constant. + * + * The combination of these three aspects is balanced only approximately. + * + * 1. Statement Type: + * ----------------- number + * + * V1 = V2 9 + * (incl. V1 = F(..) + * V = Constant 12 + * Assignment, 7 + * with array element + * Assignment, 6 + * with record component + * -- + * 34 34 + * + * X = Y +|-|"&&"|"|" Z 5 + * X = Y +|-|"==" Constant 6 + * X = X +|- 1 3 + * X = Y *|/ Z 2 + * X = Expression, 1 + * two operators + * X = Expression, 1 + * three operators + * -- + * 18 18 + * + * if .... 14 + * with "else" 7 + * without "else" 7 + * executed 3 + * not executed 4 + * for ... 7 | counted every time + * while ... 4 | the loop condition + * do ... while 1 | is evaluated + * switch ... 1 + * break 1 + * declaration with 1 + * initialization + * -- + * 34 34 + * + * P (...) procedure call 11 + * user procedure 10 + * library procedure 1 + * X = F (...) + * function call 6 + * user function 5 + * library function 1 + * -- + * 17 17 + * --- + * 103 + * + * The average number of parameters in procedure or function calls + * is 1.82 (not counting the function values aX * + * + * 2. Operators + * ------------ + * number approximate + * percentage + * + * Arithmetic 32 50.8 + * + * + 21 33.3 + * - 7 11.1 + * * 3 4.8 + * / (int div) 1 1.6 + * + * Comparison 27 42.8 + * + * == 9 14.3 + * /= 4 6.3 + * > 1 1.6 + * < 3 4.8 + * >= 1 1.6 + * <= 9 14.3 + * + * Logic 4 6.3 + * + * && (AND-THEN) 1 1.6 + * | (OR) 1 1.6 + * ! (NOT) 2 3.2 + * + * -- ----- + * 63 100.1 + * + * + * 3. Operand Type (counted once per operand reference): + * --------------- + * number approximate + * percentage + * + * Integer 175 72.3 % + * Character 45 18.6 % + * Pointer 12 5.0 % + * String30 6 2.5 % + * Array 2 0.8 % + * Record 2 0.8 % + * --- ------- + * 242 100.0 % + * + * When there is an access path leading to the final operand (e.g. a record + * component), only the final data type on the access path is counted. + * + * + * 4. Operand Locality: + * ------------------- + * number approximate + * percentage + * + * local variable 114 47.1 % + * global variable 22 9.1 % + * parameter 45 18.6 % + * value 23 9.5 % + * reference 22 9.1 % + * function result 6 2.5 % + * constant 55 22.7 % + * --- ------- + * 242 100.0 % + * + * The program does not compute anything meaningful, but it is syntactically + * and semantically correct. All variables have a value assigned to them + * before they are used as a source operand. + * + * There has been no explicit effort to account for the effects of a + * cache, or to balance the use of long or short displacements for code or + * data. + * + *************************************************************************** + */ + +/* Compiler and system dependent definitions: */ + +/* variables for time measurement: */ + +#ifdef TIME + +#define CLOCK_TYPE "time()" +#undef HZ +#define HZ (1) /* time() returns time in seconds */ +extern long time(); /* see library function "time" */ +#define Too_Small_Time 2 /* Measurements should last at least 2 seconds */ +#define Start_Timer() Begin_Time = time ( (long *) 0) +#define Stop_Timer() End_Time = time ( (long *) 0) + +#else + +#ifdef MSC_CLOCK /* Use Microsoft C hi-res clock */ + +#undef HZ +#undef TIMES +#include + +#ifdef CLOCKS_PER_SEC +#define HZ CLOCKS_PER_SEC +#define CLOCK_TYPE "STDC clock()" +#else +#define HZ CLK_TCK +#define CLOCK_TYPE "MSC clock()" +#endif +extern clock_t clock(); +#define Too_Small_Time (2*HZ) +#define Start_Timer() Begin_Time = clock() +#define Stop_Timer() End_Time = clock() + +#else + /* Use times(2) time function unless */ + /* explicitly defined otherwise */ +#define CLOCK_TYPE "times()" +#include +#include +#ifndef HZ /* Added by SP 900619 */ +#include /* If your system doesn't have this, use -DHZ=xxx */ +#else + *** You must define HZ!!! *** +#endif /* HZ */ +#ifndef PASS2 +struct tms time_info; +#endif +/*extern int times ();*/ + /* see library function "times" */ +#define Too_Small_Time (2*HZ) + /* Measurements should last at least about 2 seconds */ +#define Start_Timer() times(&time_info); Begin_Time=(long)time_info.tms_utime +#define Stop_Timer() times(&time_info); End_Time = (long)time_info.tms_utime + +#endif /* MSC_CLOCK */ +#endif /* TIME */ + + +#define Mic_secs_Per_Second 1000000.0 + +#ifdef FIXED_NUMBER_OF_PASSES +#define NUMBER_OF_RUNS FIXED_NUMBER_OF_PASSES +#else +#define NUMBER_OF_RUNS 50000 /* Default number of runs */ +#endif + +#ifdef NOSTRUCTASSIGN +#define structassign(d, s) memcpy(&(d), &(s), sizeof(d)) +#else +#define structassign(d, s) d = s +#endif + +#ifdef NOENUM +#define Ident_1 0 +#define Ident_2 1 +#define Ident_3 2 +#define Ident_4 3 +#define Ident_5 4 + typedef int Enumeration; +#else + typedef enum {Ident_1, Ident_2, Ident_3, Ident_4, Ident_5} + Enumeration; +#endif + /* for boolean and enumeration types in Ada, Pascal */ + +/* General definitions: */ + +#include + /* for strcpy, strcmp */ + +#define Null 0 + /* Value of a Null pointer */ +#define true 1 +#define false 0 + +typedef int One_Thirty; +typedef int One_Fifty; +typedef char Capital_Letter; +typedef int Boolean; +typedef char Str_30 [31]; +typedef int Arr_1_Dim [50]; +typedef int Arr_2_Dim [50] [50]; + +typedef struct record + { + struct record *Ptr_Comp; + Enumeration Discr; + union { + struct { + Enumeration Enum_Comp; + int Int_Comp; + char Str_Comp [31]; + } var_1; + struct { + Enumeration E_Comp_2; + char Str_2_Comp [31]; + } var_2; + struct { + char Ch_1_Comp; + char Ch_2_Comp; + } var_3; + } variant; + } Rec_Type, *Rec_Pointer; + +#ifndef PASS2 + +/* Global Variables: */ + +Rec_Pointer Ptr_Glob, + Next_Ptr_Glob; +int Int_Glob; +Boolean Bool_Glob; +char Ch_1_Glob, + Ch_2_Glob; +int Arr_1_Glob [50]; +int Arr_2_Glob [50] [50]; + +//extern char *malloc (); +Enumeration Func_1 (); + /* forward declaration necessary since Enumeration may not simply be int */ + +#ifndef REG + Boolean Reg = false; +#define REG + /* REG becomes defined as empty */ + /* i.e. no register variables */ +#else + Boolean Reg = true; +#undef REG +#define REG register +#endif + +Boolean Done; + +long Begin_Time, + End_Time, + User_Time; +#ifdef INTEGER_ONLY +long long Microseconds, + Dhrystones_Per_Second; +#else +float Microseconds, + Dhrystones_Per_Second; +#endif +/* end of variables for time measurement */ + + +main (argc, argv) int argc; char *argv[]; +/*****/ + + /* main program, corresponds to procedures */ + /* Main and Proc_0 in the Ada version */ +{ + One_Fifty Int_1_Loc; + REG One_Fifty Int_2_Loc; + One_Fifty Int_3_Loc; + REG char Ch_Index; + Enumeration Enum_Loc; + Str_30 Str_1_Loc; + Str_30 Str_2_Loc; + REG int Run_Index; + REG int Number_Of_Runs; + +#if !defined(FIXED_NUMBER_OF_PASSES) + /* Arguments */ + if (argc > 2) + { + printf ("Usage: %s [number of loops]\n", argv[0]); + spinix_exit(1); + } + if (argc == 2) + { + Number_Of_Runs = atoi (argv[1]); + } else + { + Number_Of_Runs = NUMBER_OF_RUNS; + } + if (Number_Of_Runs <= 0) +#endif + { + Number_Of_Runs = NUMBER_OF_RUNS; + } + + /* Initializations */ + + Next_Ptr_Glob = (Rec_Pointer) malloc (sizeof (Rec_Type)); + Ptr_Glob = (Rec_Pointer) malloc (sizeof (Rec_Type)); + + Ptr_Glob->Ptr_Comp = Next_Ptr_Glob; + Ptr_Glob->Discr = Ident_1; + Ptr_Glob->variant.var_1.Enum_Comp = Ident_3; + Ptr_Glob->variant.var_1.Int_Comp = 40; + strcpy (Ptr_Glob->variant.var_1.Str_Comp, + "DHRYSTONE PROGRAM, SOME STRING"); + strcpy (Str_1_Loc, "DHRYSTONE PROGRAM, 1'ST STRING"); + + Arr_2_Glob [8][7] = 10; + /* Was missing in published program. Without this statement, */ + /* Arr_2_Glob [8][7] would have an undefined value. */ + /* Warning: With 16-Bit processors and Number_Of_Runs > 32000, */ + /* overflow may occur for this array element. */ + + printf ("\n"); + printf ("Dhrystone Benchmark, Version %s\n", Version); + if (Reg) + { + printf ("Program compiled with 'register' attribute\n"); + } + else + { + printf ("Program compiled without 'register' attribute\n"); + } + printf ("Using %s, HZ=%d\n", CLOCK_TYPE, HZ); + printf ("\n"); + + Done = false; + while (!Done) { + + printf ("Trying %d runs through Dhrystone:\n", Number_Of_Runs); + + /***************/ + /* Start timer */ + /***************/ + + Start_Timer(); + + for (Run_Index = 1; Run_Index <= Number_Of_Runs; ++Run_Index) + { + + Proc_5(); + Proc_4(); + /* Ch_1_Glob == 'A', Ch_2_Glob == 'B', Bool_Glob == true */ + Int_1_Loc = 2; + Int_2_Loc = 3; + strcpy (Str_2_Loc, "DHRYSTONE PROGRAM, 2'ND STRING"); + Enum_Loc = Ident_2; + Bool_Glob = ! Func_2 (Str_1_Loc, Str_2_Loc); + /* Bool_Glob == 1 */ + while (Int_1_Loc < Int_2_Loc) /* loop body executed once */ + { + Int_3_Loc = 5 * Int_1_Loc - Int_2_Loc; + /* Int_3_Loc == 7 */ + Proc_7 (Int_1_Loc, Int_2_Loc, &Int_3_Loc); + /* Int_3_Loc == 7 */ + Int_1_Loc += 1; + } /* while */ + /* Int_1_Loc == 3, Int_2_Loc == 3, Int_3_Loc == 7 */ + Proc_8 (Arr_1_Glob, Arr_2_Glob, Int_1_Loc, Int_3_Loc); + /* Int_Glob == 5 */ + Proc_1 (Ptr_Glob); + for (Ch_Index = 'A'; Ch_Index <= Ch_2_Glob; ++Ch_Index) + /* loop body executed twice */ + { + if (Enum_Loc == Func_1 (Ch_Index, 'C')) + /* then, not executed */ + { + Proc_6 (Ident_1, &Enum_Loc); + strcpy (Str_2_Loc, "DHRYSTONE PROGRAM, 3'RD STRING"); + Int_2_Loc = Run_Index; + Int_Glob = Run_Index; + } + } + /* Int_1_Loc == 3, Int_2_Loc == 3, Int_3_Loc == 7 */ + Int_2_Loc = Int_2_Loc * Int_1_Loc; + Int_1_Loc = Int_2_Loc / Int_3_Loc; + Int_2_Loc = 7 * (Int_2_Loc - Int_3_Loc) - Int_1_Loc; + /* Int_1_Loc == 1, Int_2_Loc == 13, Int_3_Loc == 7 */ + Proc_2 (&Int_1_Loc); + /* Int_1_Loc == 5 */ + + } /* loop "for Run_Index" */ + + /**************/ + /* Stop timer */ + /**************/ + + Stop_Timer(); + + User_Time = End_Time - Begin_Time; + + if (User_Time < Too_Small_Time) + { + printf ("Measured time too small to obtain meaningful results\n"); + Number_Of_Runs = Number_Of_Runs * 10; + printf ("\n"); + } else Done = true; + } + + printf ("Final values of the variables used in the benchmark:\n"); + printf ("\n"); + printf ("Int_Glob: %d\n", Int_Glob); + printf (" should be: %d\n", 5); + printf ("Bool_Glob: %d\n", Bool_Glob); + printf (" should be: %d\n", 1); + printf ("Ch_1_Glob: %c\n", Ch_1_Glob); + printf (" should be: %c\n", 'A'); + printf ("Ch_2_Glob: %c\n", Ch_2_Glob); + printf (" should be: %c\n", 'B'); + printf ("Arr_1_Glob[8]: %d\n", Arr_1_Glob[8]); + printf (" should be: %d\n", 7); + printf ("Arr_2_Glob[8][7]: %d\n", Arr_2_Glob[8][7]); + printf (" should be: Number_Of_Runs + 10\n"); + printf ("Ptr_Glob->\n"); + printf (" Ptr_Comp: %d\n", (int) Ptr_Glob->Ptr_Comp); + printf (" should be: (implementation-dependent)\n"); + printf (" Discr: %d\n", Ptr_Glob->Discr); + printf (" should be: %d\n", 0); + printf (" Enum_Comp: %d\n", Ptr_Glob->variant.var_1.Enum_Comp); + printf (" should be: %d\n", 2); + printf (" Int_Comp: %d\n", Ptr_Glob->variant.var_1.Int_Comp); + printf (" should be: %d\n", 17); + printf (" Str_Comp: %s\n", Ptr_Glob->variant.var_1.Str_Comp); + printf (" should be: DHRYSTONE PROGRAM, SOME STRING\n"); + printf ("Next_Ptr_Glob->\n"); + printf (" Ptr_Comp: %d\n", (int) Next_Ptr_Glob->Ptr_Comp); + printf (" should be: (implementation-dependent), same as above\n"); + printf (" Discr: %d\n", Next_Ptr_Glob->Discr); + printf (" should be: %d\n", 0); + printf (" Enum_Comp: %d\n", Next_Ptr_Glob->variant.var_1.Enum_Comp); + printf (" should be: %d\n", 1); + printf (" Int_Comp: %d\n", Next_Ptr_Glob->variant.var_1.Int_Comp); + printf (" should be: %d\n", 18); + printf (" Str_Comp: %s\n", + Next_Ptr_Glob->variant.var_1.Str_Comp); + printf (" should be: DHRYSTONE PROGRAM, SOME STRING\n"); + printf ("Int_1_Loc: %d\n", Int_1_Loc); + printf (" should be: %d\n", 5); + printf ("Int_2_Loc: %d\n", Int_2_Loc); + printf (" should be: %d\n", 13); + printf ("Int_3_Loc: %d\n", Int_3_Loc); + printf (" should be: %d\n", 7); + printf ("Enum_Loc: %d\n", Enum_Loc); + printf (" should be: %d\n", 1); + printf ("Str_1_Loc: %s\n", Str_1_Loc); + printf (" should be: DHRYSTONE PROGRAM, 1'ST STRING\n"); + printf ("Str_2_Loc: %s\n", Str_2_Loc); + printf (" should be: DHRYSTONE PROGRAM, 2'ND STRING\n"); + printf ("\n"); + + +#ifdef INTEGER_ONLY + Microseconds = (long long) User_Time * 1000000LL + / ((long long) HZ * ((long long) Number_Of_Runs)); + Dhrystones_Per_Second = ((long long) HZ * (long long) Number_Of_Runs) + / (long long) User_Time; + + printf ("Microseconds for one run through Dhrystone: "); + printf ("%u \n", (unsigned)Microseconds); + printf ("Dhrystones per Second: "); + printf ("%u \n", (unsigned)Dhrystones_Per_Second); + printf ("\n"); +#else + Microseconds = (float) User_Time * Mic_secs_Per_Second + / ((float) HZ * ((float) Number_Of_Runs)); + Dhrystones_Per_Second = ((float) HZ * (float) Number_Of_Runs) + / (float) User_Time; + + printf ("Microseconds for one run through Dhrystone: "); + printf ("%10.1f \n", Microseconds); + printf ("Dhrystones per Second: "); + printf ("%10.0f \n", Dhrystones_Per_Second); + printf ("\n"); +#endif + spinix_exit(0); +} + + +Proc_1 (Ptr_Val_Par) +/******************/ + +REG Rec_Pointer Ptr_Val_Par; + /* executed once */ +{ + REG Rec_Pointer Next_Record = Ptr_Val_Par->Ptr_Comp; + /* == Ptr_Glob_Next */ + /* Local variable, initialized with Ptr_Val_Par->Ptr_Comp, */ + /* corresponds to "rename" in Ada, "with" in Pascal */ + + structassign (*Ptr_Val_Par->Ptr_Comp, *Ptr_Glob); + Ptr_Val_Par->variant.var_1.Int_Comp = 5; + Next_Record->variant.var_1.Int_Comp + = Ptr_Val_Par->variant.var_1.Int_Comp; + Next_Record->Ptr_Comp = Ptr_Val_Par->Ptr_Comp; + Proc_3 (&Next_Record->Ptr_Comp); + /* Ptr_Val_Par->Ptr_Comp->Ptr_Comp + == Ptr_Glob->Ptr_Comp */ + if (Next_Record->Discr == Ident_1) + /* then, executed */ + { + Next_Record->variant.var_1.Int_Comp = 6; + Proc_6 (Ptr_Val_Par->variant.var_1.Enum_Comp, + &Next_Record->variant.var_1.Enum_Comp); + Next_Record->Ptr_Comp = Ptr_Glob->Ptr_Comp; + Proc_7 (Next_Record->variant.var_1.Int_Comp, 10, + &Next_Record->variant.var_1.Int_Comp); + } + else /* not executed */ + structassign (*Ptr_Val_Par, *Ptr_Val_Par->Ptr_Comp); +} /* Proc_1 */ + + +Proc_2 (Int_Par_Ref) +/******************/ + /* executed once */ + /* *Int_Par_Ref == 1, becomes 4 */ + +One_Fifty *Int_Par_Ref; +{ + One_Fifty Int_Loc; + Enumeration Enum_Loc; + + Int_Loc = *Int_Par_Ref + 10; + do /* executed once */ + if (Ch_1_Glob == 'A') + /* then, executed */ + { + Int_Loc -= 1; + *Int_Par_Ref = Int_Loc - Int_Glob; + Enum_Loc = Ident_1; + } /* if */ + while (Enum_Loc != Ident_1); /* true */ +} /* Proc_2 */ + + +Proc_3 (Ptr_Ref_Par) +/******************/ + /* executed once */ + /* Ptr_Ref_Par becomes Ptr_Glob */ + +Rec_Pointer *Ptr_Ref_Par; + +{ + if (Ptr_Glob != Null) + /* then, executed */ + *Ptr_Ref_Par = Ptr_Glob->Ptr_Comp; + Proc_7 (10, Int_Glob, &Ptr_Glob->variant.var_1.Int_Comp); +} /* Proc_3 */ + + +Proc_4 () /* without parameters */ +/*******/ + /* executed once */ +{ + Boolean Bool_Loc; + + Bool_Loc = Ch_1_Glob == 'A'; + Bool_Glob = Bool_Loc | Bool_Glob; + Ch_2_Glob = 'B'; +} /* Proc_4 */ + + +Proc_5 () /* without parameters */ +/*******/ + /* executed once */ +{ + Ch_1_Glob = 'A'; + Bool_Glob = false; +} /* Proc_5 */ + + + /* Procedure for the assignment of structures, */ + /* if the C compiler doesn't support this feature */ +#ifdef NOSTRUCTASSIGN +memcpy (d, s, l) +register char *d; +register char *s; +register int l; +{ + while (l--) *d++ = *s++; +} +#endif + + +#else /* PASS2 */ + +#ifndef REG +#define REG + /* REG becomes defined as empty */ + /* i.e. no register variables */ +#else +#undef REG +#define REG register +#endif + +extern int Int_Glob; +extern char Ch_1_Glob; + + +Proc_6 (Enum_Val_Par, Enum_Ref_Par) +/*********************************/ + /* executed once */ + /* Enum_Val_Par == Ident_3, Enum_Ref_Par becomes Ident_2 */ + +Enumeration Enum_Val_Par; +Enumeration *Enum_Ref_Par; +{ + *Enum_Ref_Par = Enum_Val_Par; + if (! Func_3 (Enum_Val_Par)) + /* then, not executed */ + *Enum_Ref_Par = Ident_4; + switch (Enum_Val_Par) + { + case Ident_1: + *Enum_Ref_Par = Ident_1; + break; + case Ident_2: + if (Int_Glob > 100) + /* then */ + *Enum_Ref_Par = Ident_1; + else *Enum_Ref_Par = Ident_4; + break; + case Ident_3: /* executed */ + *Enum_Ref_Par = Ident_2; + break; + case Ident_4: break; + case Ident_5: + *Enum_Ref_Par = Ident_3; + break; + } /* switch */ +} /* Proc_6 */ + + +Proc_7 (Int_1_Par_Val, Int_2_Par_Val, Int_Par_Ref) +/**********************************************/ + /* executed three times */ + /* first call: Int_1_Par_Val == 2, Int_2_Par_Val == 3, */ + /* Int_Par_Ref becomes 7 */ + /* second call: Int_1_Par_Val == 10, Int_2_Par_Val == 5, */ + /* Int_Par_Ref becomes 17 */ + /* third call: Int_1_Par_Val == 6, Int_2_Par_Val == 10, */ + /* Int_Par_Ref becomes 18 */ +One_Fifty Int_1_Par_Val; +One_Fifty Int_2_Par_Val; +One_Fifty *Int_Par_Ref; +{ + One_Fifty Int_Loc; + + Int_Loc = Int_1_Par_Val + 2; + *Int_Par_Ref = Int_2_Par_Val + Int_Loc; +} /* Proc_7 */ + + +Proc_8 (Arr_1_Par_Ref, Arr_2_Par_Ref, Int_1_Par_Val, Int_2_Par_Val) +/*********************************************************************/ + /* executed once */ + /* Int_Par_Val_1 == 3 */ + /* Int_Par_Val_2 == 7 */ +Arr_1_Dim Arr_1_Par_Ref; +Arr_2_Dim Arr_2_Par_Ref; +int Int_1_Par_Val; +int Int_2_Par_Val; +{ + REG One_Fifty Int_Index; + REG One_Fifty Int_Loc; + + Int_Loc = Int_1_Par_Val + 5; + Arr_1_Par_Ref [Int_Loc] = Int_2_Par_Val; + Arr_1_Par_Ref [Int_Loc+1] = Arr_1_Par_Ref [Int_Loc]; + Arr_1_Par_Ref [Int_Loc+30] = Int_Loc; + for (Int_Index = Int_Loc; Int_Index <= Int_Loc+1; ++Int_Index) + Arr_2_Par_Ref [Int_Loc] [Int_Index] = Int_Loc; + Arr_2_Par_Ref [Int_Loc] [Int_Loc-1] += 1; + Arr_2_Par_Ref [Int_Loc+20] [Int_Loc] = Arr_1_Par_Ref [Int_Loc]; + Int_Glob = 5; +} /* Proc_8 */ + + +Enumeration Func_1 (Ch_1_Par_Val, Ch_2_Par_Val) +/*************************************************/ + /* executed three times */ + /* first call: Ch_1_Par_Val == 'H', Ch_2_Par_Val == 'R' */ + /* second call: Ch_1_Par_Val == 'A', Ch_2_Par_Val == 'C' */ + /* third call: Ch_1_Par_Val == 'B', Ch_2_Par_Val == 'C' */ + +Capital_Letter Ch_1_Par_Val; +Capital_Letter Ch_2_Par_Val; +{ + Capital_Letter Ch_1_Loc; + Capital_Letter Ch_2_Loc; + + Ch_1_Loc = Ch_1_Par_Val; + Ch_2_Loc = Ch_1_Loc; + if (Ch_2_Loc != Ch_2_Par_Val) + /* then, executed */ + return (Ident_1); + else /* not executed */ + { + Ch_1_Glob = Ch_1_Loc; + return (Ident_2); + } +} /* Func_1 */ + + +Boolean Func_2 (Str_1_Par_Ref, Str_2_Par_Ref) +/*************************************************/ + /* executed once */ + /* Str_1_Par_Ref == "DHRYSTONE PROGRAM, 1'ST STRING" */ + /* Str_2_Par_Ref == "DHRYSTONE PROGRAM, 2'ND STRING" */ + +Str_30 Str_1_Par_Ref; +Str_30 Str_2_Par_Ref; +{ + REG One_Thirty Int_Loc; + Capital_Letter Ch_Loc; + + Int_Loc = 2; + while (Int_Loc <= 2) /* loop body executed once */ + if (Func_1 (Str_1_Par_Ref[Int_Loc], + Str_2_Par_Ref[Int_Loc+1]) == Ident_1) + /* then, executed */ + { + Ch_Loc = 'A'; + Int_Loc += 1; + } /* if, while */ + if (Ch_Loc >= 'W' && Ch_Loc < 'Z') + /* then, not executed */ + Int_Loc = 7; + if (Ch_Loc == 'R') + /* then, not executed */ + return (true); + else /* executed */ + { + if (strcmp (Str_1_Par_Ref, Str_2_Par_Ref) > 0) + /* then, not executed */ + { + Int_Loc += 7; + Int_Glob = Int_Loc; + return (true); + } + else /* executed */ + return (false); + } /* if Ch_Loc */ +} /* Func_2 */ + + +Boolean Func_3 (Enum_Par_Val) +/***************************/ + /* executed once */ + /* Enum_Par_Val == Ident_3 */ +Enumeration Enum_Par_Val; +{ + Enumeration Enum_Loc; + + Enum_Loc = Enum_Par_Val; + if (Enum_Loc == Ident_3) + /* then, executed */ + return (true); + else /* not executed */ + return (false); +} /* Func_3 */ + +#endif /* PASS2 */ diff --git a/cdemos/dry/spinix.c b/cdemos/dry/spinix.c new file mode 100644 index 0000000..f1c06f3 --- /dev/null +++ b/cdemos/dry/spinix.c @@ -0,0 +1,45 @@ +#include +#include "spinix.h" + +static int run_prog[] = { + 0xa0bc65f0, 0x08bc6e32, 0x80fc6404, 0x08bc7032, 0x80fc6404, 0x08bc6032, + 0x80fc6404, 0x08bc6232, 0x80fc63ff, 0x28fc6209, 0x08fc6600, 0x00fc6804, + 0x083c6029, 0x083c5c2a, 0x083c582b, 0x08bc642b, 0x863c642c, 0x5c68000f, + 0x80fc6001, 0x80fc5d00, 0x80fc5d00, 0xe4fc620c, 0x087c6600, 0x007c6804, + 0x04fc6a08, 0x04fc6c0a, 0xa0bc6236, 0x84bc6235, 0x28fc6202, 0x083c5a35, + 0x80fc6a04, 0xe4fc621d, 0x083c5a36, 0x80fc6c04, 0x083c6e36, 0x80fc6c04, + 0x083c7036, 0x0cfc6401, 0x60fc6407, 0x68bc5e32, 0x0c7c5e02, 0x00007fd8, + 0x00007fdc, 0x00007fd4, 0x00000072, 0x00000000, 0x00000000, 0x0007c010}; + +static int arg_list[] = {0, 0, 0, 0}; + +void spinix_enter(int argc, char **argv) +{ +} + +void spinix_exit(int retval) +{ + int i; + int cogspi = (*(int *)spinix_spi_engine_cog) - 1; + arg_list[2] = *(int *)spinix_shell_sector; + arg_list[3] = *(int *)spinix_shell_size; + + *(int *)spinix_return_value = retval; + + // Stop all the cogs except this one and the SD SPI cog + for (i = 0; i < 8; i++) + { + if (i != cogid() && i != cogspi) + cogstop(i); + } + + // Clear and return all the locks + for (i = 0; i < 8; i++) + { + lockclr(i); + lockret(i); + } + + // Start run_prog in this cog + coginit(cogid(), run_prog, arg_list); +} diff --git a/cdemos/dry/spinix.h b/cdemos/dry/spinix.h new file mode 100644 index 0000000..ba7d835 --- /dev/null +++ b/cdemos/dry/spinix.h @@ -0,0 +1,45 @@ +#define spinix_start 0x7c00 // Extra space for the stand-alone loader +#define spinix_rendezvous 0x7e50 +#define spinix_environ_vars 0x7e50 +#define spinix_environ_vars_end 0x7ed3 +#define spinix_argv_parms 0x7ed4 +#define spinix_return_value 0x7f94 +#define spinix_vga_cog 0x7f98 +#define spinix_vga_handle 0x7f9c +#define spinix_sd_pins 0x7fa0 +#define spinix_config 0x7fa4 +#define spinix_shell_sector 0x7fa8 +#define spinix_unixtime 0x7fac +#define spinix_cycle0 0x7fb0 +#define spinix_timezone 0x7fb4 +#define spinix_scriptline 0x7fb8 +#define spinix_ifflag 0x7fbc +#define spinix_whileflag 0x7fc0 +#define spinix_shell_size 0x7fc4 +#define spinix_shell_level 0x7fc8 +#define spinix_bootflag 0x7fcc +#define spinix_spi_engine_cog 0x7fd0 +#define spinix_spi_command 0x7fd4 +#define spinix_spi_block_index 0x7fd8 +#define spinix_spi_buffer_address 0x7fdc +#define spinix_serial 0x7fe0 +#define spinix_stdio 0x7fe4 +#define spinix_stdin 0x7fe8 +#define spinix_stdout 0x7fec +#define spinix_memlocknum 0x7ff0 +#define spinix_memfreelist 0x7ff4 +#define spinix_malloclist 0x7ff8 +#define spinix_laststackaddr 0x7ffc +#define spinix_checkword 0xdead1eaf +#define spinix_proc_type_spin 1 +#define spinix_proc_type_pasm 2 +#define spinix_proc_type_capp 3 +#define spinix_proc_type_driver 0x80 +#define spinix_run_shell_wait 0x00 +#define spinix_run_shell_nowait 0x08 +#define spinix_run_kill_caller 0x10 +#define spinix_run_at_address0 0x20 +#define spinix_run_c_program 0x40 +#define spinix_run_spin_program 0x80 +#define spinix_run_stand_alone 0x100 + diff --git a/cdemos/fibo/Makefile b/cdemos/fibo/Makefile new file mode 100644 index 0000000..82e361a --- /dev/null +++ b/cdemos/fibo/Makefile @@ -0,0 +1,22 @@ +# +# objects for this program +# + +PROPLIB = ../common + +NAME = fibo +OBJS = fibo.o spinix.o + +# memory model +MODEL = lmm + +# +# enable experimental fcache code for this demo +# +#CFLAGS = -Os -mfcache +CFLAGS = -g -Os +#CFLAGS = -g + +all: $(NAME).elf + +include $(PROPLIB)/common.mk diff --git a/cdemos/fibo/buildit b/cdemos/fibo/buildit new file mode 100644 index 0000000..c4975ad --- /dev/null +++ b/cdemos/fibo/buildit @@ -0,0 +1,2 @@ +propeller-elf-gcc -Os -mlmm -o fibo.elf fibo.c spinix.c +propeller-load -s -D clkfreq=1347436867 -D clkmode=98 fibo.elf diff --git a/cdemos/fibo/fibo.c b/cdemos/fibo/fibo.c new file mode 100644 index 0000000..3ddae1f --- /dev/null +++ b/cdemos/fibo/fibo.c @@ -0,0 +1,47 @@ +//#include +//#include +//#include +#include +#include +#include "spinix.h" + +//#define CNT _CNT +//#define CLKFREQ _CLKFREQ + +unsigned int fibo (unsigned int n) +{ + if (n < 2) + { + return (n); + } + else + { + return fibo(n - 1) + fibo(n - 2); + } +} + +extern unsigned int clock(void); + +int main (int argc, char* argv[]) +{ + int n; + int result; + unsigned int startTime; + unsigned int endTime; + unsigned int executionTime; + unsigned int rawTime; + + printf("hello, world!\r\n"); + for (n = 0; n <= 26; n++) + { + printf("fibo(%02d) = ", n); + startTime = clock(); + result = fibo(n); + endTime = clock(); + rawTime = endTime - startTime; + executionTime = rawTime / (CLKFREQ / 1000); + printf ("%06d (%05ums) (%u ticks)\n", result, executionTime, rawTime); + } + + spinix_exit(0); +} diff --git a/cdemos/fibo/spinix.c b/cdemos/fibo/spinix.c new file mode 100644 index 0000000..f1c06f3 --- /dev/null +++ b/cdemos/fibo/spinix.c @@ -0,0 +1,45 @@ +#include +#include "spinix.h" + +static int run_prog[] = { + 0xa0bc65f0, 0x08bc6e32, 0x80fc6404, 0x08bc7032, 0x80fc6404, 0x08bc6032, + 0x80fc6404, 0x08bc6232, 0x80fc63ff, 0x28fc6209, 0x08fc6600, 0x00fc6804, + 0x083c6029, 0x083c5c2a, 0x083c582b, 0x08bc642b, 0x863c642c, 0x5c68000f, + 0x80fc6001, 0x80fc5d00, 0x80fc5d00, 0xe4fc620c, 0x087c6600, 0x007c6804, + 0x04fc6a08, 0x04fc6c0a, 0xa0bc6236, 0x84bc6235, 0x28fc6202, 0x083c5a35, + 0x80fc6a04, 0xe4fc621d, 0x083c5a36, 0x80fc6c04, 0x083c6e36, 0x80fc6c04, + 0x083c7036, 0x0cfc6401, 0x60fc6407, 0x68bc5e32, 0x0c7c5e02, 0x00007fd8, + 0x00007fdc, 0x00007fd4, 0x00000072, 0x00000000, 0x00000000, 0x0007c010}; + +static int arg_list[] = {0, 0, 0, 0}; + +void spinix_enter(int argc, char **argv) +{ +} + +void spinix_exit(int retval) +{ + int i; + int cogspi = (*(int *)spinix_spi_engine_cog) - 1; + arg_list[2] = *(int *)spinix_shell_sector; + arg_list[3] = *(int *)spinix_shell_size; + + *(int *)spinix_return_value = retval; + + // Stop all the cogs except this one and the SD SPI cog + for (i = 0; i < 8; i++) + { + if (i != cogid() && i != cogspi) + cogstop(i); + } + + // Clear and return all the locks + for (i = 0; i < 8; i++) + { + lockclr(i); + lockret(i); + } + + // Start run_prog in this cog + coginit(cogid(), run_prog, arg_list); +} diff --git a/cdemos/fibo/spinix.h b/cdemos/fibo/spinix.h new file mode 100644 index 0000000..ba7d835 --- /dev/null +++ b/cdemos/fibo/spinix.h @@ -0,0 +1,45 @@ +#define spinix_start 0x7c00 // Extra space for the stand-alone loader +#define spinix_rendezvous 0x7e50 +#define spinix_environ_vars 0x7e50 +#define spinix_environ_vars_end 0x7ed3 +#define spinix_argv_parms 0x7ed4 +#define spinix_return_value 0x7f94 +#define spinix_vga_cog 0x7f98 +#define spinix_vga_handle 0x7f9c +#define spinix_sd_pins 0x7fa0 +#define spinix_config 0x7fa4 +#define spinix_shell_sector 0x7fa8 +#define spinix_unixtime 0x7fac +#define spinix_cycle0 0x7fb0 +#define spinix_timezone 0x7fb4 +#define spinix_scriptline 0x7fb8 +#define spinix_ifflag 0x7fbc +#define spinix_whileflag 0x7fc0 +#define spinix_shell_size 0x7fc4 +#define spinix_shell_level 0x7fc8 +#define spinix_bootflag 0x7fcc +#define spinix_spi_engine_cog 0x7fd0 +#define spinix_spi_command 0x7fd4 +#define spinix_spi_block_index 0x7fd8 +#define spinix_spi_buffer_address 0x7fdc +#define spinix_serial 0x7fe0 +#define spinix_stdio 0x7fe4 +#define spinix_stdin 0x7fe8 +#define spinix_stdout 0x7fec +#define spinix_memlocknum 0x7ff0 +#define spinix_memfreelist 0x7ff4 +#define spinix_malloclist 0x7ff8 +#define spinix_laststackaddr 0x7ffc +#define spinix_checkword 0xdead1eaf +#define spinix_proc_type_spin 1 +#define spinix_proc_type_pasm 2 +#define spinix_proc_type_capp 3 +#define spinix_proc_type_driver 0x80 +#define spinix_run_shell_wait 0x00 +#define spinix_run_shell_nowait 0x08 +#define spinix_run_kill_caller 0x10 +#define spinix_run_at_address0 0x20 +#define spinix_run_c_program 0x40 +#define spinix_run_spin_program 0x80 +#define spinix_run_stand_alone 0x100 + diff --git a/cdemos/hello/Makefile b/cdemos/hello/Makefile new file mode 100644 index 0000000..4cc2d14 --- /dev/null +++ b/cdemos/hello/Makefile @@ -0,0 +1,22 @@ +# +# objects for this program +# + +PROPLIB = ../common + +NAME = hello +OBJS = hello.o spinix.o + +# memory model +MODEL = lmm + +# +# enable experimental fcache code for this demo +# +#CFLAGS = -Os -mfcache +CFLAGS = -g -Os +#CFLAGS = -g + +all: $(NAME).elf + +include $(PROPLIB)/common.mk diff --git a/cdemos/hello/buildit b/cdemos/hello/buildit new file mode 100644 index 0000000..d460bae --- /dev/null +++ b/cdemos/hello/buildit @@ -0,0 +1,2 @@ +propeller-elf-gcc -Os -mlmm -o hello.elf hello.c spinix.c +propeller-load -s -D clkfreq=1347436867 -D clkmode=98 hello.elf diff --git a/cdemos/hello/hello.c b/cdemos/hello/hello.c new file mode 100644 index 0000000..d9653a6 --- /dev/null +++ b/cdemos/hello/hello.c @@ -0,0 +1,9 @@ +#include +#include "spinix.h" + +int main (int argc, char **argv) +{ + spinix_enter(); + printf("Hello World!\n"); + spinix_exit(0); +} diff --git a/cdemos/hello/spinix.c b/cdemos/hello/spinix.c new file mode 100644 index 0000000..f1c06f3 --- /dev/null +++ b/cdemos/hello/spinix.c @@ -0,0 +1,45 @@ +#include +#include "spinix.h" + +static int run_prog[] = { + 0xa0bc65f0, 0x08bc6e32, 0x80fc6404, 0x08bc7032, 0x80fc6404, 0x08bc6032, + 0x80fc6404, 0x08bc6232, 0x80fc63ff, 0x28fc6209, 0x08fc6600, 0x00fc6804, + 0x083c6029, 0x083c5c2a, 0x083c582b, 0x08bc642b, 0x863c642c, 0x5c68000f, + 0x80fc6001, 0x80fc5d00, 0x80fc5d00, 0xe4fc620c, 0x087c6600, 0x007c6804, + 0x04fc6a08, 0x04fc6c0a, 0xa0bc6236, 0x84bc6235, 0x28fc6202, 0x083c5a35, + 0x80fc6a04, 0xe4fc621d, 0x083c5a36, 0x80fc6c04, 0x083c6e36, 0x80fc6c04, + 0x083c7036, 0x0cfc6401, 0x60fc6407, 0x68bc5e32, 0x0c7c5e02, 0x00007fd8, + 0x00007fdc, 0x00007fd4, 0x00000072, 0x00000000, 0x00000000, 0x0007c010}; + +static int arg_list[] = {0, 0, 0, 0}; + +void spinix_enter(int argc, char **argv) +{ +} + +void spinix_exit(int retval) +{ + int i; + int cogspi = (*(int *)spinix_spi_engine_cog) - 1; + arg_list[2] = *(int *)spinix_shell_sector; + arg_list[3] = *(int *)spinix_shell_size; + + *(int *)spinix_return_value = retval; + + // Stop all the cogs except this one and the SD SPI cog + for (i = 0; i < 8; i++) + { + if (i != cogid() && i != cogspi) + cogstop(i); + } + + // Clear and return all the locks + for (i = 0; i < 8; i++) + { + lockclr(i); + lockret(i); + } + + // Start run_prog in this cog + coginit(cogid(), run_prog, arg_list); +} diff --git a/cdemos/hello/spinix.h b/cdemos/hello/spinix.h new file mode 100644 index 0000000..ba7d835 --- /dev/null +++ b/cdemos/hello/spinix.h @@ -0,0 +1,45 @@ +#define spinix_start 0x7c00 // Extra space for the stand-alone loader +#define spinix_rendezvous 0x7e50 +#define spinix_environ_vars 0x7e50 +#define spinix_environ_vars_end 0x7ed3 +#define spinix_argv_parms 0x7ed4 +#define spinix_return_value 0x7f94 +#define spinix_vga_cog 0x7f98 +#define spinix_vga_handle 0x7f9c +#define spinix_sd_pins 0x7fa0 +#define spinix_config 0x7fa4 +#define spinix_shell_sector 0x7fa8 +#define spinix_unixtime 0x7fac +#define spinix_cycle0 0x7fb0 +#define spinix_timezone 0x7fb4 +#define spinix_scriptline 0x7fb8 +#define spinix_ifflag 0x7fbc +#define spinix_whileflag 0x7fc0 +#define spinix_shell_size 0x7fc4 +#define spinix_shell_level 0x7fc8 +#define spinix_bootflag 0x7fcc +#define spinix_spi_engine_cog 0x7fd0 +#define spinix_spi_command 0x7fd4 +#define spinix_spi_block_index 0x7fd8 +#define spinix_spi_buffer_address 0x7fdc +#define spinix_serial 0x7fe0 +#define spinix_stdio 0x7fe4 +#define spinix_stdin 0x7fe8 +#define spinix_stdout 0x7fec +#define spinix_memlocknum 0x7ff0 +#define spinix_memfreelist 0x7ff4 +#define spinix_malloclist 0x7ff8 +#define spinix_laststackaddr 0x7ffc +#define spinix_checkword 0xdead1eaf +#define spinix_proc_type_spin 1 +#define spinix_proc_type_pasm 2 +#define spinix_proc_type_capp 3 +#define spinix_proc_type_driver 0x80 +#define spinix_run_shell_wait 0x00 +#define spinix_run_shell_nowait 0x08 +#define spinix_run_kill_caller 0x10 +#define spinix_run_at_address0 0x20 +#define spinix_run_c_program 0x40 +#define spinix_run_spin_program 0x80 +#define spinix_run_stand_alone 0x100 + diff --git a/chess/buildit b/chess/buildit new file mode 100644 index 0000000..26f4be2 --- /dev/null +++ b/chess/buildit @@ -0,0 +1,2 @@ +propeller-elf-gcc -o chess.elf -Os -mlmm -fno-exceptions chess.c -ltiny -lpthread +propeller-load -s chess.elf diff --git a/chess/chess.c b/chess/chess.c new file mode 100644 index 0000000..247166f --- /dev/null +++ b/chess/chess.c @@ -0,0 +1,1412 @@ +/* +############################################################################ +# This program implements the game of chess on a processor that has multiple +# cores. It works by generating a list of moves, and then each core will +# get a move from the list and evaluate it. This is done by using pthreads, +# where each pthread is mapped to an additional core. The number of +# pthreads is determined by NUM_PTHREADS. For a dual-core x86 processor, +# NUM_PTHREADS is set to 1. The main thread runs on one core, and the +# pthread runs on the second core. Setting NUM_PTHREADS to 0 will cause the +# program to run a single-threaded mode without using pthreads. +# +# This program also run on the Parallax Propeller processor, and uses 6 of +# the 8 cogs that are available. The number of cogs used is limited by the +# amount of hub memory available. +# +# Copyright (c) 2013, Dave Hein +# MIT Licensed +############################################################################ +*/ + +#include +#include +#include +#include + +#ifdef __PROPELLER__ +#include + +#define MAX_DEPTH 4 +#define NUM_PTHREADS 5 +#define PTHREAD_STACKSIZE 1176 +#else +#define MAX_DEPTH 5 +#define NUM_PTHREADS 1 +#endif + +#define uchar unsigned char + +#define BOARD_BORDER 2 +#define BOARD_WIDTH 12 +#define BOARD_HEIGHT 12 +#define BOARD_SIZE (BOARD_WIDTH * BOARD_HEIGHT) + +#define OUT_OF_BOUNDS 0xff +#define PIECE_MASK 0x07 +#define COLOR_MASK 0x80 +#define WHITE 0x80 +#define BLACK 0x00 +#define PIECE_MOVED 0x40 + +#define POSITION_A8 ((BOARD_WIDTH * 2) + 2) +#define POSITION_H8 ((BOARD_WIDTH * 2) + 9) +#define POSITION_A1 ((BOARD_WIDTH * 9) + 2) +#define POSITION_D5 ((BOARD_WIDTH * 5) + 5) +#define POSITION_E5 ((BOARD_WIDTH * 5) + 6) +#define POSITION_D4 ((BOARD_WIDTH * 6) + 5) +#define POSITION_E4 ((BOARD_WIDTH * 6) + 6) + +// Piece numbers +#define PAWN 1 +#define KNIGHT 2 +#define BISHOP 3 +#define ROOK 4 +#define QUEEN 5 +#define KING 6 + +// Directions +#define N (-BOARD_WIDTH) +#define S BOARD_WIDTH +#define E 1 +#define W (-1) +#define NW (N + W) +#define SW (S + W) +#define NE (N + E) +#define SE (S + E) +#define NNW (N + N + W) +#define NNE (N + N + E) +#define SSW (S + S + W) +#define SSE (S + S + E) +#define WSW (W + S + W) +#define WNW (W + N + W) +#define ESE (E + S + E) +#define ENE (E + N + E) + +typedef struct levelS { + uchar board[BOARD_SIZE]; + short value; + short best_value; + uchar color; + uchar depth; + uchar old_pos; + uchar new_pos; + uchar best_old; + uchar best_new; + uchar wking_pos; + uchar bking_pos; + uchar en_passant; +} levelT; + +typedef void (*FuncPtr)(levelT *); + +// Function prototypes +void PerformMove(levelT *level); +void QueueUpMove(levelT *level); +void AnalyzeMoveQueue(levelT *level); +void StartPthreads(void); +void GenerateQueuedMoves(levelT *level); + +char *symbols = "xPNBRQKx"; + +// Directions that the pieces can move +int king_moves[8] = {SW, SE, NW, NE, W, S, E, N}; +int knight_moves[8] = {NNE, ENE, ESE, SSE, SSW, WSW, WNW, NNW}; + +// The piece values +int values[] = { 0, 20, 64, 65, 100, 195, 10000, 0}; + +// Pawn position values +signed char pawn_values[] = { + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 10, 10, 10, 10, 10, 10, 10, 10, 0, 0, + 0, 0, 2, 2, 4, 6, 6, 4, 2, 2, 0, 0, + 0, 0, 1, 1, 2, 6, 6, 2, 1, 1, 0, 0, + 0, 0, 0, 0, 0, 5, 5, 0, 0, 0, 0, 0, + 0, 0, 1, -1, -2, 0, 0, -2, -1, 1, 0, 0, + 0, 0, 1, 2, 2, -5, -5, 2, 2, 1, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }; + +// Knight position values +signed char knight_values[] = { + 0, 0,-10, -8 -6, -6, -6, -6, -8,-10, 0, 0, + 0, 0, -8, -4, 0, 0, 0, 0, -4, -8, 0, 0, + 0, 0, -6, 0, 2, 3, 3, 2, 0, -6, 0, 0, + 0, 0, -6, 1, 3, 4, 4, 3, 1, -6, 0, 0, + 0, 0, -6, 0, 3, 4, 4, 3, 0, -6, 0, 0, + 0, 0, -6, 1, 2, 3, 3, 2, 1, -6, 0, 0, + 0, 0, -8, -4, 0, 1, 1, 0, -4, -8, 0, 0, + 0, 0,-10, -8 -4, -6, -6, -4, -8,-10, 0, 0}; + +// Bishop position values +signed char bishop_values[] = { + 0, 0, -4, -2, -2, -2, -2, -2, -2, -4, 0, 0, + 0, 0, -2, 0, 0, 0, 0, 0, 0, -2, 0, 0, + 0, 0, -2, 0, 1, 2, 2, 1, 0, -2, 0, 0, + 0, 0, -2, 1, 1, 2, 2, 1, 1, -2, 0, 0, + 0, 0, -2, 0, 2, 2, 2, 2, 0, -2, 0, 0, + 0, 0, -2, 2, 2, 2, 2, 2, 2, -2, 0, 0, + 0, 0, -2, 1, 0, 0, 0, 0, 1, -2, 0, 0, + 0, 0, -4, -2, -8, -2, -2, -8, -2, -4, 0, 0}; + +// King position values +signed char king_values[] = { + 0, 0, -6, -8, -8,-10,-10, -8, -8, -6, 0, 0, + 0, 0, -6, -8, -8,-10,-10, -8, -8, -6, 0, 0, + 0, 0, -6, -8, -8,-10,-10, -8, -8, -6, 0, 0, + 0, 0, -6, -8, -8,-10,-10, -8, -8, -6, 0, 0, + 0, 0, -4, -6, -6, -8, -8, -6, -6, -4, 0, 0, + 0, 0, -2, -4, -4, -4, -4, -4, -4, -2, 0, 0, + 0, 0, 4, 4, 0, 0, 0, 0, 4, 4, 0, 0, + 0, 0, 4, 6, 2, 0, 0, 2, 6, 4, 0, 0}; + +// Null position values used for the queen and rooks +signed char null_values[] = { + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }; + +signed char *pos_values[8] = { null_values, pawn_values, knight_values, +bishop_values, null_values, null_values, king_values, null_values}; + +uchar black_rank[8] = {ROOK, KNIGHT, BISHOP, QUEEN, KING, BISHOP, KNIGHT, ROOK}; +uchar white_rank[8] = {ROOK | WHITE, KNIGHT | WHITE, BISHOP | WHITE, + QUEEN | WHITE, KING | WHITE, BISHOP | WHITE, KNIGHT | WHITE, ROOK | WHITE}; + +// Global variables +FuncPtr MoveFunction; // Function that is called for each move +int movenum; // current move number +int person_old; // postion selected by person to move from +int person_new; // postion selected by person to move to +int playdepth; // number of moves to look ahead +int validmove; // indicates if a human's move is valid +int compcolor; // color that the computer is playing +int human_playing; // indicates that a person is playing +char inbuf[80]; // buffer for human input + +// Prepare to search the next level +static void InitializeNextLevel(levelT *level, levelT *next_level) +{ + memcpy(next_level, level, sizeof(levelT)); // copy board to next level. + next_level->depth++; +} + +static void ChangeColor(levelT *level) +{ + level->color ^= COLOR_MASK; +} + +static int BoardValue(levelT *level) +{ + return level->value; +} + +// Convert a numeric position value to a string +char *PositionToString(int position) +{ + int row, col; + static char str[3]; + + row = position / BOARD_WIDTH; + col = position % BOARD_WIDTH; + + str[0] = col - 2 + 'a'; + str[1] = 10 - row + '0'; + str[2] = 0; + + return str; +} + +// Convert a postion string to a numeric value +int StringToPostion(char *str) +{ + unsigned int col = tolower(str[0]) - 'a'; + unsigned int row = str[1] - '1'; + + if (col > 7 || row > 7) return -1; + return (7 - row + 2) * BOARD_WIDTH + col + 2; +} + +// Print the board +void PrintBoard(levelT *level) +{ + int i, j; + uchar *ptr = level->board + (BOARD_WIDTH * BOARD_BORDER); + + printf("\n "); + for (i = 'a'; i <= 'h'; i++) printf("|%c ", i); + printf("|"); + for (i = 0; i < 8; i++) + { + printf("\n-+--+--+--+--+--+--+--+--+"); + printf("\n%c", '8' - i); + for (j = 2; j < 10; j++) + { + if (ptr[j]) + { + printf("|%c", symbols[ptr[j] & PIECE_MASK]); + if (ptr[j] & COLOR_MASK) + printf("W"); + else + printf("B"); + } + else if ((i^j)&1) + printf("|--"); + else + printf("| "); + } + printf("|"); + ptr += BOARD_WIDTH; + } + printf("\n-+--+--+--+--+--+--+--+--+"); + printf("\n\n"); +} + +// Determine if the board position contains the current color's piece +int IsMyPiece(levelT *level, int offs) +{ + uchar *brd = level->board; + + if (brd[offs] == 0) return 0; + if (brd[offs] == OUT_OF_BOUNDS) return 0; + return ((brd[offs] & COLOR_MASK) == level->color); +} + +// Determine if the board position contains the other color's piece +int IsOtherPiece(levelT *level, int offs) +{ + uchar *brd = level->board; + + if (brd[offs] == 0) return 0; + if (brd[offs] == OUT_OF_BOUNDS) return 0; + return ((brd[offs] & COLOR_MASK) != level->color); +} + +// Determine if the board position does not contain the +// current color's piece and is in bounds. +int IsMoveOK(levelT *level, int offs) +{ + uchar *brd = level->board; + return (!IsMyPiece(level, offs) && brd[offs] != OUT_OF_BOUNDS); +} + +// Generate moves in a certain direction for a bishop, rook or queen +void AnalyzeDirectionalMoves(levelT *level, int direction) +{ + uchar *brd = level->board; + + level->new_pos = level->old_pos; + while (1) + { + level->new_pos += direction; + if (IsMyPiece(level, level->new_pos) || brd[level->new_pos] == OUT_OF_BOUNDS) break; + (*MoveFunction)(level); + if (brd[level->new_pos] != 0) break; + } +} + +// Determine if the king's space is under attack +int IsCheck(levelT *level) +{ + int color = level->color; + uchar *king_ptr = level->board; + int i, row_step, offs, incr, flags, piece; + + if (color) + { + row_step = -BOARD_WIDTH; + king_ptr += level->wking_pos; + } + else + { + row_step = BOARD_WIDTH; + king_ptr += level->bking_pos; + } + + color ^= COLOR_MASK; + + // Check for pawns + if ((king_ptr[row_step + 1] & (COLOR_MASK | PIECE_MASK)) == (color | PAWN)) + return 1; + + if ((king_ptr[row_step - 1] & (COLOR_MASK | PIECE_MASK)) == (color | PAWN)) + return 1; + + // Check for knights + for (i = 0; i < 8; i++) + { + if ((king_ptr[knight_moves[i]] & (COLOR_MASK | PIECE_MASK)) == (color | KNIGHT)) + return 1; + } + + // Check for king, queen, bishop or rook + for (i = 0; i < 8; i++) + { + offs = incr = king_moves[i]; + + if ((king_ptr[offs] & (COLOR_MASK | PIECE_MASK)) == (color | KING)) + return 1; + + while (king_ptr[offs] == 0) + offs += incr; + + flags = king_ptr[offs]; + + if ((flags & COLOR_MASK) != color) + continue; + + piece = flags & PIECE_MASK; + + if (piece == QUEEN) + return 1; + + if (i < 4) + { + if (piece == BISHOP) + return 1; + } + else + { + if (piece == ROOK) + return 1; + } + } + return 0; +} + +// This routine catches invalid piece values, and should never be called +void Invalid(levelT *level) +{ + printf("Invalid piece\n"); + exit(1); +} + +// Generate all possible moves for a pawn +void Pawn(levelT *level) +{ + int row_step = BOARD_WIDTH; + uchar *brd = level->board; + + if (level->color) + row_step = -BOARD_WIDTH; + + // Check capture to the left + level->new_pos = level->old_pos - 1 + row_step; + if (IsOtherPiece(level, level->new_pos) || level->en_passant == level->old_pos - 1) + (*MoveFunction)(level); + + // Check capture to the right + level->new_pos += 2; + if (IsOtherPiece(level, level->new_pos) || level->en_passant == level->old_pos + 1) + (*MoveFunction)(level); + + // Check moving forward one space + level->new_pos -= 1; + if (IsMoveOK(level, level->new_pos) && !IsOtherPiece(level, level->new_pos)) + (*MoveFunction)(level); + else + return; + + // Check moving forward two spaces + if (brd[level->old_pos] & PIECE_MOVED) + return; + + level->new_pos += row_step; + if (IsMoveOK(level, level->new_pos) && !IsOtherPiece(level, level->new_pos)) + (*MoveFunction)(level); +} + +// Generate all possible moves for a knight +void Knight(levelT *level) +{ + int i; + uchar *brd = level->board; + + for (i = 0; i < 8; i++) + { + level->new_pos = level->old_pos + knight_moves[i]; + if (IsMoveOK(level, level->new_pos)) + (*MoveFunction)(level); // then generate move + } +} + +// Generate all possible moves for a bishop +void Bishop(levelT *level) +{ + int i; + + for (i = 0; i < 4; i++) + AnalyzeDirectionalMoves(level, king_moves[i]); +} + +// Generate all possible moves for a rook +void Rook(levelT *level) +{ + int i; + uchar *brd = level->board; + + for (i = 4; i < 8; i++) + AnalyzeDirectionalMoves(level, king_moves[i]); +} + +// Generate all possible moves for a queen +void Queen(levelT *level) +{ + int i; + + for (i = 0; i < 8; i++) + AnalyzeDirectionalMoves(level, king_moves[i]); +} + +// Determine if this space is under attack +int IsSpaceUnderAttack(levelT *level, int position) +{ + int retval, king_pos; + + if (level->color) + { + king_pos = level->wking_pos; + level->wking_pos = position; + retval = IsCheck(level); + level->wking_pos = king_pos; + } + else + { + king_pos = level->bking_pos; + level->bking_pos = position; + retval = IsCheck(level); + level->bking_pos = king_pos; + } + + return retval; +} + +void CastleRight(levelT *level) +{ + uchar *brd = level->board; + int old_pos = level->old_pos; + + if (brd[old_pos + 1]) return; + if (brd[old_pos + 2]) return; + if (brd[old_pos + 3] & PIECE_MOVED) return; + if (IsCheck(level)) return; + if (IsSpaceUnderAttack(level, level->old_pos + 1)) return; + level->new_pos = level->old_pos + 2; + (*MoveFunction)(level); +} + +void CastleLeft(levelT *level) +{ + uchar *brd = level->board; + int old_pos = level->old_pos; + + if (brd[old_pos - 1]) return; + if (brd[old_pos - 2]) return; + if (brd[old_pos - 3]) return; + if (brd[old_pos - 4] & PIECE_MOVED) return; + if (IsCheck(level)) return; + if (IsSpaceUnderAttack(level, level->old_pos - 1)) return; + level->new_pos = level->old_pos - 2; + (*MoveFunction)(level); +} + +// Generate all possible moves for a king +void King(levelT *level) +{ + int i; + uchar *brd = level->board; + + // Check 8 single-space moves + for (i = 0; i < 8; i++) + { + level->new_pos = level->old_pos + king_moves[i]; + if (IsMoveOK(level, level->new_pos)) + (*MoveFunction)(level); + } + + // Check castling + if (!(brd[level->old_pos] & PIECE_MOVED)) + { + CastleRight(level); + CastleLeft(level); + } +} + +FuncPtr PieceFunctions[8] = {Invalid, Pawn, Knight, Bishop, Rook, Queen, King, Invalid}; + +// Call the piece move generator function if this the color is correct +void MoveIfMyPiece(levelT *level) +{ + int piece; + uchar *brd = level->board; + + if (IsMyPiece(level, level->old_pos)) + { + piece = brd[level->old_pos] & PIECE_MASK; + (*PieceFunctions[piece])(level); + } +} + +// Generate all moves on the board and analyze them +void AnalyzeAllMoves(levelT *level) +{ + int row, col, rowinc; + + if (level->color == BLACK) + { + level->old_pos = POSITION_A8; // start at the left top + rowinc = BOARD_WIDTH - 8; + } + else + { + level->old_pos = POSITION_A1; // start at the left bottom + rowinc = -BOARD_WIDTH - 8; + } + + for (row = 0; row < 8; row++) + { + for (col = 0; col < 8; col++) + { + MoveIfMyPiece(level); + level->old_pos++; + } + level->old_pos += rowinc; + } +} + +// Remove a piece from the board and subtract its value +void RemovePiece(levelT *level, int position) +{ + uchar *brd = level->board; + int entry = brd[position]; + int piece = entry & PIECE_MASK; + int value = values[piece]; + + if (entry == 0) return; + + if (entry == OUT_OF_BOUNDS) + { + printf("RemovePiece: %d is out of bounds\n", position); + exit(1); + } + + if (entry & COLOR_MASK) + { + value += pos_values[piece][position - (2 * BOARD_WIDTH)]; + level->value -= value; + } + else + { + value += pos_values[piece][(BOARD_WIDTH * 10) - 1 - position]; + level->value += value; + } + + brd[position] = 0; +} + +// Add a piece to the board and add its value +void AddPiece(levelT *level, int position, int entry) +{ + uchar *brd = level->board; + int piece = entry & PIECE_MASK; + int value = values[piece]; + + if (brd[position]) + { + printf("AddPiece: %d occupied\n", position); + exit(1); + } + + if (brd[position]) + { + printf("RemovePiece: %d is out of bounds\n", position); + exit(1); + } + + if (entry & COLOR_MASK) + { + value += pos_values[piece][position - (2 * BOARD_WIDTH)]; + level->value += value; + } + else + { + value += pos_values[piece][(BOARD_WIDTH * 10) - 1 - position]; + level->value -= value; + } + + brd[position] = entry | PIECE_MOVED; +} + +// Move a piece from one place to another and adjust the board's value +void MovePiece(levelT *level, int old_pos, int new_pos) +{ + uchar *brd = level->board; + int entry1 = brd[old_pos]; + int entry2 = brd[new_pos]; + int piece = entry1 & PIECE_MASK; + int value = values[piece]; + + if (entry1 == 0) return; + + if (entry1 == OUT_OF_BOUNDS) + { + printf("MovePiece: %d is out of bounds\n", old_pos); + exit(1); + } + + if (entry2) + RemovePiece(level, new_pos); + + RemovePiece(level, old_pos); + AddPiece(level, new_pos, entry1); +} + +// Move a piece and remove an opponent's piece if taken +// Check for castling, en passant capture and pawn promotion +void PerformMove(levelT *level) +{ + uchar *brd = level->board; + int val1 = brd[level->old_pos]; + int val2 = brd[level->new_pos]; + int en_passant = level->en_passant; + int value = values[val2 & PIECE_MASK]; + + // Clear en_passant flag. May be set later. + level->en_passant = 0; + + // Check if taking opponent's piece + if (val2) RemovePiece(level, level->new_pos); + + // Check if moving king + if ((val1 & PIECE_MASK) == KING) + { + // Update it's position + if (val1 & COLOR_MASK) + level->wking_pos = level->new_pos; + else + level->bking_pos = level->new_pos; + + // Check for castle right + if (level->new_pos == level->old_pos + 2) + { + if (level->depth == 0) printf("CASTLE RIGHT\n\n"); + MovePiece(level, level->old_pos + 3, level->old_pos + 1); + } + + // Check for castle left + if (level->new_pos == level->old_pos - 2) + { + if (level->depth == 0) printf("CASTLE LEFT\n\n"); + MovePiece(level, level->old_pos - 4, level->old_pos - 1); + } + } + + if ((val1 & PIECE_MASK) == PAWN) + { + if (val1 & COLOR_MASK) + { + // Set the en passant flag if moving pawn two spaces + if (level->new_pos == level->old_pos - (2 * BOARD_WIDTH)) + { + level->en_passant = level->new_pos; + } + // Check for en passant capture + else if (level->new_pos == en_passant - BOARD_WIDTH) + { + if (level->depth == 0) printf("EN PASSANT\n\n"); + RemovePiece(level, en_passant); + } + // Promote pawn to queen if reaching final rank + else if (level->new_pos <= POSITION_H8) + { + RemovePiece(level, level->old_pos); + AddPiece(level, level->new_pos, WHITE | QUEEN); + return; + } + } + else + { + // Set the en passant flag if moving pawn two spaces + if (level->new_pos == level->old_pos + (2 * BOARD_WIDTH)) + { + level->en_passant = level->new_pos; + } + // Check for en passant capture + else if (level->new_pos == en_passant + BOARD_WIDTH) + { + if (level->depth == 0) printf("EN PASSANT\n\n"); + RemovePiece(level, en_passant); + } + // Promote pawn to queen if reaching final rank + else if (level->new_pos >= POSITION_A1) + { + RemovePiece(level, level->old_pos); + AddPiece(level, level->new_pos, BLACK | QUEEN); + return; + } + } + } + + MovePiece(level, level->old_pos, level->new_pos); +} + +#ifdef DEBUG +int IsBoardValid(levelT *level) +{ + int row, col; + uchar *ptr = level->board; + + for (row = 0; row < BOARD_HEIGHT; row++) + { + for (col = 0; col < BOARD_WIDTH; col++) + { + if (*ptr == 0) {} + else if (*ptr == OUT_OF_BOUNDS) + { + if (row >= 2 && row < 10 && col >= 2 && col < 10) + return 0; + } + else if (*ptr == 0x80) + return 0; + else if (*ptr & 0x38) + return 0; + else if (((*ptr) & PIECE_MASK) == 7) + return 0; + ptr++; + } + } + return 1; +} +#endif + +// Analyze move from old_pos to new_pos. If we have reached the maximum depth +// check if the board value is better than the values from the previous moves. +// If we have not reached the maximum depth, determine the best counter-move +// at the next level, and check if better than any previous move. +// In the case of a tie, pick the new move 25% of the time. +void AnalyzeMove(levelT *level) +{ + levelT next_level; + int update, value; + uchar *ptr = level->board; + + if (ptr[level->old_pos] == OUT_OF_BOUNDS || ptr[level->new_pos] == OUT_OF_BOUNDS) + { + printf("BAD MOVE: %2.2x-%2.2x\n", level->old_pos, level->new_pos); + exit(0); + } + + InitializeNextLevel(level, &next_level); + PerformMove(&next_level); + + if (IsCheck(&next_level)) return; + +#ifdef DEBUG + if (!IsBoardValid(level)) + { + printf("BAD BOARD!\n"); + exit(0); + } +#endif + + value = BoardValue(&next_level); + + // Stop searching if checkmate + if (value > 5000 || value < -5000) + { + level->best_value = value; + level->best_old = level->old_pos; + level->best_new = level->new_pos; + } + else if (next_level.depth == playdepth) + { + if (level->color) + update = (value > level->best_value); + else + update = (value < level->best_value); + + if (update) + { + level->best_value = value; + level->best_old = level->old_pos; + level->best_new = level->new_pos; + } + } + else + { + ChangeColor(&next_level); + if (next_level.color == BLACK) + next_level.best_value = 0x7fff; + else + next_level.best_value = -0x7fff; + next_level.best_old = 0; + next_level.best_new = 0; + + AnalyzeAllMoves(&next_level); + + if (!next_level.best_old) + { + // Check for check + if (IsCheck(&next_level)) + { + if (level->color) + next_level.best_value = 10000; + else + next_level.best_value = -10000; + } + else + next_level.best_value = 0; // Should go for draw only if way behind + } + + value = next_level.best_value; + + if (value == level->best_value) + { + update = ((rand() & 3) == 0); + } + else + { + if (level->color) + update = (value > level->best_value); + else + update = (value < level->best_value); + } + + if (update) + { + level->best_value = value; + level->best_old = level->old_pos; + level->best_new = level->new_pos; + } + } +} + +int IsCheckMate(levelT *level) +{ + int retval = 0; + int playdepth_save = playdepth; + + playdepth = 2; + + MoveFunction = AnalyzeMove; + if (level->color == BLACK) + level->best_value = 0x7fff; + else + level->best_value = -0x7fff; + level->best_old = 0; + level->best_new = 0; + + AnalyzeAllMoves(level); + + if (level->best_value > 5000 || level->best_value < -5000) + { + printf("CHECKMATE\n"); + retval = 1; + } + + playdepth = playdepth_save; + + return retval; +} + +// Analyze all possible moves and select the best one +int PerformComputerMove(levelT *level) +{ + int value; + +#if NUM_PTHREADS + GenerateQueuedMoves(level); +#endif + + MoveFunction = AnalyzeMove; + if (level->color == BLACK) + level->best_value = 0x7fff; + else + level->best_value = -0x7fff; + level->best_old = 0; + level->best_new = 0; + +#if NUM_PTHREADS + AnalyzeMoveQueue(level); +#else + AnalyzeAllMoves(level); +#endif + + // Check if best_old was updated, which indicates at least one move + if (level->best_old) + { + if (level->best_old == level->best_new) + printf("STALEMATE\n"); + level->old_pos = level->best_old; + level->new_pos = level->best_new; + PerformMove(level); + } + else + { + printf("Couldn't find a move\n"); + printf("STALEMATE\n"); + } + + value = BoardValue(level); + + if (value > 5000 || value < -5000) + { + printf("CHECKMATE\n"); + return 0; + } + + if (movenum > 200) + { + printf("STALEMATE\n"); + return 0; + } + + if (level->color) + printf("White's Move %d: ", ++movenum); + else + printf("Blacks's Move %d: ", movenum); + printf(" %s", PositionToString(level->best_old)); + printf("-%s\n", PositionToString(level->best_new)); + // printf("value = %d, best_value = %d\n", value, level->best_value); + + return 1; +} + +// Check if the person's intended move matches any generated moves +void CheckPersonMove(levelT *level) +{ + levelT next_level; + + // Is there a match? + if ((level->old_pos == person_old) && (person_new == level->new_pos)) + { + InitializeNextLevel(level, &next_level); + PerformMove(&next_level); + if (!IsCheck(&next_level)) + { + validmove = 1; + memcpy(level, &next_level, sizeof(levelT)); + } + } +} + +// Prompt for a move and check if it's valid +int PerformPersonMove(levelT *level) +{ + levelT next_level; + + validmove = 0; + + if (level->color) + movenum++; + + // Loop until we get a valid move + while (!validmove) + { + if (level->color) + printf("White's Move %d: ", movenum); + else + printf("Blacks's Move %d: ", movenum); + + gets(inbuf); + if (toupper(inbuf[0]) == 'Q') + __builtin_propeller_clkset(128); // return 0; + + person_old = StringToPostion(inbuf); + person_new = StringToPostion(inbuf + 3); + + if (person_old >= 0 && person_new >= 0 && inbuf[2] == '-') + { + MoveFunction = CheckPersonMove; + level->old_pos = person_old; + memcpy(&next_level, level, sizeof(levelT)); + MoveIfMyPiece(&next_level); + } + } + + next_level.depth = 0; + memcpy(level, &next_level, sizeof(levelT)); + + return 1; +} + +// Prompt person for color, and set the computer's color +static void GetColor() +{ + printf("Do you want White (Y/N): "); + gets(inbuf); + compcolor = (toupper(inbuf[0]) == 'Y') ? BLACK : WHITE; +} + +// Prompt for the playing level +static void GetPlayLevel() +{ + playdepth = 0; + while (playdepth < 1 || playdepth > MAX_DEPTH) + { + printf("Enter Play Level (1-%d): ", MAX_DEPTH); + gets(inbuf); + sscanf(inbuf, "%d", &playdepth); + } +} + +void Initialize(levelT *level) +{ + uchar *ptr = level->board + POSITION_A8; + + memset(level->board, OUT_OF_BOUNDS, BOARD_SIZE); + + memcpy(ptr, black_rank, 8); + memset(ptr + BOARD_WIDTH, BLACK | PAWN, 8); + memset(ptr + (BOARD_WIDTH * 2), 0, 8); + memset(ptr + (BOARD_WIDTH * 3), 0, 8); + memset(ptr + (BOARD_WIDTH * 4), 0, 8); + memset(ptr + (BOARD_WIDTH * 5), 0, 8); + memset(ptr + (BOARD_WIDTH * 6), WHITE | PAWN, 8); + memcpy(ptr + (BOARD_WIDTH * 7), white_rank, 8); + + movenum = 0; + level->depth = 0; + level->color = WHITE; + level->value = 0; + level->wking_pos = POSITION_A1 + 4; + level->bking_pos = POSITION_A8 + 4; + level->en_passant = 0; +} + +void PlayChess() +{ + int retval; + levelT level; + + GetPlayLevel(); + printf("Do you want to play against the computer? (Y/N): "); + gets(inbuf); + human_playing = (toupper(inbuf[0]) == 'Y'); + + if (human_playing) + GetColor(); + else + compcolor = WHITE; + + Initialize(&level); + PrintBoard(&level); + + while (1) + { + if (compcolor == level.color) + retval = PerformComputerMove(&level); + else + retval = PerformPersonMove(&level); + + if (!retval) return; + + PrintBoard(&level); + if (IsCheck(&level)) + printf("Illegal move into check %d\n", level.color); + ChangeColor(&level); + if (IsCheck(&level)) + { + if (IsCheckMate(&level)) break; + printf("CHECK\n\n"); + } + + if (!human_playing) + compcolor ^= COLOR_MASK; + } +} + +int main() +{ +#ifdef __PROPELLER__ + sleep(1); +#endif + + printf("Threaded Chess\n"); + + srand(1); + +#if NUM_PTHREADS + StartPthreads(); +#endif + + while (1) + PlayChess(); + + return 1; +} + +// **************************************************************** +// Pthread code +// **************************************************************** +#if NUM_PTHREADS +int queue_max = 0; +int queue_num; +int queue_index; +uchar queue_old[200]; +uchar queue_new[200]; + +#ifdef __PROPELLER__ +int queue_lock; +#else +pthread_mutex_t queue_lock; +#endif +levelT thread_level[NUM_PTHREADS]; +volatile int thread_active[NUM_PTHREADS]; + +pthread_t threads[NUM_PTHREADS]; +#ifdef __PROPELLER__ +int stacks[NUM_PTHREADS][PTHREAD_STACKSIZE/4]; +int mainstackstart = 0; +int mainstackend; + +void InitializeMainStack() +{ + int i; + int retval = (int)malloc(4); + + if (retval) + { + mainstackstart = (retval & ~3) + 80; + mainstackend = ((int)&retval) - 80; + free((void *)retval); + retval = (int)(&retval) - retval; + for (i = mainstackstart; i < mainstackend; i += 4) + *(int *)i = 0xdeadbeef; + } + + //printf("Main stack space = %d bytes\n", retval); +} + +void CheckPthreadStacks() +{ + int i, j; + + for (j = mainstackstart; j < 0x8000; j += 4) + { + if (*(int *)j != 0xdeadbeef) break; + } + if (j - mainstackstart < 100) + printf("Main stack space available = %d bytes\n", j - mainstackstart); + + for (i = 0; i < NUM_PTHREADS; i++) + { + for (j = 0; j < PTHREAD_STACKSIZE/4; j++) + { + if (stacks[i][j] != 0xdeadbeef) break; + } + if (j * 4 < 100) + printf("Pthread %d stack space available = %d bytes\n", i, j * 4); + } +} +#endif + +void GenerateQueuedMoves(levelT *level) +{ + MoveFunction = QueueUpMove; + queue_num = 0; + AnalyzeAllMoves(level); + if (queue_max < queue_num) + queue_max = queue_num; + // printf("%d moves queued, %d max queued\n", queue_num, queue_max); +} + +static int GetQueuedItem() +{ + int index; + +#ifdef __PROPELLER__ + while (lockset(queue_lock)); + index = queue_index; + if (queue_index < queue_num) queue_index++; + lockclr(queue_lock); +#else + pthread_mutex_lock(&queue_lock); + index = queue_index; + if (queue_index < queue_num) queue_index++; + pthread_mutex_unlock(&queue_lock); +#endif + + return index; +} + +void ProcessMoveQueue(levelT *level) +{ + int index; + + while (1) + { + index = GetQueuedItem(); + if (index >= queue_num) break; + level->old_pos = queue_old[index]; + level->new_pos = queue_new[index]; + (*MoveFunction)(level); + } +} + +// This routine is run in all the pthreads +void *ThreadFunc(void *arg) +{ + int instance = (int)arg; + + while (1) + { + usleep(1000); + if (!thread_active[instance]) continue; + ProcessMoveQueue(&thread_level[instance]); + thread_active[instance] = 0; + } + + return 0; +} + +// This routine is run by the main thread +void AnalyzeMoveQueue(levelT *level) +{ + int index, update; + + queue_index = 0; + + // Copy level to other thread's level vars, and set active flag + for (index = 0; index < NUM_PTHREADS; index++) + { + memcpy(&thread_level[index], level, sizeof(levelT)); + thread_active[index] = 1; + } + ProcessMoveQueue(level); +#if 0 + printf("Analyze %s", PositionToString(level->old_pos)); + printf("-%s\n", PositionToString(level->new_pos)); +#endif + + // Wait for all the ptheads to finish + while (1) + { + for (index = 0; index < NUM_PTHREADS; index++) + { + if (thread_active[index]) break; + } + if (index == NUM_PTHREADS) break; + } + +#ifdef __PROPELLER__ + CheckPthreadStacks(); +#endif + + // Find the best move from the different threads + for (index = 0; index < NUM_PTHREADS; index++) + { + if (level->color) + update = (thread_level[index].best_value > level->best_value); + else + update = (thread_level[index].best_value < level->best_value); + + if (update) + { + level->best_value = thread_level[index].best_value; + level->best_old = thread_level[index].best_old; + level->best_new = thread_level[index].best_new; + } + } +} + +#ifdef __PROPELLER__ +void StartPthreads(void) +{ + int i, j; + pthread_attr_t attr; + + for (i = 0; i < NUM_PTHREADS; i++) + { + thread_active[i] = 0; + pthread_attr_init(&attr); + pthread_attr_setstacksize(&attr, PTHREAD_STACKSIZE); + pthread_attr_setstackaddr(&attr, stacks[i]); + for (j = 0; j < PTHREAD_STACKSIZE/4; j++) + stacks[i][j] = 0xdeadbeef; + + if (pthread_create(&threads[i], &attr, ThreadFunc, (void *)i)) + { + printf("pthread_create %d failed\n", i); + exit(1); + } + } + + InitializeMainStack(); + queue_lock = locknew(); +} +#else +void StartPthreads() +{ + int i; + for (i = 0; i < NUM_PTHREADS; i++) + { + thread_active[i] = 0; + if (pthread_create(&threads[i], 0, ThreadFunc, (void *)i)) + { + printf("pthread_create %d failed\n", i); + exit(1); + } + } + pthread_mutex_init(&queue_lock, NULL); +} +#endif + +void QueueUpMove(levelT *level) +{ + levelT next_level; + uchar *ptr = level->board; + + if (ptr[level->old_pos] == OUT_OF_BOUNDS || ptr[level->new_pos] == OUT_OF_BOUNDS) + { + printf("BAD MOVE: %2.2x-%2.2x\n", level->old_pos, level->new_pos); + exit(0); + } + + InitializeNextLevel(level, &next_level); + PerformMove(&next_level); + + if (IsCheck(&next_level)) return; + +#ifdef DEBUG + if (!IsBoardValid(level)) + { + printf("BAD BOARD!\n"); + exit(0); + } +#endif + + queue_old[queue_num] = level->old_pos; + queue_new[queue_num] = level->new_pos; + queue_num++; +} +#endif + +/* ++-------------------------------------------------------------------- +| TERMS OF USE: MIT License ++-------------------------------------------------------------------- +Permission is hereby granted, free of charge, to any person obtaining +a copy of this software and associated documentation files +(the "Software"), to deal in the Software without restriction, +including without limitation the rights to use, copy, modify, merge, +publish, distribute, sublicense, and/or sell copies of the Software, +and to permit persons to whom the Software is furnished to do so, +subject to the following conditions: + +The above copyright notice and this permission notice shall be +included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. ++------------------------------------------------------------------ +*/ diff --git a/chess/chess.side b/chess/chess.side new file mode 100644 index 0000000..a298223 --- /dev/null +++ b/chess/chess.side @@ -0,0 +1,8 @@ +chess.c +>compiler=C +>memtype=lmm main ram +>optimize=-Os +>-fno-exceptions +>-ltiny +>-lpthread +>BOARD::C3F diff --git a/devel/fibo.spn b/devel/fibo.spn new file mode 100644 index 0000000..bda2681 --- /dev/null +++ b/devel/fibo.spn @@ -0,0 +1,31 @@ +con + _clkmode = $62 + _clkfreq = $4E495053 ' SPIN + +obj + c : "clibsd" + +pub main(argc, argv) | n, startTime, endTime, executionTime, rawTime + c.enter(argc, argv) + c.printf0(string("hello, world!\n")) + n := 0 + repeat while n =< 26 + c.printf1(string("fibo(%d) = "), n) + startTime := clock + result := fibo(n) + endTime := clock + rawTime := endTime - startTime + executionTime := rawTime / (CLKFREQ / 1000) + c.printf3(string("%d (%dms) (%d ticks)\n"), result, executionTime, rawTime) + n++ + c.exit(0) + +pub clock + result := cnt + +pub fibo(n) + if n < 2 + return n + else + return fibo(n - 1) + fibo(n - 2) + diff --git a/devel/hello.spn b/devel/hello.spn new file mode 100644 index 0000000..d48ee56 --- /dev/null +++ b/devel/hello.spn @@ -0,0 +1,11 @@ +con + _clkmode = $62 + _clkfreq = $4E495053 ' SPIN + +obj + c : "clibsd" + +pub main(argc, argv) + c.enter(argc, argv) + c.printf0(string("Hello World\n")) + c.exit(0) diff --git a/devel/makepong b/devel/makepong new file mode 100644 index 0000000..eb0cd84 --- /dev/null +++ b/devel/makepong @@ -0,0 +1,7 @@ +#shell +echo spinit pong +spinit pong +echo spasm pong +spasm pong +echo splink pong.bin pongclib.bin pong +splink pong.bin pongclib.bin pong diff --git a/devel/makepong.all b/devel/makepong.all new file mode 100644 index 0000000..4022b3e --- /dev/null +++ b/devel/makepong.all @@ -0,0 +1,13 @@ +#shell +echo spinit pongsubs +spinit pongsubs +echo spasm pongsubs +spasm pongsubs +echo splink pongsubs.bin clibsd.bin pongclib.bin +splink pongsubs.bin clibsd.bin pongclib.bin +echo spinit pong +spinit pong +echo spasm pong +spasm pong +echo splink pong.bin pongclib.bin pong +splink pong.bin pongclib.bin pong diff --git a/devel/makepong.sub b/devel/makepong.sub new file mode 100644 index 0000000..315efa3 --- /dev/null +++ b/devel/makepong.sub @@ -0,0 +1,7 @@ +#shell +echo spinit pongsubs +spinit pongsubs +echo spasm pongsubs +spasm pongsubs +echo splink pongsubs.bin clibsd.bin pongclib.bin +splink pongsubs.bin clibsd.bin pongclib.bin diff --git a/devel/pong.spn b/devel/pong.spn new file mode 100644 index 0000000..ce8e84d --- /dev/null +++ b/devel/pong.spn @@ -0,0 +1,174 @@ +CON + _clkmode = $62 + _clkfreq = $4E495053 ' SPIN + + height = 3 + xmax = 78 + xmin = 1 + ymax = 23 + ymin = 1 + +OBJ + p : "pongsubs" + c : "clibsd" + +DAT + left long 0 + right long 0 + config long 0 + x long 0 + y long 0 + xvel long 0 + yvel long 0 + score0 long 0 + score1 long 0 + center long 0 + xold long 1 + yold long 1 + +pub main(argc, argv) | time, deltat + c.enter(argc, argv) + p.splash + initialize + deltat := clkfreq/20 + time := cnt + repeat + check_input + update_position + check_score + p.plotit(xold, yold, p.getval(xold, yold)) + p.plotit(x, y, "@") + xold := x + yold := y + time += deltat + repeat while (cnt - time) < deltat + c.setconfig(config) + c.putchar(13) + c.exit(0) + +pub initialize | i + c.putchar(0) + config := c.getconfig + c.setconfig(1) + center := (xmin + xmax)/2 + p.updatevars(ymin, center, score0, score1) + x := 1 + y := 10 + xvel := 1 + yvel := 1 + left := (ymin + ymax)/2 + right := left + p.moveto(xmin-1, ymin-1) + i := xmin-1 + repeat while i++ =< xmax + 1 + c.putchar("#") + p.moveto(xmin-1, ymax+1) + i := xmin-1 + repeat while i++ =< xmax + 1 + c.putchar("#") + i := ymin + repeat while i =< ymax + p.moveto(center, i) + c.putchar(".") + i++ + p.plotpaddle(xmin-1, left, height) + p.plotpaddle(xmax+1, right, height) + p.putnum(center-8, ymin+1, 0) + p.putnum(center+3, ymin+1, 0) + p.moveto(xmin+1, ymin) + +pub update_position + x += xvel + y += yvel + if y < ymin + y := ymin - y + yvel := 0 - yvel + c.putchar(7) + elseif y > ymax + y := 2 * ymax - y + yvel := 0 - yvel + c.putchar(7) + +pub check_score + if x < xmin + if y =< left + height and y => left - height + x := xmin - x + xvel := 0 - xvel + c.putchar(7) + else + scoreit(1) + elseif x > xmax + if y =< right + height and y => right - height + x := 2 * xmax - x + xvel := 0 - xvel + c.putchar(7) + else + scoreit(0) + if y =< left + height and y => left - height + +pub check_input | val + repeat while p.kbhit + val := c.getchar + if val == "x" + c.putchar(0) + c.setconfig(config) + c.exit(0) + if val == "q" + move_left_up + move_left_up + if val == "a" + move_left_down + move_left_down + if val == "p" + move_right_up + move_right_up + if val == "l" + move_right_down + move_right_down + +pub move_left_up + if left > ymin + height + p.plotit(xmin-1, left + height, " ") + left-- + p.plotit(xmin-1, left - height, "#") + +pub move_left_down + if left < ymax - height + p.plotit(xmin-1, left - height, " ") + left++ + p.plotit(xmin-1, left + height, "#") + +pub move_right_up + if right > ymin + height + p.plotit(xmax+1, right + height, " ") + right-- + p.plotit(xmax+1, right - height, "#") + +pub move_right_down + if right < ymax - height + p.plotit(xmax+1, right - height, " ") + right++ + p.plotit(xmax+1, right + height, "#") + +pub scoreit(player) + if player + score1++ + p.putnum(center+3, ymin+1, score1) + if right > (ymin+ymax)/2 + y := right - height - 3 + else + y := right + height + 3 + x := xmax + 1 + xvel := 0 - 1 + else + score0++ + p.putnum(center-8, ymin+1, score0) + if left > (ymin+ymax)/2 + y := left - height - 3 + else + y := left + height + 3 + x := xmin - 1 + xvel := 1 + yvel := 1 + p.updatevars(ymin, center, score0, score1) + diff --git a/devel/pongsubs.spn b/devel/pongsubs.spn new file mode 100644 index 0000000..be0b29f --- /dev/null +++ b/devel/pongsubs.spn @@ -0,0 +1,97 @@ +OBJ + c : "clibsd" + +dat + score0 long 0 + score1 long 0 + ymin long 0 + center long 0 + +pub splash + c.putchar(0) + c.printf0(string("PONG\n")) + c.printf0(string("----\n")) + c.printf0(string("Press 'q' and 'a' to move the")) + c.printf0(string(" left paddle up and down\n")) + c.printf0(string("Press 'p' and 'l' to move the")) + c.printf0(string(" right paddle up and down\n")) + c.printf0(string("Press 'x' to exit\n")) + c.printf0(string("Press any key to start\n")) + c.getchar + +pub updatevars(p_ymin, p_center, p_score0, p_score1) + ymin := p_ymin + center := p_center + score0 := p_score0 + score1 := p_score1 + +pub plotit(xpos, ypos, val) + moveto(xpos, ypos) + c.putchar(val) + +pub moveto(xpos, ypos) + c.putchar(2) + c.putchar(xpos) + c.putchar(ypos) + +pub kbhit | stream, handle, rxhead, rxtail + stream := c.getstdin + handle := long[stream][1] + rxhead := word[handle][6] + rxtail := word[handle][7] + return rxhead <> rxtail + +dat + digit + byte %111111, %110011, %110011, %110011, %111111 + byte %001100, %011100, %001100, %001100, %011110 + byte %111111, %000011, %111111, %110000, %111111 + byte %111111, %000011, %001111, %000011, %111111 + byte %110011, %110011, %111111, %000011, %000011 + byte %111111, %110000, %111111, %000011, %111111 + byte %111111, %110000, %111111, %110011, %111111 + byte %111111, %000011, %000110, %000110, %000110 + byte %111111, %110011, %111111, %110011, %111111 + byte %111111, %110011, %111111, %000011, %000011 + +pub getval(xpos, ypos) | ptr, val + if xpos == center + return "." + if ypos < ymin + 1 or ypos > ymin + 5 + return " " + if xpos < center - 8 or xpos > center + 8 + return " " + if xpos > center - 3 and xpos < center + 3 + return " " + if xpos < center + val := score0 + xpos -= center - 8 + else + val := score1 + xpos -= center + 3 + val := byte[@digit][val*5 + ypos - ymin - 1] + if val & ($20 >> xpos) + result := "#" + else + result := " " + +pub putnum(xpos, ypos, num) | ptr, temp, i, j + i := 5 + ptr := @digit + (num//10)*5 + repeat while i-- + j := 6 + temp := byte[ptr++] + moveto(xpos, ypos++) + repeat while j-- + if temp & $20 + c.putchar("#") + else + c.putchar(" ") + temp <<= 1 + +pub plotpaddle(xpos, ypos, height) | i + i := ypos - height + repeat while i =< ypos + height + plotit(xpos, i, "#") + i++ + diff --git a/devel/prime.spn b/devel/prime.spn new file mode 100644 index 0000000..409ed8e --- /dev/null +++ b/devel/prime.spn @@ -0,0 +1,62 @@ +con + _clkmode = $62 + _clkfreq = $4E495053 ' SPIN + +obj + c : "clibsd" + +dat + primes long $00020001, 0, 0, 0, 0, 0, 0, 0, 0, 0 + long 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 + long 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 + long 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 + long 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 + long 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 + long 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 + long 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 + long 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 + last long 0 + +pub main(argc, argv) | i, j, n, num, maxnum, count, prime + + c.enter(argc, argv) + + if argc <> 2 + c.printf0(string("usage: prime max_number\n")) + c.exit(1) + + maxnum := 0 + i := long[argv][1] + repeat while byte[i] + maxnum := (maxnum * 10) + byte[i++] - "0" + + c.printf1(string("%d "), 1) + c.printf1(string("%d "), 2) + + num := (@last - @primes)/2 + n := 2 + i := 3 + count := 2 + repeat while i < maxnum + j := 2 + repeat while j < n + prime := word[@primes][j] + if prime * prime > i + j := n + quit + if i / prime * prime == i + quit + j++ + if j == n + if n < num + word[@primes][n++] := i + if n == num and i * i < maxnum + maxnum := i * i + c.printf1(string("%d "), i) + if ++count => 10 + count := 0 + c.printf0(string("\n")) + i += 2 + if count + c.printf0(string("\n")) + c.exit(0) diff --git a/devel/readme.txt b/devel/readme.txt new file mode 100644 index 0000000..7519630 --- /dev/null +++ b/devel/readme.txt @@ -0,0 +1,47 @@ +This directory contains sample Spin programs that can be compiled by the Spin +development tools. These tools consist of spinit, spasm, splink and spc. + +spinit is a Spin compiler that produces Spin assmebly (spasm) code. It is +executed as follows: + +spinit hello.spn + +This will generate the Spasm source file, hello.spa. The Spasm assmebler +is executed as follows: + +spasm hello.spa + +This will assemble the hello.spa Spasm source file, and generate the +output file hello.bin. The hello programs uses the clibsd object. An +executable file can be created by linking hello.bin with clibsd.bin using +the Spin linke, splink. This is done as follows: + +splink hello.bin clibsd.bin hello + +You can then run hello as a normal Spinix program. + +An assembly listing can be produced by using the -l option, as follows: + +spasm -l hello.spa + +When running spinit and spasm, the file extensions are optional, and are +assumed to be .spn and .spa if not specified. + +A Spin program can be compiled into an executable with one command by using +the spc command. The hello pragram can be built by typing + +spc hello + +The will run spinit, spasm and splink. + +This directory contains the following sample programs: + +hello - Hello World Program +fibo - A Spin version of the PropGCC fibo program +prime - A program that prints prime numbers +pong - A simple pong program developed entirely under spinix +pongsubs - Object used by pong + +The pong program can be built using the script makepong.all. The pongsubs +object can be built and linked to clibsd using makepong.sub. makepong will +compile pong and link it to the combined pongsubs/clibsd object. diff --git a/filetest/Makefile b/filetest/Makefile new file mode 100644 index 0000000..b91e746 --- /dev/null +++ b/filetest/Makefile @@ -0,0 +1,20 @@ +CC=propeller-elf-gcc +MODEL=lmm +CFLAGS=-Os -m$(MODEL) +LDFLAGS=-m$(MODEL) +OBJDIR=./ +SRCDIR=./ + +OBJS = filetest.o printf.o spinix.o file_io.o +HDRS = + +filetest.elf: $(OBJS) + @$(CC) $(LDFLAGS) $(OBJS) -o $@ -s +binary: + propeller-load -s -D clkfreq=1347436867 -D clkmode=98 filetest.elf + +clean: + rm -f *.o *.elf *.binary + +$(OBJDIR)/%.o: $(SRCDIR)/%.c $(HDRS) + @$(CC) $(CFLAGS) -c $< -o $@ diff --git a/filetest/dosfs.h b/filetest/dosfs.h new file mode 100644 index 0000000..927f914 --- /dev/null +++ b/filetest/dosfs.h @@ -0,0 +1,394 @@ +/* + DOSFS Embedded FAT-Compatible Filesystem + (C) 2005 Lewin A.R.W. Edwards (sysadm@zws.com) + 11/5/2011 Added PROPGCC_MODS and DFS_DIRECTORY - Dave Hein +*/ + +#ifndef _DOSFS_H +#define _DOSFS_H + +#include + +#define PROPGCC_MODS /* This flag is used to enable changes for PropGCC */ + +//=================================================================== +// User-supplied functions +uint32_t DFS_ReadSector(uint8_t unit, uint8_t *buffer, uint32_t sector, uint32_t count); +uint32_t DFS_WriteSector(uint8_t unit, uint8_t *buffer, uint32_t sector, uint32_t count); + + +//=================================================================== +// Configurable items +#define MAX_PATH 64 // Maximum path length (increasing this will + // GREATLY increase stack requirements!) +#define DIR_SEPARATOR '/' // character separating directory components + +// End of configurable items +//=================================================================== + +//=================================================================== +// 32-bit error codes +#define DFS_OK 0 // no error +#define DFS_EOF 1 // end of file (not an error) +#define DFS_WRITEPROT 2 // volume is write protected +#define DFS_NOTFOUND 3 // path or file not found +#define DFS_PATHLEN 4 // path too long +#define DFS_ALLOCNEW 5 // must allocate new directory cluster +#define DFS_IOERR 6 // i/o error +#define DFS_INVAL 7 // invalid argument +#define DFS_ERRMISC 0xffffffff // generic error + +//=================================================================== +// File access modes +#define DFS_READ 1 // read-only +#define DFS_WRITE 2 // write-only +#ifdef PROPGCC_MODS +#define DFS_DIRECTORY 0x10 // directory-mode +#endif + +//=================================================================== +// Miscellaneous constants +#define SECTOR_SIZE 512 // sector size in bytes + +//=================================================================== +// Internal subformat identifiers +#define FAT12 0 +#define FAT16 1 +#define FAT32 2 + +//=================================================================== +// DOS attribute bits +#define ATTR_READ_ONLY 0x01 +#define ATTR_HIDDEN 0x02 +#define ATTR_SYSTEM 0x04 +#define ATTR_VOLUME_ID 0x08 +#define ATTR_DIRECTORY 0x10 +#define ATTR_ARCHIVE 0x20 +#define ATTR_LONG_NAME (ATTR_READ_ONLY | ATTR_HIDDEN | ATTR_SYSTEM | ATTR_VOLUME_ID) + + +/* + Directory entry structure + note: if name[0] == 0xe5, this is a free dir entry + if name[0] == 0x00, this is a free entry and all subsequent entries are free + if name[0] == 0x05, the first character of the name is 0xe5 [a kanji nicety] + + Date format: bit 0-4 = day of month (1-31) + bit 5-8 = month, 1=Jan..12=Dec + bit 9-15 = count of years since 1980 (0-127) + Time format: bit 0-4 = 2-second count, (0-29) + bit 5-10 = minutes (0-59) + bit 11-15= hours (0-23) +*/ +typedef struct _tagDIRENT { + uint8_t name[11]; // filename + uint8_t attr; // attributes (see ATTR_* constant definitions) + uint8_t reserved; // reserved, must be 0 + uint8_t crttimetenth; // create time, 10ths of a second (0-199 are valid) + uint8_t crttime_l; // creation time low byte + uint8_t crttime_h; // creation time high byte + uint8_t crtdate_l; // creation date low byte + uint8_t crtdate_h; // creation date high byte + uint8_t lstaccdate_l; // last access date low byte + uint8_t lstaccdate_h; // last access date high byte + uint8_t startclus_h_l; // high word of first cluster, low byte (FAT32) + uint8_t startclus_h_h; // high word of first cluster, high byte (FAT32) + uint8_t wrttime_l; // last write time low byte + uint8_t wrttime_h; // last write time high byte + uint8_t wrtdate_l; // last write date low byte + uint8_t wrtdate_h; // last write date high byte + uint8_t startclus_l_l; // low word of first cluster, low byte + uint8_t startclus_l_h; // low word of first cluster, high byte + uint8_t filesize_0; // file size, low byte + uint8_t filesize_1; // + uint8_t filesize_2; // + uint8_t filesize_3; // file size, high byte +} DIRENT, *PDIRENT; + +/* + Partition table entry structure +*/ +typedef struct _tagPTINFO { + uint8_t active; // 0x80 if partition active + uint8_t start_h; // starting head + uint8_t start_cs_l; // starting cylinder and sector (low byte) + uint8_t start_cs_h; // starting cylinder and sector (high byte) + uint8_t type; // type ID byte + uint8_t end_h; // ending head + uint8_t end_cs_l; // ending cylinder and sector (low byte) + uint8_t end_cs_h; // ending cylinder and sector (high byte) + uint8_t start_0; // starting sector# (low byte) + uint8_t start_1; // + uint8_t start_2; // + uint8_t start_3; // starting sector# (high byte) + uint8_t size_0; // size of partition (low byte) + uint8_t size_1; // + uint8_t size_2; // + uint8_t size_3; // size of partition (high byte) +} PTINFO, *PPTINFO; + +/* + Master Boot Record structure +*/ +typedef struct _tagMBR { + uint8_t bootcode[0x1be]; // boot sector + PTINFO ptable[4]; // four partition table structures + uint8_t sig_55; // 0x55 signature byte + uint8_t sig_aa; // 0xaa signature byte +} MBR, *PMBR; + +/* + BIOS Parameter Block structure (FAT12/16) +*/ +typedef struct _tagBPB { + uint8_t bytepersec_l; // bytes per sector low byte (0x00) + uint8_t bytepersec_h; // bytes per sector high byte (0x02) + uint8_t secperclus; // sectors per cluster (1,2,4,8,16,32,64,128 are valid) + uint8_t reserved_l; // reserved sectors low byte + uint8_t reserved_h; // reserved sectors high byte + uint8_t numfats; // number of FAT copies (2) + uint8_t rootentries_l; // number of root dir entries low byte (0x00 normally) + uint8_t rootentries_h; // number of root dir entries high byte (0x02 normally) + uint8_t sectors_s_l; // small num sectors low byte + uint8_t sectors_s_h; // small num sectors high byte + uint8_t mediatype; // media descriptor byte + uint8_t secperfat_l; // sectors per FAT low byte + uint8_t secperfat_h; // sectors per FAT high byte + uint8_t secpertrk_l; // sectors per track low byte + uint8_t secpertrk_h; // sectors per track high byte + uint8_t heads_l; // heads low byte + uint8_t heads_h; // heads high byte + uint8_t hidden_0; // hidden sectors low byte + uint8_t hidden_1; // (note - this is the number of MEDIA sectors before + uint8_t hidden_2; // first sector of VOLUME - we rely on the MBR instead) + uint8_t hidden_3; // hidden sectors high byte + uint8_t sectors_l_0; // large num sectors low byte + uint8_t sectors_l_1; // + uint8_t sectors_l_2; // + uint8_t sectors_l_3; // large num sectors high byte +} BPB, *PBPB; + +/* + Extended BIOS Parameter Block structure (FAT12/16) +*/ +typedef struct _tagEBPB { + uint8_t unit; // int 13h drive# + uint8_t head; // archaic, used by Windows NT-class OSes for flags + uint8_t signature; // 0x28 or 0x29 + uint8_t serial_0; // serial# + uint8_t serial_1; // serial# + uint8_t serial_2; // serial# + uint8_t serial_3; // serial# + uint8_t label[11]; // volume label + uint8_t system[8]; // filesystem ID +} EBPB, *PEBPB; + +/* + Extended BIOS Parameter Block structure (FAT32) +*/ +typedef struct _tagEBPB32 { + uint8_t fatsize_0; // big FAT size in sectors low byte + uint8_t fatsize_1; // + uint8_t fatsize_2; // + uint8_t fatsize_3; // big FAT size in sectors high byte + uint8_t extflags_l; // extended flags low byte + uint8_t extflags_h; // extended flags high byte + uint8_t fsver_l; // filesystem version (0x00) low byte + uint8_t fsver_h; // filesystem version (0x00) high byte + uint8_t root_0; // cluster of root dir, low byte + uint8_t root_1; // + uint8_t root_2; // + uint8_t root_3; // cluster of root dir, high byte + uint8_t fsinfo_l; // sector pointer to FSINFO within reserved area, low byte (2) + uint8_t fsinfo_h; // sector pointer to FSINFO within reserved area, high byte (0) + uint8_t bkboot_l; // sector pointer to backup boot sector within reserved area, low byte (6) + uint8_t bkboot_h; // sector pointer to backup boot sector within reserved area, high byte (0) + uint8_t reserved[12]; // reserved, should be 0 + + uint8_t unit; // int 13h drive# + uint8_t head; // archaic, used by Windows NT-class OSes for flags + uint8_t signature; // 0x28 or 0x29 + uint8_t serial_0; // serial# + uint8_t serial_1; // serial# + uint8_t serial_2; // serial# + uint8_t serial_3; // serial# + uint8_t label[11]; // volume label + uint8_t system[8]; // filesystem ID +} EBPB32, *PEBPB32; + +/* + Logical Boot Record structure (volume boot sector) +*/ +typedef struct _tagLBR { + uint8_t jump[3]; // JMP instruction + uint8_t oemid[8]; // OEM ID, space-padded + BPB bpb; // BIOS Parameter Block + union { + EBPB ebpb; // FAT12/16 Extended BIOS Parameter Block + EBPB32 ebpb32; // FAT32 Extended BIOS Parameter Block + } ebpb; + uint8_t code[420]; // boot sector code + uint8_t sig_55; // 0x55 signature byte + uint8_t sig_aa; // 0xaa signature byte +} LBR, *PLBR; + +/* + Volume information structure (Internal to DOSFS) +*/ +typedef struct _tagVOLINFO { + uint8_t unit; // unit on which this volume resides + uint8_t filesystem; // formatted filesystem + +// These two fields aren't very useful, so support for them has been commented out to +// save memory. (Note that the "system" tag is not actually used by DOS to determine +// filesystem type - that decision is made entirely on the basis of how many clusters +// the drive contains. DOSFS works the same way). +// See tag: OEMID in dosfs.c +// uint8_t oemid[9]; // OEM ID ASCIIZ +// uint8_t system[9]; // system ID ASCIIZ + uint8_t label[12]; // volume label ASCIIZ + uint32_t startsector; // starting sector of filesystem + uint8_t secperclus; // sectors per cluster + uint16_t reservedsecs; // reserved sectors + uint32_t numsecs; // number of sectors in volume + uint32_t secperfat; // sectors per FAT + uint16_t rootentries; // number of root dir entries + + uint32_t numclusters; // number of clusters on drive + + // The fields below are PHYSICAL SECTOR NUMBERS. + uint32_t fat1; // starting sector# of FAT copy 1 + uint32_t rootdir; // starting sector# of root directory (FAT12/FAT16) or cluster (FAT32) + uint32_t dataarea; // starting sector# of data area (cluster #2) +} VOLINFO, *PVOLINFO; + +/* + Flags in DIRINFO.flags +*/ +#define DFS_DI_BLANKENT 0x01 // Searching for blank entry + +/* + Directory search structure (Internal to DOSFS) +*/ +typedef struct _tagDIRINFO { + uint32_t startcluster; // first cluster in dir + uint32_t currentcluster; // current cluster in dir + uint8_t currentsector; // current sector in cluster + uint8_t currententry; // current dir entry in sector + uint8_t *scratch; // ptr to user-supplied scratch buffer (one sector) + uint8_t flags; // internal DOSFS flags +} DIRINFO, *PDIRINFO; + +/* + File handle structure (Internal to DOSFS) +*/ +typedef struct _tagFILEINFO { + PVOLINFO volinfo; // VOLINFO used to open this file + uint32_t dirsector; // physical sector containing dir entry of this file + uint8_t diroffset; // # of this entry within the dir sector + uint8_t mode; // mode in which this file was opened + uint32_t firstcluster; // first cluster of file + uint32_t filelen; // byte length of file + + uint32_t cluster; // current cluster + uint32_t pointer; // current (BYTE) pointer + uint32_t parentcluster; // first cluster of parent dir +} FILEINFO, *PFILEINFO; + +/* + Set the Date and Time in a DIRENT to the Default Date and Time. +*/ +void DFS_SetDirEntDateTime(PDIRENT pde); + +/* + Set the Cluster in a DIRENT +*/ +void DFS_SetStartCluster(PDIRENT pde, uint32_t cluster); + +/* + Get starting sector# of specified partition on drive #unit + NOTE: This code ASSUMES an MBR on the disk. + scratchsector should point to a SECTOR_SIZE scratch area + Returns 0xffffffff for any error. + If pactive is non-NULL, this function also returns the partition active flag. + If pptype is non-NULL, this function also returns the partition type. + If psize is non-NULL, this function also returns the partition size. +*/ +uint32_t DFS_GetPtnStart(uint8_t unit, uint8_t *scratchsector, uint8_t pnum, uint8_t *pactive, uint8_t *pptype, uint32_t *psize); + +/* + Retrieve volume info from BPB and store it in a VOLINFO structure + You must provide the unit and starting sector of the filesystem, and + a pointer to a sector buffer for scratch + Attempts to read BPB and glean information about the FS from that. + Returns 0 OK, nonzero for any error. +*/ +uint32_t DFS_GetVolInfo(uint8_t unit, uint8_t *scratchsector, uint32_t startsector, PVOLINFO volinfo); + +/* + Open a directory for enumeration by DFS_GetNextDirEnt + You must supply a populated VOLINFO (see DFS_GetVolInfo) + The empty string or a string containing only the directory separator are + considered to be the root directory. + Returns 0 OK, nonzero for any error. +*/ +uint32_t DFS_OpenDir(PVOLINFO volinfo, uint8_t *dirname, PDIRINFO dirinfo); + +/* + Get next entry in opened directory structure. Copies fields into the dirent + structure, updates dirinfo. Note that it is the _caller's_ responsibility to + handle the '.' and '..' entries. + A deleted file will be returned as a NULL entry (first char of filename=0) + by this code. Filenames beginning with 0x05 will be translated to 0xE5 + automatically. Long file name entries will be returned as NULL. + returns DFS_EOF if there are no more entries, DFS_OK if this entry is valid, + or DFS_ERRMISC for a media error +*/ +uint32_t DFS_GetNext(PVOLINFO volinfo, PDIRINFO dirinfo, PDIRENT dirent); + +/* + Open a file for reading or writing. You supply populated VOLINFO, a path to the file, + mode (DFS_READ or DFS_WRITE) and an empty fileinfo structure. You also need to + provide a pointer to a sector-sized scratch buffer. + Returns various DFS_* error states. If the result is DFS_OK, fileinfo can be used + to access the file from this point on. +*/ +uint32_t DFS_OpenFile(PVOLINFO volinfo, uint8_t *path, uint8_t mode, uint8_t *scratch, PFILEINFO fileinfo); + +/* + Read an open file + You must supply a prepopulated FILEINFO as provided by DFS_OpenFile, and a + pointer to a SECTOR_SIZE scratch buffer. + Note that returning DFS_EOF is not an error condition. This function updates the + successcount field with the number of bytes actually read. +*/ +uint32_t DFS_ReadFile(PFILEINFO fileinfo, uint8_t *scratch, uint8_t *buffer, uint32_t *successcount, uint32_t len); + +/* + Write an open file + You must supply a prepopulated FILEINFO as provided by DFS_OpenFile, and a + pointer to a SECTOR_SIZE scratch buffer. + This function updates the successcount field with the number of bytes actually written. +*/ +uint32_t DFS_WriteFile(PFILEINFO fileinfo, uint8_t *scratch, uint8_t *buffer, uint32_t *successcount, uint32_t len); + +/* + Seek file pointer to a given position + This function does not return status - refer to the fileinfo->pointer value + to see where the pointer wound up. + Requires a SECTOR_SIZE scratch buffer +*/ +void DFS_Seek(PFILEINFO fileinfo, uint32_t offset, uint8_t *scratch); + +/* + Delete a file + scratch must point to a sector-sized buffer +*/ +uint32_t DFS_UnlinkFile(PVOLINFO volinfo, uint8_t *path, uint8_t *scratch); + +// If we are building a host-emulation version, include host support +#ifdef HOSTVER +#include "hostemu.h" +#endif + +#endif // _DOSFS_H diff --git a/filetest/enter.c b/filetest/enter.c new file mode 100644 index 0000000..f3b34fa --- /dev/null +++ b/filetest/enter.c @@ -0,0 +1,87 @@ +#include +#include +#include "spinix.h" +#include "varsubs.h" + +static char retvalstr[16]; + +void sprintdec(char *str, int val) +{ + int scale; + int zeroflag = 0; + + if (val < 0) + { + *str++ = '-'; + val = -val; + } + + for (scale = 1000000000; scale > 1; scale /= 10) + { + if (val >= scale) + { + *str++ = '0' + val/scale; + val %= scale; + zeroflag = 1; + } + else if (zeroflag) + *str++ = '0'; + } + + *str++ = '0' + val; + *str = 0; +} + +char *varsubs_FindVar(char *str) +{ + char *ptr = (char *)spinix_environ_vars; + while (*ptr) + { + if (!strcmp(str, ptr + 1)) + { + if (*ptr != varsubs_ALIAS_FLAG) + { + return ptr; + } + } + ptr += strlen(ptr) + 1; + ptr += strlen(ptr) + 1; + } + return 0; +} + +char *varsubs_GetVarVal(char *name) +{ + char *ptr; + if (!strcmp(name, "?")) + { + sprintdec(retvalstr, *(int *)spinix_return_value); + return retvalstr; + } + if ((ptr = varsubs_FindVar(name))) + { + return ptr + strlen(ptr) + 1; + } + return ""; +} + +void enter(void) +{ + char *mode; + char *ptr = varsubs_GetVarVal("PWD"); + if (ptr) chdir(ptr); + ptr = varsubs_GetVarVal("<"); + if (*ptr) + printf("stdin = %s\n", ptr); + ptr = varsubs_GetVarVal(">"); + if (*ptr) + mode = ">"; + else + { + ptr = varsubs_GetVarVal(">>"); + if (*ptr) + mode = ">>"; + } + printf("stdout = %s %s\n", mode, ptr); +} + diff --git a/filetest/file_io.c b/filetest/file_io.c new file mode 100644 index 0000000..03b6c42 --- /dev/null +++ b/filetest/file_io.c @@ -0,0 +1,180 @@ +/* +# ######################################################### +# This file contains the SD driver code for the C3 +# Propeller board. It contains the low-level three file +# system routines, which are DFS_InitFileIO, DFS_ReadSector +# and DFS_WriteSector. +# +# Written by Dave Hein with contributions from other GCC +# development team members. +# Copyright (c) 2011 Parallax, Inc. +# MIT Licensed +# ######################################################### +*/ + +#include +#include +#include +#include +#include +#include +#include +#include +#include "sd_internal.h" + +#define SD_INIT_CMD 0x0d +#define SD_READ_CMD 0x11 +#define SD_WRITE_CMD 0x15 +#define BUS_LOCK_CMD 0x1D + +static volatile uint32_t __attribute__((section(".hub"))) sd_lock = -1; +static volatile uint32_t __attribute__((section(".hub"))) *sd_mbox; + +#if defined(__PROPELLER_XMM__) || defined(__PROPELLER_XMMC__) +#define USE_XMM_MBOX +extern uint16_t _xmm_mbox_p; +#endif +uint32_t *_sd_mbox_p = 0; + +// This routine sends a command to the driver cog and returns the result +// It must be loaded in HUB RAM so we don't generate a cache miss +static uint32_t __attribute__((section(".hubtext"))) do_cmd(uint32_t *params) +{ + //printf("%x %x %x %x\n", sd_mbox, params[0], params[1], params[2]); + sd_mbox[1] = params[1]; + sd_mbox[2] = params[2]; + sd_mbox[0] = params[0]; + while (sd_mbox[0] == params[0]); + return sd_mbox[0]; +} + +// This routine passes a bus lock to the low-level driver +void dfs_use_lock(uint32_t lockId) +{ +#if 0 + if (sd_mbox) + do_cmd(BUS_LOCK_CMD | (lockId << 8)); + else +#endif + sd_lock = lockId; +} + +// This routine initializes the low-level driver +int DFS_InitFileIO(void) +{ +#if 0 + int retries = 5; + +#if !defined(USE_XMM_MBOX) + sd_mbox = _sd_mbox_p; + if (!_sd_mbox_p) + return DFS_INVAL; +#else + if (_sd_mbox_p) + sd_mbox = _sd_mbox_p; + else + { + sd_mbox = (uint32_t *)(uint32_t)_xmm_mbox_p; + retries = -1; // We're using the SD Cache driver - it's already initialized! + } +#endif + + // Handle the case where we were given the lock before dfs_mount() was called. + if (sd_lock != -1) + dfs_use_lock(sd_lock); + + while (retries-- > 0) + { + if (do_cmd(SD_INIT_CMD) == 0) + return DFS_OK; + } + + return DFS_IOERR; +#else + sd_mbox = (uint32_t *)0x7fd4; + return DFS_OK; +#endif +} + +// This routine reads 512-byte sectors into a buffer. If the buffer is +// not located in hub memory it reads the sectors into the scratch buffer, +// and then copies it to to the buffer. +uint32_t DFS_ReadSector(uint8_t unit, uint8_t *buffer, uint32_t sector, uint32_t count) +{ + uint32_t params[3], result; + while (count > 0) { + if (((uint32_t)buffer) & 0xffff0000) + params[2] = (uint32_t)dfs_scratch; + else + params[2] = (uint32_t)buffer; + params[0] = 'r'; + params[1] = sector; + result = do_cmd(params); + if (result != 0) { + return DFS_IOERR; + } + if (((uint32_t)buffer) & 0xffff0000) + memcpy(buffer, dfs_scratch, SECTOR_SIZE); + buffer += SECTOR_SIZE; + ++sector; + --count; + } + + return DFS_OK; +} + +// This routine writes 512-byte sectors from a buffer. If the buffer is +// not located in hub memory the data is first copied to the scratch buffer, +// and then written from the scratch buffer. +uint32_t DFS_WriteSector(uint8_t unit, uint8_t *buffer, uint32_t sector, uint32_t count) +{ + uint32_t params[3], result; + while (count > 0) { + if (((uint32_t)buffer) & 0xffff0000) + { + memcpy(dfs_scratch, buffer, SECTOR_SIZE); + params[2] = (uint32_t)dfs_scratch; + } + else + params[2] = (uint32_t)buffer; + params[0] = 'w'; + params[1] = sector; + result = do_cmd(params); + if (result != 0) { + return DFS_IOERR; + } + buffer += SECTOR_SIZE; + ++sector; + --count; + } + return DFS_OK; +} + +void LoadSDDriver(uint32_t configwords[2]) +{ +} + +/* ++-------------------------------------------------------------------- +| TERMS OF USE: MIT License ++-------------------------------------------------------------------- +Permission is hereby granted, free of charge, to any person obtaining +a copy of this software and associated documentation files +(the "Software"), to deal in the Software without restriction, +including without limitation the rights to use, copy, modify, merge, +publish, distribute, sublicense, and/or sell copies of the Software, +and to permit persons to whom the Software is furnished to do so, +subject to the following conditions: + +The above copyright notice and this permission notice shall be +included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. ++------------------------------------------------------------------ +*/ diff --git a/filetest/filetest.c b/filetest/filetest.c new file mode 100644 index 0000000..65de5b4 --- /dev/null +++ b/filetest/filetest.c @@ -0,0 +1,505 @@ +/* +############################################################################ +# This program is used to test the basic functions of the SD file system. +# It implements simple versions of the cat, rm, ls, echo, cd, pwd, mkdir and +# rmdir commands plus the <, > and >> file redirection operators. +# The program starts up the file driver and then prompts for a command. +# +# Written by Dave Hein +# Copyright (c) 2011 Parallax, Inc. +# MIT Licensed +############################################################################ +*/ + +#include +#include +#include +#include +#include +#include +#include +#include +#include "spinix.h" + +extern _Driver _SimpleSerialDriver; +extern _Driver _FileDriver; + +/* This is a list of all drivers we can use in the + * program. The default _InitIO function opens stdin, + * stdout, and stderr based on the first driver in + * the list (the serial driver, for us) + */ +_Driver *_driverlist[] = { + &_SimpleSerialDriver, + &_FileDriver, + NULL +}; + +char *FindChar(char *ptr, int val); + +FILE *stdinfile; +FILE *stdoutfile; + +/* Print help information */ +void Help() +{ + printf("Commands are help, cat, rm, ls, ll, echo, cd, pwd and exit\n"); +} + +void Cd(int argc, char **argv) +{ + if (argc < 2) return; + + if (chdir(argv[1])) + perror(argv[1]); +} + +void Pwd(int argc, char **argv) +{ + uint8_t buffer[64]; + char *ptr = getcwd(buffer, 64); + if (!ptr) + perror(0); + else + fprintf(stdoutfile, "%s\n", ptr); +} + +#if 0 +void Mkdir(int argc, char **argv) +{ + int i; + + for (i = 1; i < argc; i++) + { + if (mkdir(argv[i], 0)) + perror(argv[i]); + } +} + +void Rmdir(int argc, char **argv) +{ + int i; + + for (i = 1; i < argc; i++) + { + if (rmdir(argv[i])) + perror(argv[i]); + } +} +#endif + +/* This routine implements the file cat function */ +void Cat(int argc, char **argv) +{ + int i; + int num; + void *infile; + uint8_t buffer[40]; + + for (i = 0; i < argc; i++) + { + if (i == 0) + { + if (argc == 1 || stdinfile != stdin) + infile = stdinfile; + else + continue; + } + else + { + infile = fopen(argv[i], "r"); + if (infile == 0) + { + perror(argv[i]); + continue; + } + } + if (infile == stdin) + { + while (gets(buffer)) + { + if (buffer[0] == 4) break; + fprintf(stdoutfile, "%s\n", buffer); + } + } + else + { + while ((num = fread(buffer, 1, 40, infile))) + fwrite(buffer, 1, num, stdoutfile); + } + if (i) + fclose(infile); + } + fflush(stdout); +} + +/* This routine deletes the files specified by the command line arguments */ +void Remove(int argc, char **argv) +{ + int i; + + for (i = 1; i < argc; i++) + { + if (remove(argv[i])) + perror(argv[i]); + } +} + +/* This routine echos the command line arguments */ +void Echo(int argc, char **argv) +{ + int i; + for (i = 1; i < argc; i++) + { + if (i != argc - 1) + fprintf(stdoutfile, "%s ", argv[i]); + else + fprintf(stdoutfile, "%s\n", argv[i]); + } +} + +/* This routine lists the root directory or any subdirectories specified + in the command line arguments. If the "-l" option is specified, it + will print the file attributes and size. Otherwise, it will just + print the file names. */ +void List(int argc, char **argv) +{ + int i, j; + char *ptr; + char fname[13]; + int32_t count = 0; + uint32_t filesize; + uint32_t longflag = 0; + char *path; + char drwx[5]; + int column; + int prevlen; + DIR *dirp; + struct dirent *entry; + + // Check flags + for (j = 1; j < argc; j++) + { + if (argv[j][0] == '-') + { + if (!strcmp(argv[j], "-l")) + longflag = 1; + else + printf("Unknown option \"%s\"\n", argv[j]); + } + else + count++; + } + + // List directories + for (j = 1; j < argc || count == 0; j++) + { + if (count == 0) + { + count--; + path = "./"; + } + else if (argv[j][0] == '-') + continue; + else + path = argv[j]; + + if (count >= 2) + fprintf(stdoutfile, "\n%s:\n", path); + + dirp = opendir(path); + + if (!dirp) + { + perror(path); + continue; + } + + column = 0; + prevlen = 14; + while (entry = readdir(dirp)) + { + if (entry->name[0] == '.') continue; + ptr = fname; + for (i = 0; i < 8; i++) + { + if (entry->name[i] == ' ') break; + *ptr++ = tolower(entry->name[i]); + } + if (entry->name[8] != ' ') + { + *ptr++ = '.'; + for (i = 8; i < 11; i++) + { + if (entry->name[i] == ' ') break; + *ptr++ = tolower(entry->name[i]); + } + } + *ptr = 0; + filesize = entry->filesize_3; + filesize = (filesize << 8) | entry->filesize_2; + filesize = (filesize << 8) | entry->filesize_1; + filesize = (filesize << 8) | entry->filesize_0; + strcpy(drwx, "-rw-"); + if (entry->attr & ATTR_READ_ONLY) + drwx[2] = '-'; + if (entry->attr & ATTR_ARCHIVE) + drwx[3] = 'x'; + if (entry->attr & ATTR_DIRECTORY) + { + drwx[0] = 'd'; + drwx[3] = 'x'; + } + if (longflag) + fprintf(stdoutfile, "%s %8d %s\n", drwx, filesize, fname); + else if (++column == 5) + { + for (i = prevlen; i < 14; i++) fprintf(stdoutfile, " "); + fprintf(stdoutfile, "%s\n", fname); + column = 0; + prevlen = 14; + } + else + { + for (i = prevlen; i < 14; i++) fprintf(stdoutfile, " "); + prevlen = strlen(fname); + fprintf(stdoutfile, "%s", fname); + } + } + closedir(dirp); + if (!longflag && column) + fprintf(stdoutfile, "\n"); + } +} + +/* This routine returns a pointer to the first character that doesn't + match val. */ +char *SkipChar(char *ptr, int val) +{ + while (*ptr) + { + if (*ptr != val) break; + ptr++; + } + return ptr; +} + +/* This routine returns a pointer to the first character that matches val. */ +char *FindChar(char *ptr, int val) +{ + while (*ptr) + { + if (*ptr == val) break; + ptr++; + } + return ptr; +} + +/* This routine extracts tokens from a string that are separated by one or + more spaces. It returns the number of tokens found. */ +int tokenize(char *ptr, char *tokens[]) +{ + int num = 0; + + while (*ptr) + { + ptr = SkipChar(ptr, ' '); + if (*ptr == 0) break; + if (ptr[0] == '>') + { + ptr++; + if (ptr[0] == '>') + { + tokens[num++] = ">>"; + ptr++; + } + else + tokens[num++] = ">"; + continue; + } + if (ptr[0] == '<') + { + ptr++; + tokens[num++] = "<"; + continue; + } + tokens[num++] = ptr; + ptr = FindChar(ptr, ' '); + if (*ptr) *ptr++ = 0; + } + return num; +} + +/* This routine searches the list of tokens for the redirection operators + and opens the files for input, output or append depending on the + operator. */ +int CheckRedirection(char **tokens, int num) +{ + int i, j; + + for (i = 0; i < num-1; i++) + { + if (!strcmp(tokens[i], ">")) + { + stdoutfile = fopen(tokens[i+1], "w"); + if (!stdoutfile) + { + perror(tokens[i+1]); + stdoutfile = stdout; + return 0; + } + } + else if (!strcmp(tokens[i], ">>")) + { + stdoutfile = fopen(tokens[i+1], "a"); + if (!stdoutfile) + { + perror(tokens[i+1]); + stdoutfile = stdout; + return 0; + } + } + else if (!strcmp(tokens[i], "<")) + { + stdinfile = fopen(tokens[i+1], "r"); + if (!stdinfile) + { + perror(tokens[i+1]); + stdinfile = stdin; + return 0; + } + } + else + continue; + for (j = i + 2; j < num; j++) tokens[j-2] = tokens[j]; + i--; + num -= 2; + } + return num; +} + +/* This routine closes files that were open for redirection */ +void CloseRedirection() +{ + if (stdinfile != stdin) + { + fclose(stdinfile); + stdinfile = stdin; + } + if (stdoutfile != stdout) + { + fclose(stdoutfile); + stdoutfile = stdout; + } +} + +int getdec(char *ptr) +{ + int val = 0; + while (*ptr) val = (val * 10) + *ptr++ - '0'; + return val; +} + +static char pins[4]; +static char *CWD; + +#if 0 +void GetConfig(int argc, char **argv) +{ + unsigned int *lptr; + char *ptr = argv[argc-1]; + ptr += strlen(ptr) + 1; + ptr = (char *)(((((int)ptr) + 3) >> 2) << 2); + lptr = (unsigned int *)ptr; + //printf("%8.8x, %8.8x, %s\n", lptr[0], lptr[1], ptr + 8); + memcpy(pins, &lptr[1], 4); + CWD = ptr + 8; +} +#endif + +/* The program starts the file system. It then loops reading commands + and calling the appropriate routine to process it. */ +int main(int argc, char **argv) +{ + int num; + char *tokens[20]; + uint8_t buffer[80]; + + stdinfile = stdin; + stdoutfile = stdout; + + spinix_enter(); + + printf("\n"); + Help(); + + while (1) + { + printf("\n> "); + fflush(stdout); + gets(buffer); + num = tokenize(buffer, tokens); + num = CheckRedirection(tokens, num); + if (num == 0) continue; + if (!strcmp(tokens[0], "help")) + Help(); + else if (!strcmp(tokens[0], "cat")) + Cat(num, tokens); + else if (!strcmp(tokens[0], "ls")) + List(num, tokens); + else if (!strcmp(tokens[0], "ll")) + { + tokens[num++] = "-l"; + List(num, tokens); + } + else if (!strcmp(tokens[0], "rm")) + Remove(num, tokens); + else if (!strcmp(tokens[0], "echo")) + Echo(num, tokens); + else if (!strcmp(tokens[0], "cd")) + Cd(num, tokens); + else if (!strcmp(tokens[0], "pwd")) + Pwd(num, tokens); +#if 0 + else if (!strcmp(tokens[0], "mkdir")) + Mkdir(num, tokens); + else if (!strcmp(tokens[0], "rmdir")) + Rmdir(num, tokens); +#endif + else if (!strcmp(tokens[0], "exit")) + spinix_exit(0); + else + { + printf("Invalid command\n"); + Help(); + } + CloseRedirection(); + } + + spinix_exit(0); +} +/* ++-------------------------------------------------------------------- +| TERMS OF USE: MIT License ++-------------------------------------------------------------------- +Permission is hereby granted, free of charge, to any person obtaining +a copy of this software and associated documentation files +(the "Software"), to deal in the Software without restriction, +including without limitation the rights to use, copy, modify, merge, +publish, distribute, sublicense, and/or sell copies of the Software, +and to permit persons to whom the Software is furnished to do so, +subject to the following conditions: + +The above copyright notice and this permission notice shall be +included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. ++------------------------------------------------------------------ +*/ + diff --git a/filetest/mount.c b/filetest/mount.c new file mode 100644 index 0000000..9dea1c5 --- /dev/null +++ b/filetest/mount.c @@ -0,0 +1,37 @@ +#include +#include +#include +#include + +void mount(int MISO, int CLK, int MOSI, int CS) +{ +#if 0 + uint32_t mountErr; + _SD_Params params; + + if (MISO == 10 && CLK == 11 && MOSI == 9 && CS == 25) + { + params.AttachmentType = _SDA_SerialDeMUX; + params.pins.SerialDeMUX.MISO = MISO; + params.pins.SerialDeMUX.CLK = CLK; + params.pins.SerialDeMUX.MOSI = MOSI; + params.pins.SerialDeMUX.CLR = CS; + params.pins.SerialDeMUX.INC = 8; + params.pins.SerialDeMUX.ADDR = 5; + } + else + { + params.AttachmentType = _SDA_SingleSPI; + params.pins.SingleSPI.MISO = MISO; + params.pins.SingleSPI.CLK = CLK; + params.pins.SingleSPI.MOSI = MOSI; + params.pins.SingleSPI.CS = CS; + } + + if ((mountErr = dfs_mount(¶ms))) + { + printf("Mount error: %d\n", mountErr); + exit(1); + } +#endif +} diff --git a/filetest/printf.c b/filetest/printf.c new file mode 100644 index 0000000..fb1523b --- /dev/null +++ b/filetest/printf.c @@ -0,0 +1,97 @@ +#include +#include + +void printdec(FILE *fp, int val) +{ + int scale; + int zeroflag = 0; + + if (val < 0) + { + fputc('-', fp); + val = -val; + } + + for (scale = 1000000000; scale > 1; scale /= 10) + { + if (val >= scale) + { + fputc('0' + val/scale, fp); + val %= scale; + zeroflag = 1; + } + else if (zeroflag) + fputc('0', fp); + else + fputc(' ', fp); + } + + fputc('0'+val, fp); +} + +void printhex(FILE *fp, int val) +{ + int i; + static char hexdigits[] = "0123456789abcdef"; + + for (i = 0; i < 8; i++) + { + fputc(hexdigits[(val >> 28)&15], fp); + val <<= 4; + } +} + +void _doprint(FILE *fp, const char *fmt, va_list args ) +{ + int val; + int state = 0; + + while (*fmt) + { + val = *fmt++; + if (state) + { + if (val == 's') + { + state = 0; + val = va_arg(args, int); + fputs((char *)val, fp); + } + else if (val == 'd') + { + state = 0; + val = va_arg(args, int); + printdec(fp, val); + } + else if (val == 'x') + { + state = 0; + val = va_arg(args, int); + printhex(fp, val); + } + } + else + { + if (val == '%') state = 1; + else fputc(val, fp); + } + } +} + +int printf(const char *fmt, ...) +{ + va_list args; + va_start(args, fmt); + _doprint(stdout, fmt, args); + va_end(args); + return 0; +} + +int fprintf(FILE *fp, const char *fmt, ...) +{ + va_list args; + va_start(args, fmt); + _doprint(fp, fmt, args); + va_end(args); + return 0; +} diff --git a/filetest/sd_internal.h b/filetest/sd_internal.h new file mode 100644 index 0000000..4e8ceef --- /dev/null +++ b/filetest/sd_internal.h @@ -0,0 +1,11 @@ +#ifndef _SD_INTERNAL_H +#define _SD_INTERNAL_H + +#include "dosfs.h" + +extern int dfs_mountflag; +extern VOLINFO dfs_volinfo; +extern char dfs_currdir[MAX_PATH]; +extern __attribute__((section(".hub"))) uint8_t dfs_scratch[512]; + +#endif diff --git a/filetest/spinix.c b/filetest/spinix.c new file mode 100644 index 0000000..f1c06f3 --- /dev/null +++ b/filetest/spinix.c @@ -0,0 +1,45 @@ +#include +#include "spinix.h" + +static int run_prog[] = { + 0xa0bc65f0, 0x08bc6e32, 0x80fc6404, 0x08bc7032, 0x80fc6404, 0x08bc6032, + 0x80fc6404, 0x08bc6232, 0x80fc63ff, 0x28fc6209, 0x08fc6600, 0x00fc6804, + 0x083c6029, 0x083c5c2a, 0x083c582b, 0x08bc642b, 0x863c642c, 0x5c68000f, + 0x80fc6001, 0x80fc5d00, 0x80fc5d00, 0xe4fc620c, 0x087c6600, 0x007c6804, + 0x04fc6a08, 0x04fc6c0a, 0xa0bc6236, 0x84bc6235, 0x28fc6202, 0x083c5a35, + 0x80fc6a04, 0xe4fc621d, 0x083c5a36, 0x80fc6c04, 0x083c6e36, 0x80fc6c04, + 0x083c7036, 0x0cfc6401, 0x60fc6407, 0x68bc5e32, 0x0c7c5e02, 0x00007fd8, + 0x00007fdc, 0x00007fd4, 0x00000072, 0x00000000, 0x00000000, 0x0007c010}; + +static int arg_list[] = {0, 0, 0, 0}; + +void spinix_enter(int argc, char **argv) +{ +} + +void spinix_exit(int retval) +{ + int i; + int cogspi = (*(int *)spinix_spi_engine_cog) - 1; + arg_list[2] = *(int *)spinix_shell_sector; + arg_list[3] = *(int *)spinix_shell_size; + + *(int *)spinix_return_value = retval; + + // Stop all the cogs except this one and the SD SPI cog + for (i = 0; i < 8; i++) + { + if (i != cogid() && i != cogspi) + cogstop(i); + } + + // Clear and return all the locks + for (i = 0; i < 8; i++) + { + lockclr(i); + lockret(i); + } + + // Start run_prog in this cog + coginit(cogid(), run_prog, arg_list); +} diff --git a/filetest/spinix.h b/filetest/spinix.h new file mode 100644 index 0000000..ba7d835 --- /dev/null +++ b/filetest/spinix.h @@ -0,0 +1,45 @@ +#define spinix_start 0x7c00 // Extra space for the stand-alone loader +#define spinix_rendezvous 0x7e50 +#define spinix_environ_vars 0x7e50 +#define spinix_environ_vars_end 0x7ed3 +#define spinix_argv_parms 0x7ed4 +#define spinix_return_value 0x7f94 +#define spinix_vga_cog 0x7f98 +#define spinix_vga_handle 0x7f9c +#define spinix_sd_pins 0x7fa0 +#define spinix_config 0x7fa4 +#define spinix_shell_sector 0x7fa8 +#define spinix_unixtime 0x7fac +#define spinix_cycle0 0x7fb0 +#define spinix_timezone 0x7fb4 +#define spinix_scriptline 0x7fb8 +#define spinix_ifflag 0x7fbc +#define spinix_whileflag 0x7fc0 +#define spinix_shell_size 0x7fc4 +#define spinix_shell_level 0x7fc8 +#define spinix_bootflag 0x7fcc +#define spinix_spi_engine_cog 0x7fd0 +#define spinix_spi_command 0x7fd4 +#define spinix_spi_block_index 0x7fd8 +#define spinix_spi_buffer_address 0x7fdc +#define spinix_serial 0x7fe0 +#define spinix_stdio 0x7fe4 +#define spinix_stdin 0x7fe8 +#define spinix_stdout 0x7fec +#define spinix_memlocknum 0x7ff0 +#define spinix_memfreelist 0x7ff4 +#define spinix_malloclist 0x7ff8 +#define spinix_laststackaddr 0x7ffc +#define spinix_checkword 0xdead1eaf +#define spinix_proc_type_spin 1 +#define spinix_proc_type_pasm 2 +#define spinix_proc_type_capp 3 +#define spinix_proc_type_driver 0x80 +#define spinix_run_shell_wait 0x00 +#define spinix_run_shell_nowait 0x08 +#define spinix_run_kill_caller 0x10 +#define spinix_run_at_address0 0x20 +#define spinix_run_c_program 0x40 +#define spinix_run_spin_program 0x80 +#define spinix_run_stand_alone 0x100 + diff --git a/filetest/varsubs.c b/filetest/varsubs.c new file mode 100644 index 0000000..263c81f --- /dev/null +++ b/filetest/varsubs.c @@ -0,0 +1,193 @@ +#include +#include +#include "spinix.h" +#include "varsubs.h" + +static char retvalstr[16]; + +//****************************************************************************** +// Copyright (c) 2013 Dave Hein +// See end of file for terms of use. +//****************************************************************************** +char *varsubs_FindVar(char *str) +{ + char *ptr = (char *)spinix_environ_vars; + while (*ptr) + { + if (!strcmp(str, ptr + 1)) + { + if (*ptr != varsubs_ALIAS_FLAG) + { + return ptr; + } + } + ptr += strlen(ptr) + 1; + ptr += strlen(ptr) + 1; + } + return 0; +} + +char *varsubs_FindAlias(char *str) +{ + char *ptr = (char *)spinix_environ_vars; + while (*ptr) + { + if (!strcmp(str, ptr + 1)) + { + if (*ptr == varsubs_ALIAS_FLAG) + { + return ptr; + } + } + ptr += strlen(ptr) + 1; + ptr += strlen(ptr) + 1; + } + return 0; +} + +char *varsubs_FindEnd(void) +{ + char *ptr = (char *)spinix_environ_vars; + while (*ptr) + { + ptr += strlen(ptr) + 1; + ptr += strlen(ptr) + 1; + } + return ptr; +} + +char *varsubs_RemoveEntry(char *ptr1) +{ + int i, len; + char *ptr2 = ptr1 + strlen(ptr1) + 1; + ptr2 += strlen(ptr2) + 1; + while (*ptr2) + { + for (i = 0; i < 2; i++) + { + len = strlen(ptr2) + 1; + memmove (ptr1, ptr2, len); + ptr1 += len; + ptr2 += len; + } + } + *ptr1 = 0; + return ptr1; +} + +void varsubs_RemoveVar(char *name) +{ + char *ptr; + if ((ptr = varsubs_FindVar(name))) + { + ptr = varsubs_RemoveEntry(ptr); + } +} + +void varsubs_SaveVar(char *name, char *value, int type) +{ + char *ptr; + int space, len1, len2; + // Check if variable already exists + if ((ptr = varsubs_FindVar(name))) + { + type = *ptr; + ptr = varsubs_RemoveEntry(ptr); + } + else + { + ptr = varsubs_FindEnd(); + } + // Check if space is available + space = spinix_environ_vars_end - (int)ptr + 1; + len1 = strlen(name) + 1; + len2 = strlen(value) + 1; + if ((space < len1 + len2 + 2)) + { + printf("Not enough variable space\n"); + return; + } + else if ((space < len1 + len2 + 82)) + { + printf("space = %d, need = %d\n", space, len1 + len2 + 2); + } + // Add variable + *ptr++ = type; + memmove (ptr, name, len1); + ptr += len1; + memmove (ptr, value, len2); + ptr += len2; + *ptr = 0; +} + +char *varsubs_GetVarVal(char *name) +{ + char *ptr; + if (!strcmp(name, "?")) + { + sprintf(retvalstr, "%d", *(int *)spinix_return_value); + return retvalstr; + } + if ((ptr = varsubs_FindVar(name))) + { + return ptr + strlen(ptr) + 1; + } + return ""; +} + +char *varsubs_Val(char *ptr) +{ + if (*ptr != '$') return ptr; + return varsubs_GetVarVal(ptr + 1); +} + +int varsubs_NumVal(char *ptr) +{ + if (*ptr == '$') + { + if (!strcmp(ptr, "$?")) + { + return *(int *)spinix_return_value; + } + ptr = varsubs_GetVarVal(ptr + 1); + } + return atol(ptr); +} + +#if 0 +char *varsubs_FindChar(char *str, int val) +{ + while (*str && *str != val) str++; + return str; +} + +char *varsubs_SkipChar(char *str, int val) +{ + while (*str && *str == val) str++; + return str; +} +#endif + +/* ++-----------------------------------------------------------------------------+ +| TERMS OF USE: MIT License | ++-----------------------------------------------------------------------------+ +|Permission is hereby granted, free of charge, to any person obtaining a copy | +|of this software and associated documentation files (the "Software"), to deal| +|in the Software without restriction, including without limitation the rights | +|to use, copy, modify, merge, publish, distribute, sublicense, and/or sell | +|copies of the Software, and to permit persons to whom the Software is | +|furnished to do so, subject to the following conditions: | +| | +|The above copyright notice and this permission notice shall be included in | +|all copies or substantial portions of the Software. | +| | +|THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | +|IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | +|FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE | +|AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | +|LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,| +|OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE| +|SOFTWARE. | ++-----------------------------------------------------------------------------+ +*/ diff --git a/filetest/varsubs.h b/filetest/varsubs.h new file mode 100644 index 0000000..e322413 --- /dev/null +++ b/filetest/varsubs.h @@ -0,0 +1,17 @@ +// CONSTANTS +#define varsubs_GLOBAL_FLAG 'G' +#define varsubs_LOCAL_FLAG 'L' +#define varsubs_PARM_FLAG 'P' +#define varsubs_ALIAS_FLAG 'A' + +char *varsubs_FindVar(char *str); +char *varsubs_FindAlias(char *str); +char *varsubs_FindEnd(void); +char *varsubs_RemoveEntry(char *ptr1); +void varsubs_RemoveVar(char *name); +void varsubs_SaveVar(char *name, char *value, int type); +char *varsubs_GetVarVal(char *name); +char *varsubs_Val(char *ptr); +int varsubs_NumVal(char *ptr); +char *varsubs_FindChar(char *str, int val); +char *varsubs_SkipChar(char *str, int val); diff --git a/lerner/cfloatstr.spin b/lerner/cfloatstr.spin new file mode 100644 index 0000000..c8ad139 --- /dev/null +++ b/lerner/cfloatstr.spin @@ -0,0 +1,315 @@ +'****************************************************************************** +' Floating Point I/O Routines +' Author: Dave Hein +' Copyright (c) 2010 +' See end of file for terms of use. +'****************************************************************************** +'****************************************************************************** +' Revison History +' v1.0 - 4/2/2010 First official release +'****************************************************************************** +{ + These routines are used by the C library to perform formatted floating point + I/O. The two output routines, putfloate and putfloatf write to string. + + Input formatting is performed by the strtofloat routine. It uses a pointer + to a string pointer and returns the resulting floating point value. + + If floating point I/O is not required for an application this file can be + removed by deleting the references to the object and the three routines in + clib.spin. +} + +'****************************************************************************** +' Floating Point Routines +'****************************************************************************** +PUB putfloate(str, x, width, digits) | man, exp10, signbit +{{ + Convert the floating point value in x to a string of characters in scientific + notation in str. digits determines the number of fractional digits used and + width determines the minimum length of the output string. Leading blanks are + added to achieve the minimum width. + }} + if (digits < 0) + digits := 6 + signbit := tofloat10(x, @man, @exp10) + exp10 += round10(digits + 1, @man) + digits + width -= 5 + signbit - (digits <> 0) + repeat while (width-- > digits) + byte[str++] := " " + if (signbit) + byte[str++] := "-" + str := utoa10(man, str, 1) + byte[str++] := "e" + if (exp10 => 0) + byte[str++] := "+" + else + byte[str++] := "-" + exp10 := 0-exp10 + if (exp10 < 10) + byte[str++] := "0" + str := utoa10(exp10, str, -1) + byte[str] := 0 + return str + +PUB putfloatf(str, x, width, digits) | lead0, trail0, man, exp10, signbit, digits0 +{{ + Convert the floating point value in x to a string of character in standard + notation in str. digits determines the number of fractional digits used and + width determines the minimum length of the output string. Leading blanks are + added to achieve the minimum width. +}} + if (digits < 0) + digits := 6 + signbit := tofloat10(x, @man, @exp10) + digits0 := numdigits(man, @lead0) + exp10 + if (digits0 > 0) + width -= digits0 + digits0 += digits + if (digits0 > 8) + digits0 := 8 + exp10 += round10(digits0, @man) + digits0 - 1 + if (digits0 < 0) + digits0 := 0 + elseif (digits0 == 0 and man == 1 and digits > 0) + digits0 := 1 + lead0 := digits - digits0 + trail0 := digits - digits0 + exp10 + 1 + width -= signbit + digits - (lead0 => 0) + 1 + repeat while (width-- > 0) + byte[str++] := " " + if (signbit) + byte[str++] := "-" + if (lead0 => 0) + byte[str++] := "0" + if (lead0 > 0) + byte[str++] := "." + repeat while (lead0-- > 0) + byte[str++] := "0" + if (digits0 > 0) + str := utoa10(man, str, exp10 + 1) + exp10 -= digits0 - 1 + repeat while (trail0-- > 0) + if (exp10-- == 0) + byte[str++] := "." + byte[str++] := "0" + byte[str] := 0 + return str + +PUB strtofloat(pstr) | value, exp10, exp10a, signbit, mode, char, esignbit, str +{{ + Convert the string of characters pointer to by "pstr" into a floating point + value. The input can be in either standard or scientific notation. Leading + blanks are ignored. The string pointed to by "pstr" is updated to the last + character postioned that caused processing to be completed. +}} + str := long[pstr] + esignbit := 0 + mode := 0 + value := 0 + exp10 := 0 + exp10a := 0 + signbit := 0 + repeat + char := byte[str++] + if (char == 0) + quit + case mode + 0: + case char + "0".."9": value := char - "0" + "-" : signbit := 1 + " " : next + "+": mode := 1 + other:quit + mode := 1 + 1 : + case char + "0".."9": + if (value =< 200_000_000) + value := (value * 10) + char - "0" + else + exp10++ + ".": mode := 2 + "e", "E": mode := 3 + other: quit + 2: + case char + "0".."9": + if (value =< 200_000_000) + value := (value * 10) + char - "0" + exp10-- + "e", "E": mode := 3 + other: quit + 3: + case char + "0".."9": exp10a := char - "0" + "-" : esignbit := 1 + "+": mode := 4 + other:quit + mode := 4 + 4: + case char + "0".."9":exp10a := (exp10a * 10) + char - "0" + other: quit + if (esignbit) + exp10 -= exp10a + else + exp10 += exp10a + long[pstr] := str + return fromfloat10(value, exp10, signbit) + +DAT +{{ + +________________________ + These tables of scalers are used to scale a floating point number by a ratio of a + power of 10 versus a power of 2. +}} +''SCALE1 10/16 100/128 1000/1024 10^6/2^20 10^12/2^40 10^24/2^80 + scale1 long 1342177280, 1677721600, 2097152000, 2048000000, 1953125000, 1776356839 +''SCALE2 8/10 64/100 512/1000 2^19/10^6 2^39/10^12 2^79/10^24 + scale2 long 1717986918, 1374389535, 1099511628, 1125899907, 1180591621, 1298074215 + nbits1 byte 4, 7, 10, 20, 40, 80 + nbits2 byte 3, 6, 9, 19, 39, 79 + ndecs byte 1, 2, 3, 6, 12, 24 + +PRI floatloop(man, pexp0, pexp1, step0, step1, scale, pexp2, step2) | i +{{ + This private routine reduces the value of exp0 toward 0 while increasing the value + of exp1. This is done in a successive approximation method using the scaling + table passed in "scale". This routine is used here to convert between a mantissa + times a power of 2 or 10 to a mantissa times a power of 10 or 2. +}} + repeat i from 5 to 0 + if (long[pexp0] => byte[step0][i]) + man := (man ** long[scale][i]) << 1 + long[pexp0] -= byte[step0][i] + long[pexp1] += byte[step1][i] + if ((man & $40000000) == 0) + man <<= 1 + long[pexp2] -= step2 + return man + +PRI tofloat10(value, pman, pexp10) | exp2, exp10, man +{{ + This private routine converts from a mantissa times a power of 2 to a mantissa + times a power of 10. +}} + result := value >> 31 + exp2 := ((value >> 23) & 255) - 157 + man := ((value & $007fffff) | $00800000) << 7 + exp10 := 0 + if (exp2 =< 0) + exp2 := -exp2 + man := floatloop(man, @exp2, @exp10, @nbits1, @ndecs, @scale1, @exp2, -1) + man >>= exp2 + exp10 := -exp10 + else + exp2 += 2 + man := floatloop(man, @exp2, @exp10, @nbits2, @ndecs, @scale2, @exp2, 1) + man >>= 2 - exp2 + long[pman] := man + long[pexp10] := exp10 + +PRI fromfloat10(man, exp10, signbit) | exp2 +{{ + This private routine converts from a mantissa times a power of 10 to a mantissa + times a power of two. +}} + if (man == 0) + return 0 + exp2 := 0 + repeat while(man & $40000000) == 0 + man <<= 1 + exp2-- + if (exp10 =< 0) + exp10 := -exp10 + exp2 := -exp2 + man := floatloop(man, @exp10, @exp2, @ndecs, @nbits2, @scale2, @exp2, -1) + exp2 := -exp2 + else + man := floatloop(man, @exp10, @exp2, @ndecs, @nbits1, @scale1, @exp2, 1) + repeat while(man & $ff000000) + man >>= 1 + exp2++ + return (signbit << 31) | ((exp2 + 150) << 23) | (man & $007fffff) + +PRI numdigits(man, pdiv) : numdig | divisor +{{ + This routine determines the number of decimal digits in the number in man. +}} + numdig := 10 + divisor := 1000000000 + repeat while (divisor > man) + numdig-- + divisor /= 10 + long[pdiv] := divisor + +PRI round10(digits, pman) : exp10 | numdig, divisor, rounder, man +{{ + This routine round the number poiinted to by pman to the number of decimal + digits specified by "digits". +}} + man := long[pman] + exp10 := numdigits(man, @divisor) - digits + if (digits < 0) + man := 0 + elseif (digits == 0) + if (man / divisor => 5) + man := 1 + exp10++ + elseif (exp10 > 0) + rounder := 1 + repeat exp10 + rounder *= 10 + man :=(man + (rounder >> 1)) / rounder + divisor /= rounder + if (man / divisor > 9) + man /= 10 + exp10++ + elseif (exp10 < 0) + repeat 0-exp10 + man *= 10 + long[pman] := man + +PRI utoa10(number, str, point) | divisor, temp +{{ + This routine converts the value in "number" to a string of decimal characters + in "str". A decimal point is added after the character position specified by + the value in "point". +}} + if (number == 0) + byte[str++] := "0" + byte[str] := 0 + return str + divisor := 1_000_000_000 + repeat while (divisor > number) + divisor /= 10 + repeat while (divisor > 0) + if (point-- == 0) + byte[str++] := "." + temp := number / divisor + byte[str++] := temp + "0" + number -= temp * divisor + divisor /= 10 + byte[str] := 0 + return str + +{{ +┌──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┐ +│ TERMS OF USE: MIT License │ +├──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┤ +│Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation │ +│files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, │ +│modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software│ +│is furnished to do so, subject to the following conditions: │ +│ │ +│The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.│ +│ │ +│THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE │ +│WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR │ +│COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, │ +│ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. │ +└──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┘ +}} diff --git a/lerner/clib.spin b/lerner/clib.spin new file mode 100644 index 0000000..79a4019 --- /dev/null +++ b/lerner/clib.spin @@ -0,0 +1,1015 @@ +'****************************************************************************** +' C Function Library in Spin +' Author: Dave Hein +' Copyright (c) 2010 +' See end of file for terms of use. +'****************************************************************************** +'****************************************************************************** +' Revison History +' v1.0 - 4/2/2010 First official release +'****************************************************************************** +{{ + This object provides some standard C library functions written in Spin. + There are three main types of functions provided -- string manipulation, + formatted I/O and memory allocation. C functions that use a variable + number of arguments are supported in two ways. The first way uses + unique function names for each number of arguments that is used, such + as printf3 for a printf with three arguments. The second way uses an + argument list, such as vprintf. The argument list consists of an array + of longs that holds the values of the arguments. + + The start function must be called before using any of the serial I/O or + memory allocation functions. It will setup a a serial port that transmits on + pin 30 and receives on pin 31 at 57600 baud. It will also establish a stack + space of 200 longs for the top object. The malloc heap begins immediately + after the stack space, and extends to the end of the RAM. + + Serial I/O and stack space parameters can be specified by calling start1 + instead of start. The first four parameters of start1 are the same as those + used in a call to the FullDuplexSerial start function. The fifth parameter, + stacksize, defines the size in longs of the stack space. + + Additional serial ports can be created by calling the openserial routine. + In additional to rxpin, txpin, mode and baudrate the receive and transmit + buffer sizes are also specified. A file stream pointer is returned, which + is used when calling fputc, fgetc and all of the other "f" routines. + + The functions puts, putchar and getchar use the str1, tx1 and rx1 methods + of a modified FullDuplexSerial object. This object, cserial, allows for multiple + serial ports through the use of a structer pointer called a handle. See + cserial.spin for more information. + + clib uses a file stream pointer to access I/O devices, such as the serial port. + The I/O routines that start with the letter "f" require a file stream pointer, + which is normally named stream. To save space, this object does not support + file I/O. clibsd.spin should be used if file I/O is needed. +}} + +OBJ + mem : "cmalloc" + ser : "cserial" + fstr : "cfloatstr" + +CON + NEW_LINE = 10 + SEEK_SET = 0 + SEEK_CUR = 1 + SEEK_END = 2 + + stream_serial = 1 + stream_fileio = 2 + stream_size = 8 + stream_type = 0 + stream_handle = 1 + +DAT + stdout long 0 + stdin long 0 + stdio long 0 + echoflag long 1 + linefeed long 0 + +'****************************************************************************** +' Initialization Routines +'****************************************************************************** +PUB start +{{ + This routine sets a top object stack size of 200 longs followed by the malloc + heap space. It initializes the standard I/O serial port to receive on P31 and + transmit on P30 at 57,600 baud. The serial port mode flag is set to use a + lock for multi-cog operation. It returns the stdio pointer if successful, or + NULL if not. +}} + return start1(31, 30, %110000, 115200, 200) + +PUB start1(rxpin, txpin, mode, baudrate, stacksize) +{{ + This routine calls the malloc initialization routine. The number of longs in + the top object stack is set by the value of "stacksize". It also calls the + openserial function to initialize the standard I/O serial port. It returns the + stdio pointer if successful, or NULL if not. +}} + mem.mallocinit(stacksize) + stdio := openserial(rxpin, txpin, mode, baudrate, 64, 64) + stdin := malloc(stream_size) + stdout := malloc(stream_size) + memcpy(stdin, stdio, stream_size) + memcpy(stdout, stdio, stream_size) + return stdio + +PUB openserial(rxpin, txpin, mode, baudrate, rxsize, txsize) | stream, handle +{{ + This routine allocates memory for a file info and serial port data structure. + It calls the start1 function in the cserial object to create a serial port in + the next available cog. It returns a pointer to the file info structure if + successful, or a NULL value if not successful. +}} + stream := malloc(stream_size + ser#header_size + rxsize + txsize) + ifnot stream + return 0 + handle := stream + stream_size + long[stream][stream_type] := stream_serial + long[stream][stream_handle] := handle + ifnot ser.start1(handle, rxpin, txpin, mode, baudrate, rxsize, txsize) + free(stream) + return 0 + return stream + +'****************************************************************************** +' String Routines +'****************************************************************************** +PUB strcmp(str1, str2) +{{ + This routine compares two strings and returns a value of zero if they are + equal. If "str1" is greater than "str2" it will return a positive value, and + if "str1" is less than "str2" it will return a positive value. +}} + if (strcomp(str1, str2)) + return 0 + repeat while (byte[str1] and byte[str1] == byte[str2]) + str1++ + str2++ + return byte[str1] - byte[str2] + +PUB strncmp(str1, str2, n) +{{ + This routine compares the first "n" characters of two strings. It returns a + value of zero if they are equal. If "str1" is greater than "str2" it will + return a positive value, and if "str1" is less than "str2" it will return a + negative value. +}} + if (n =< 0) + return 0 + repeat while (--n and byte[str1] and byte[str1] == byte[str2]) + str1++ + str2++ + return byte[str1] - byte[str2] + +PUB memcpy(dest, src, n) | bitflag +{{ + This routine copies "n" bytes from the memory location pointed to by "src" to + the memory location pointed to by "dest". The copy is performed as either longs, + words or bytes depending on the least significant bits of the parameters. A + bytemove is performed if "n" is less than 180 to avoid the extra computational + overhead for small mem copies. The value of "dest" is returned. +}} + if n < 180 + bytemove(dest, src, n) + return dest + bitflag := (dest | src | n) & 3 + ifnot bitflag + longmove(dest, src, n >> 2) + elseifnot bitflag & 1 + wordmove(dest, src, n >> 1) + else + bytemove(dest, src, n) + return dest + +PUB memset(dest, val, n) | bitflag +{{ + This routine sets the "n" bytes pointed to by "dest" to the value in the + least significant byte of "val". It returns the value of "dest". memset + uses either bytefill, wordfill or longfill depending on the least significant + bits of the parameters. A bytefill is used when "n" is less than 220 to + avoid the extra computational overhead for small buffers. +}} + if n < 220 + bytefill(dest, val, n) + return dest + val &= 255 + bitflag := (dest | n) & 3 + ifnot bitflag + val |= (val << 24) | (val << 16) | (val << 8) + longfill(dest, val, n >> 2) + elseifnot bitflag & 1 + val |= (val << 8) + wordfill(dest, val, n >> 1) + else + bytefill(dest, val, n) + return dest + +PUB strcat(dst, src) | dlen, slen +{{ + This routine concatenates the string pointed to by "src" to the string at + "dst". It returns the value of "dst". +}} + dlen := strsize(dst) + slen := strsize(src) + 1 + bytemove(dst + dlen, src, slen) + return dst + +PUB strcpy(dst, src) | slen +{{ + This routine copies the string pointed to by "src" to the location pointed + to by "dst". It returns the value of "dst". +}} + slen := strsize(src) + 1 + bytemove(dst, src, slen) + return dst + +PUB strncpy(dst, src, num) | slen +{{ + This routine copies the first num bytes from the src string to the dst string. + If src contains less than num bytes, then only the bytes contained in the src + string are copied, and the remaining bytes are set to zero. Note that if num + is less than or equal to the string length of src the dst string will not be + terminated with a NULL. This routine returns the value of the dst pointer. +}} + if (num < 1) + return dst + slen := strsize(src) + if (slen > num) + slen := num + bytemove(dst, src, slen) + bytefill(dst + slen, 0, num - slen) + return dst + +PUB isdigit(char) +{{ + This routine return true if the value of "char" represents an ASCII decimal + digit between 0 and 9. Otherwise, it returns false. +}} + return char => "0" and char =< "9" + +PUB itoa(number, str, base) | mask, shift, nbits, str0 +{{ + This routine converts the 32-bit value in "number" to an ASCII string at the + location pointed to by "str". The numeric base is determined by the value + of "base", and must be either 2, 4, 8, 10 or 16. Leading zeros are suppressed, + and the number is treated as unsigned except when the base is 10. The length + of the resulting string is returned. +}} + str0 := str + case base + 10 : return itoa10(number, str) + 2 : nbits := 1 + 4 : nbits := 2 + 8 : nbits := 3 + 16 : nbits := 4 + other: + byte[str] := 0 + return 0 + mask := base - 1 + if (nbits == 3) + shift := 30 + else + shift := 32 - nbits + repeat while shift > 0 and ((number >> shift) & mask) == 0 + shift -= nbits + repeat while (shift => 0) + byte[str++] := HexDigit[(number >> shift) & mask] + shift -= nbits + byte[str++] := 0 + return str - str0 - 1 + +'****************************************************************************** +' Output Routines +'****************************************************************************** +PUB set_linefeed(val) + linefeed := val + +PUB putnewline(handle) + ser.tx1(handle, 13) + if linefeed + ser.tx1(handle, 10) + +PUB puts(str) +{{ + This routine sends the contents of the string pointed to by "str" to the + standard output. +}} + fputs(str, stdout) + putnewline(stdout) + +PUB putchar(char) +{{ + This routine sends the character in "char" to the standard output. +}} + fputc(char, stdout) + +PUB fputc(char, stream) | type, handle +{{ + This routine sends the character in "char" to the output device defined by + "stream". +}} + type := long[stream][stream_type] + handle := long[stream][stream_handle] + if (type == stream_serial) + if char == NEW_LINE + putnewline(handle) + else + ser.tx1(handle, char) + +PUB fputs(str, stream) | type, handle, len, char +{{ + This routine sens the string pointed to by "str" to the output device defined + by "stream". +}} + type := long[stream][stream_type] + handle := long[stream][stream_handle] + if (type == stream_serial) + 'ser.str1(handle, str) + repeat while (char := byte[str++]) + if char == NEW_LINE + putnewline(handle) + else + ser.tx1(handle, char) + +PUB fwrite(buffer, elem_size, num, stream) | type, handle, char +{{ + This routine writes a block of data to the output device defined by "stream". + It returns the number of bytes written, or a -1 if there is an error. +}} + type := long[stream][stream_type] + handle := long[stream][stream_handle] + if (type == stream_serial) + num *= elem_size + repeat num + char := byte[buffer++] + if char == NEW_LINE + putnewline(handle) + else + ser.tx1(handle, char) + return num + +PUB printf0(fmtstr) +{{ + This is a version of "printf" with a format string and no additional parameters. +}} + vfprintf(stdout, fmtstr, @fmtstr) + +PUB printf1(fmtstr, arg1) +{{ + This is a version of "printf" with a format string and one additional parameter. +}} + vfprintf(stdout, fmtstr, @arg1) + +PUB printf2(fmtstr, arg1, arg2) +{{ + This is a version of "printf" with a format string and two additional parameters. +}} + vfprintf(stdout, fmtstr, @arg1) + +PUB printf3(fmtstr, arg1, arg2, arg3) +{{ + This is a version of "printf" with a format string and three additional parameters. +}} + vfprintf(stdout, fmtstr, @arg1) + +PUB printf4(fmtstr, arg1, arg2, arg3, arg4) +{{ + This is a version of "printf" with a format string and four additional parameters. +}} + vfprintf(stdout, fmtstr, @arg1) + +PUB printf5(fmtstr, arg1, arg2, arg3, arg4, arg5) +{{ + This is a version of "printf" with a format string and five additional parameters. +}} + vfprintf(stdout, fmtstr, @arg1) + +PUB printf6(fmtstr, arg1, arg2, arg3, arg4, arg5, arg6) +{{ + This is a version of "printf" with a format string and six additional parameters. +}} + vfprintf(stdout, fmtstr, @arg1) + +PUB printf7(fmtstr, arg1, arg2, arg3, arg4, arg5, arg6, arg7) +{{ + This is a version of "printf" with a format string and six additional parameters. +}} + vfprintf(stdout, fmtstr, @arg1) + +PUB vprintf(fmtstr, arglist) +{{ + This routine uses the string pointed to by "format" to send a formatted string + to the standard output. The parameter "arglist" points to a long array of + values that are merged with the output string dependent on the contents of the + format string. +}} + vfprintf(stdout, fmtstr, arglist) + +PUB sprintf0(str, fmtstr) +{{ + This is a version of "sprintf" with a format string and no additional parameters. +}} + vsprintf(str, fmtstr, @fmtstr) + +PUB sprintf1(str, fmtstr, arg1) +{{ + This is a version of "sprintf" with a format string and one additional parameter. +}} + vsprintf(str, fmtstr, @arg1) + +PUB sprintf2(str, fmtstr, arg1, arg2) +{{ + This is a version of "sprintf" with a format string and two additional parameters. +}} + vsprintf(str, fmtstr, @arg1) + +PUB sprintf3(str, fmtstr, arg1, arg2, arg3) +{{ + This is a version of "sprintf" with a format string and three additional parameters. +}} + vsprintf(str, fmtstr, @arg1) + +PUB sprintf4(str, fmtstr, arg1, arg2, arg3, arg4) +{{ + This is a version of "sprintf" with a format string and four additional parameters. +}} + vsprintf(str, fmtstr, @arg1) + +PUB sprintf5(str, fmtstr, arg1, arg2, arg3, arg4, arg5) +{{ + This is a version of "sprintf" with a format string and five additional parameters. +}} + vsprintf(str, fmtstr, @arg1) + +PUB sprintf6(str, fmtstr, arg1, arg2, arg3, arg4, arg5, arg6) +{{ + This is a version of "sprintf" with a format string and six additional parameters. +}} + vsprintf(str, fmtstr, @arg1) + +PUB fprintf0(stream, fmtstr) + vfprintf(stream, fmtstr, @fmtstr) + +PUB fprintf1(stream, fmtstr, arg1) + vfprintf(stream, fmtstr, @arg1) + +PUB fprintf2(stream, fmtstr, arg1, arg2) + vfprintf(stream, fmtstr, @arg1) + +PUB fprintf3(stream, fmtstr, arg1, arg2, arg3) + vfprintf(stream, fmtstr, @arg1) + +PUB fprintf4(stream, fmtstr, arg1, arg2, arg3, arg4) + vfprintf(stream, fmtstr, @arg1) + +PUB fprintf5(stream, fmtstr, arg1, arg2, arg3, arg4, arg5) + vfprintf(stream, fmtstr, @arg1) + +PUB fprintf6(stream, fmtstr, arg1, arg2, arg3, arg4, arg5, arg6) + vfprintf(stream, fmtstr, @arg1) + +PUB vfprintf(stream, fmtstr, arglist)| str[25] +{{ + This routine prints a formatted string to the output pointed to by "stream". + It calls vsprintf to generate the formatted string in the stack string array + "str", and then sends it to the output stream by calling fputs. The format is + pointed to by "format" and the parameters are contained on a long array pointed + to by "arglist". Note, care must be taken to ensure that the formatted output + string will fit within the 100-byte space provided by "str". Also, the stack + must be large enough to accomodate "str". +}} + vsprintf(@str, fmtstr, arglist) + fputs(@str, stream) + +PUB vsprintf(str, fmtstr, arglist) | arg, width, digits, fmtstr0 +{{ + This routines generates a formatted output string based on the string pointed + to by "format". The parameter "arglist" is a pointer to a long array of values + that are merged into the output string. The characters in the format string + are copied to the output string, exept for special character sequences that + start is % or \. The % character is used to merge values from "arglist". The + characters following the % are as follows: %[0][width][.digits][l][type]. + If a "0" immediately follows the % it indicates that leading zeros should be + displayed. The optional "width" paramter specifieds the minimum width of the + field. The optional ".digits" parameter specifies the number of fractional + digits for floating point, or it may also be used to specify leading zeros and + the minimum width for integer values. The "l" parameter indicates long values, + and it is ignored in this implementation. The "type" parameter is a single + character that indicates the type of output that should be generated. It can + be one of the following characters: + + d - signed decimal number + i - same as d + u - unsigned decimal number + x - hexidecimal number + o - octal number + b - binary number + c - character + s - string + e - floating-point number using scientific notation + f - floating-point number in standard notation + % - prints the % character + + The \ character is used to print the following special characters: + + b - backspace + n - newline or linefeed + r - carriage return + t - tab, or ASCII 9 + \ - prints the \ character + xxx - this inserts the value of a 3-digital octal number between 000 and 377 + + Note, care must be taken the the generated output string does not exceed the size + of the string. A string size of 100 bytes is normally sufficient. +}} + arg := long[arglist] + arglist += 4 + repeat while (byte[fmtstr]) + if (byte[fmtstr] == "%") + fmtstr0 := fmtstr++ + if (byte[fmtstr] == "0") + width := -1 + digits := getvalue(@fmtstr) + else + width := getvalue(@fmtstr) + if (byte[fmtstr] == ".") + fmtstr++ + digits := getvalue(@fmtstr) + else + digits := -1 + if (byte[fmtstr] == "l") + fmtstr++ + case byte[fmtstr] + "d", "i": str := putdec(str, arg, width, digits) + "u" : str := putudec(str, arg, width, digits) + "o" : str := putoctal(str, arg, width, digits) + "b" : str := putbinary(str, arg, width, digits) + "x" : str := puthex(str, arg, width, digits) + "f" : str := fstr.putfloatf(str, arg, width, digits) + "e" : str := fstr.putfloate(str, arg, width, digits) + "c" : byte[str++] := arg + "%" : byte[str++] := "%" + "s" : + strcpy(str, arg) + str += strsize(arg) + other : + byte[str++] := "%" + fmtstr := fmtstr0 + fmtstr++ + arg := long[arglist] + arglist += 4 + elseif (byte[fmtstr] == 92) + case byte[++fmtstr] + 0 : quit + "b" : byte[str++] := 8 + "t" : byte[str++] := 9 + "r" : byte[str++] := 13 + "n" : byte[str++] := 10 + "0".."3": byte[str++] := getoctalbyte(@fmtstr) + other : byte[str++] := byte[fmtstr] + fmtstr++ + else + byte[str++] := byte[fmtstr++] + byte[str++] := 0 + +PRI getoctalbyte(pstr) | str +{{ + This private routine is used to read a 3-digit octal number contained + in the format string. +}} + str := long[pstr] + repeat 3 + if (byte[str] & $f8) == "0" + result := (result << 3) + (byte[str++] & 7) + else + quit + long[pstr] := str - 1 + +PRI itoa10(number, str) | str0, divisor, temp +{{ + This private routine is used to convert a signed integer contained in + "number" to a decimal character string. It is called by itoa when the + numeric base parameter has a value of 10. +}} + str0 := str + if (number < 0) + byte[str++] := "-" + if (number == $80000000) + byte[str++] := "2" + number += 2_000_000_000 + number := -number + elseif (number == 0) + byte[str++] := "0" + byte[str] := 0 + return 1 + divisor := 1_000_000_000 + repeat while (divisor > number) + divisor /= 10 + repeat while (divisor > 0) + temp := number / divisor + byte[str++] := temp + "0" + number -= temp * divisor + divisor /= 10 + byte[str++] := 0 + return str - str0 - 1 + +PRI getvalue(pstr) | str +{{ + Thie private routine is used to extract the width and digits + fields from a format string. It is called by vsprintf. +}} + str := long[pstr] + ifnot isdigit(byte[str]) + return -1 + result := 0 + repeat while isdigit(byte[str]) + result := (result * 10) + byte[str++] - "0" + long[pstr] := str + +DAT +HexDigit byte "0123456789abcdef" + +PRI printpadded(str, numstr, count, width, digits) +{{ + This private routine is used to generate a formatted string + containg at least "width" characters. The value of count + must be identical to the length of the string in "str". + Leading spaces will be generated if width is larger than the + maximum of count and digits. Leading zeros will be generated + if digits is greater than count. +}} + if digits < count + digits := count + repeat while (width-- > digits) + byte[str++] := " " + if byte[numstr] == "-" + byte[str++] := byte[numstr++] + digits-- + repeat while (digits-- > count) + byte[str++] := "0" + strcpy(str, numstr) + return str + strsize(numstr) + +PRI putbinary(str, number, width, digits) | count, numstr[9] +{{ + This private routine converts a number to a string of binary digits. + printpadded is called to insert leading blanks and zeros. +}} + count := itoa(number, @numstr, 2) + return printpadded(str, @numstr, count, width, digits) + +PRI putoctal(str, number, width, digits) | count, numstr[3] +{{ + This private routine converts a number to a string of octal digits. + printpadded is called to insert leading blanks and zeros. +}} + count := itoa(number, @numstr, 8) + return printpadded(str, @numstr, count, width, digits) + +PRI puthex(str, number, width, digits) | count, numstr[3] +{{ + This private routine converts a number to a string of hexadecimal digits. + printpadded is called to insert leading blanks and zeros. +}} + count := itoa(number, @numstr, 16) + return printpadded(str, @numstr, count, width, digits) + +PRI putdec(str, number, width, digits)| count, numstr[3] +{{ + This private routine converts a signed number to a string of decimal + digits. printpadded is called to insert leading blanks and zeros. +}} + count := itoa10(number, @numstr) + return printpadded(str, @numstr, count, width, digits) + +PRI putudec(str, number, width, digits) | count, numstr[3], adjust +{{ + This private routine converts an unsigned number to a string of decimal + digits. printpadded is called to insert leading blanks and zeros. +}} + adjust := 0 + repeat while (number < 0) + number -= 1_000_000_000 + adjust++ + count := itoa10(number, @numstr) + byte[@numstr] += adjust + return printpadded(str, @numstr, count, width, digits) + +'****************************************************************************** +' Input Routines +'****************************************************************************** +PUB getchar +{{ + This routine returns a single character from the standard input. It will not + return until a character has been received. +}} + return fgetc(stdin) + +PUB gets(str) | last +{{ + This routine returns a string from the standard input. It will not return until + is has received a carriage return. The carriage return is not included in the + returned string. Received characters are echoed back out ot the the serial port. + Backspaces will cause the previous character to be removed from the buffer, and + a character sequence of backspace, space and backspace will be transmitted to the + serial port to erase the previous character. Backspace characters are not inserted + into the string. +}} + result := fgets(str, 100000000, stdin) + if result > 0 + last := strsize(str) - 1 + if last => 0 + str += last + if byte[str] == NEW_LINE + byte[str] := 0 + +PRI gets1(str, size, handle) | char, str0, editflag, echoflag1 + editflag := (handle == long[stdio][stream_handle]) + echoflag1 := editflag & echoflag + str0 := str + repeat while (size > 1) + char := ser.rx1(handle) + if (char == 8 AND editflag) + if (str > str0) + if echoflag1 + ser.tx1(handle, 8) + ser.tx1(handle, " ") + ser.tx1(handle, 8) + str-- + size++ + next + if char == 4 + if (str == str0) + str0 := 0 + quit + if char == NEW_LINE OR char == 13 + if echoflag1 + putnewline(handle) + byte[str++] := NEW_LINE + quit + else + if echoflag1 + ser.tx1(handle, char) + byte[str++] := char + size-- + byte[str] := 0 + return str0 + +PUB fgetc(stream) | char, type, handle +{{ + This routine returns a single character from the input device defined by "stream". + It will not return until a character has been received. +}} + type := long[stream][stream_type] + handle := long[stream][stream_handle] + if (type == stream_serial) + result := ser.rx1(handle) + +PUB fgets(str, size, stream) | type, handle +{{ + This routine returns a string from the input device defined by "stream". It will + return until it has either received a carriage return or "size" number of characters. +}} + type := long[stream][stream_type] + handle := long[stream][stream_handle] + size-- + if (type == stream_serial) + result := gets1(str, size, handle) + +PUB fread(buffer, elem_size, num, stream) | type, handle +{{ + This routine reads a block of data from the input device defined by "stream". + It returns the number of bytes read, or a -1 if the end of file is + encountered. +}} + type := long[stream][stream_type] + handle := long[stream][stream_handle] + if (type == stream_serial) + num *= elem_size + repeat num + byte[buffer++] := ser.rx1(handle) + return num + +PUB scanf1(fmtstr, parg1) +{{ + This routine is a version of "scanf" with a format string and one parameter. +}} + vscanf(fmtstr, @parg1) + +PUB scanf2(fmtstr, parg1, parg2) +{{ + This routine is a version of "scanf" with a format string and two parameters. +}} + vscanf(fmtstr, @parg1) + +PUB scanf3(fmtstr, parg1, parg2, parg3) +{{ + This routine is a version of "scanf" with a format string and three parameters. +}} + vscanf(fmtstr, @parg1) + +PUB scanf4(fmtstr, parg1, parg2, parg3, parg4) +{{ + This routine is a version of "scanf" with a format string and four parameters. +}} + vscanf(fmtstr, @parg1) + +PUB vscanf(fmtstr, arglist) | str[25] +{{ + This routine reads a string from the standard input using gets, and it converts + the data based on the format in the "format" string. The converted values are + stored at the locations determined by the list of long pointers in "arglist". + See vsscanf for the description of the format string. +}} + gets(@str) + vsscanf(@str, fmtstr, arglist) + +PUB sscanf1(str, fmtstr, parg1) +{{ + This is a version of sscanf with a format string and one parameter. +}} + vsscanf(str, fmtstr, @parg1) + +PUB sscanf2(str, fmtstr, parg1, parg2) +{{ + This is a version of sscanf with a format string and two parameters. +}} + vsscanf(str, fmtstr, @parg1) + +PUB sscanf3(str, fmtstr, parg1, parg2, parg3) +{{ + This is a version of sscanf with a format string and three parameters. +}} + vsscanf(str, fmtstr, @parg1) + +PUB sscanf4(str, fmtstr, parg1, parg2, parg3, parg4) +{{ + This is a version of sscanf with a format string and four parameters. +}} + vsscanf(str, fmtstr, @parg1) + +PUB vsscanf(str, fmtstr, arglist) | parg +{{ + This routine converts the contents of the string pointed to by "str" into + numerical values that are stored at the locations derermined by the list of + long pointers in "arglist". The format string contains conversion flags, + which are prefixed with the % character. The list of conversion flags is + as follows: + + b - Convert a binary number + o - Convert an octal number + d - Convert a signed decimal number + x - Convert a hexadecimal number + f - Convert a floating point number + e - Same as f + + Any other characters will be ignored, and will cause a character to be skipped + on the input string. Leading spaces are also ignored when converting a + number. +}} + parg := long[arglist] + arglist += 4 + repeat while byte[fmtstr] + if byte[fmtstr++] == "%" + case byte[fmtstr] + "b" : long[parg] := getbin(@str) + "o" : long[parg] := getoct(@str) + "d" : long[parg] := getdec(@str) + "x" : long[parg] := gethex(@str) + "f", "e" : long[parg] := fstr.strtofloat(@str) + other: str++ + fmtstr++ + parg := long[arglist] + arglist += 4 + else + str++ + +PRI getbin(pstr) | str +{{ + This private routine is used by vsscanf to convert a string of binary digits to + a numerical value. +}} + str := long[pstr] + repeat while byte[str] == " " + str++ + repeat while (byte[str] & $fe) == "0" + result := (result << 1) + (byte[str++] & 1) + long[pstr] := str + +PRI getoct(pstr) | str +{{ + This private routine is used by vsscanf to convert a string of octal digits to + a numerical value. +}} + str := long[pstr] + repeat while byte[str] == " " + str++ + repeat while (byte[str] & $f8) == "0" + result := (result << 3) + (byte[str++] & 7) + long[pstr] := str + +PRI gethex(pstr) | str, char +{{ + This private routine is used by vsscanf to convert a string of hexadecimal digits to + a numerical value. +}} + str := long[pstr] + repeat while byte[str] == " " + str++ + repeat + char := byte[str++] + case char + "0".."9": result := (result << 4) + char - "0" + "a".."f": result := (result << 4) + char - "a" + 10 + "A".."F": result := (result << 4) + char - "A" + 10 + other : quit + long[pstr] := str - 1 + +PRI getdec(pstr) | str, signflag +{{ + This private routine is used by vsscanf to convert a string of decimal digits to + a numerical value. +}} + signflag := 0 + str := long[pstr] + repeat while byte[str] == " " + str++ + if isdigit(byte[str]) + result:= byte[str] - "0" + elseif byte[str] == "-" + signflag := 1 + elseif byte[str] <> "+" + long[pstr] := str + return + str++ + repeat while isdigit(byte[str]) + result := (result * 10) + byte[str++] - "0" + if signflag + result := -result + long[pstr] := str + +'****************************************************************************** +' Malloc Routines +'****************************************************************************** +PUB malloc(size) +{{ + This routine provides an interface to the malloc routine in cmalloc.spin +}} + return mem.malloc(size) + +PUB free(ptr) +{{ + This routine provides an interface to the free routine in cmalloc.spin +}} + return mem.free(ptr) + +PUB calloc(size) +{{ + This routine provides an interface to the calloc routine in cmalloc.spin +}} + return mem.calloc(size) + +'****************************************************************************** +' Standard I/O redirection routines +'****************************************************************************** +PUB setstdin(stream) + memcpy(stdin, stream, stream_size) + +PUB setstdout(stream) + echoflag := 0 + memcpy(stdout, stream, stream_size) + +PUB resetstdin + memcpy(stdin, stdio, stream_size) + +PUB resetstdout + echoflag := 1 + memcpy(stdout, stdio, stream_size) + +PUB getstdin + return stdin + +PUB getstdout + return stdout + +PUB CheckStackSpace + printf1(string("The last %d longs have not been used on the stack\n"), mem.CheckStackSpace) + +PUB CheckMallocSpace | ptr, numalloc, numfree + numalloc := 0 + numfree := 0 + ptr := mem.GetMallocList + printf0(string("MallocList\n")) + repeat while(ptr) + printf2(string("addr = %04x size = %d\n"), ptr, word[ptr][1]) + numalloc += word[ptr][1] + ptr := word[ptr] + ptr := mem.GetFreeList + printf0(string("FreeList\n")) + repeat while(ptr) + printf2(string("addr = %04x size = %d\n"), ptr, word[ptr][1]) + numfree += word[ptr][1] + ptr := word[ptr] + printf2(string("%d bytes allocated, %d bytes free\n"), numalloc, numfree) + +{{ +┌──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┐ +│ TERMS OF USE: MIT License │ +├──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┤ +│Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation │ +│files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, │ +│modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software│ +│is furnished to do so, subject to the following conditions: │ +│ │ +│The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.│ +│ │ +│THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE │ +│WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR │ +│COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, │ +│ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. │ +└──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┘ +}} diff --git a/lerner/cmalloc.spin b/lerner/cmalloc.spin new file mode 100644 index 0000000..e9994f5 --- /dev/null +++ b/lerner/cmalloc.spin @@ -0,0 +1,208 @@ +'****************************************************************************** +' C malloc written in Spin +' Author: Dave Hein +' Copyright (c) 2010 +' See end of file for terms of use. +'****************************************************************************** +'****************************************************************************** +' Revison History +' v1.0 - 4/2/2010 First official release +'****************************************************************************** +{ + This object contains the malloc, free and calloc routines used by clib. +} +OBJ + sys : "sysdefs" + +CON + NEXT_BLK = 0 + BLK_SIZE = 1 + HDR_SIZE = 4 + 'RAM_SIZE = $8000 + RAM_SIZE = sys#rendezvous + CHECKWORD = $dead1eaf + +DAT + locknum long 0 + memfreelist long 0 + malloclist long 0 + laststackaddr long 0 + +PUB CheckStackSpace | addr + repeat addr from laststackaddr to 0 step 4 + if long[addr] <> CHECKWORD + quit + result++ + +PUB GetMallocList + return malloclist + +PUB GetFreeList + return memfreelist + +PUB mallocinit(stacksize) | currblk, addr +'' Initialize the malloc heap "stacksize" longs after the current stack pointer +'' Allocate a lock and return the lock number plus one if successful, or zero if not + locknum := locknew + 1 + currblk := @stacksize + (stacksize << 2) + 'Fill stack with checkword + laststackaddr := currblk - 4 + longfill(@stacksize + 40, CHECKWORD, stacksize - 10) + memfreelist := currblk + malloclist := 0 + word[currblk]{NEXT_BLK} := 0 + word[currblk][BLK_SIZE] := RAM_SIZE - currblk + return locknum + +PUB malloc(size) | prevblk, currblk, nextblk, prevblk0, currblk0, size0 +'' Allocate a memory block of "size" bytes. Return a pointer to the clock if +'' successful, or zero if a large enough memory block could not be found + 'ser.dbprintf1(string("malloc: %d\n"), size) + prevblk := 0 + prevblk0 := 0 + currblk0 := 0 + if (size =< 0) + return 0 + repeat until not lockset(locknum - 1) + currblk := memfreelist + + ' Adjust size to nearest long plus the header size + size := ((size + 3) & (!3)) + HDR_SIZE + + ' Search for a block of memory + repeat while (currblk) + if (word[currblk][BLK_SIZE] => size) + prevblk0 := prevblk + currblk0 := currblk + prevblk := currblk + currblk := word[currblk]{NEXT_BLK} + + currblk := currblk0 + prevblk := prevblk0 + + ' Return null if block not found + if (currblk == 0) + lockclr(locknum - 1) + return 0 + + ' Split block if larger than needed + size0 := word[currblk][BLK_SIZE] - size + if (size0 => HDR_SIZE + 4) + word[currblk][BLK_SIZE] := size0 + currblk += size0 + word[currblk][BLK_SIZE] := size + + ' Otherwise, use space without splitting and remove from memfreelist + else + nextblk := word[currblk]{NEXT_BLK} + if (prevblk) + word[prevblk]{NEXT_BLK} := nextblk + else + memfreelist := nextblk + + ' Add to the beginning of the malloc list + word[currblk]{NEXT_BLK} := malloclist + malloclist := currblk + lockclr(locknum - 1) + return currblk + HDR_SIZE + +PUB freeraw(ptr, size) + repeat until not lockset(locknum - 1) + word[ptr][BLK_SIZE] := size + meminsert(ptr) + lockclr(locknum - 1) + return 1 + +PUB free(ptr) | prevblk, currblk, nextblk +'' Return the memory block at "ptr" to the free list. Return a value of one +'' if successful, or zero if the memory block was not on the allocate list. + prevblk := 0 + repeat until not lockset(locknum - 1) + nextblk := malloclist + currblk := ptr - HDR_SIZE + + ' Search the malloclist for the currblk pointer + repeat while (nextblk) + if (currblk == nextblk) + ' Remove from the malloc list + if (prevblk) + word[prevblk]{NEXT_BLK} := word[nextblk]{NEXT_BLK} + else + malloclist := word[nextblk]{NEXT_BLK} + ' Add to the free list + meminsert(nextblk) + lockclr(locknum - 1) + return 1 + prevblk := nextblk + nextblk := word[nextblk]{NEXT_BLK} + + ' Return a NULL value if not found + lockclr(locknum - 1) + return 0 + +PRI meminsert(currblk) | prevblk, nextblk +'' Insert a memory block back into the free list. Merge blocks together if +'' the memory block is contiguous with other blocks on the list. + prevblk := 0 + nextblk := memfreelist + + ' Find Insertion Point + repeat while (nextblk) + if ((currblk => prevblk) and (currblk =< nextblk)) + quit + prevblk := nextblk + nextblk := word[nextblk]{NEXT_BLK} + + ' Merge with the previous block if contiguous + if (prevblk and (prevblk + word[prevblk][BLK_SIZE] == currblk)) + word[prevblk][BLK_SIZE] += word[currblk][BLK_SIZE] + ' Also merge with next block if contiguous + if (prevblk + word[prevblk][BLK_SIZE] == nextblk) + word[prevblk][BLK_SIZE] += word[nextblk][BLK_SIZE] + word[prevblk]{NEXT_BLK} := word[nextblk]{NEXT_BLK} + + ' Merge with the next block if contiguous + elseif (nextblk and (currblk + word[currblk][BLK_SIZE] == nextblk)) + word[currblk][BLK_SIZE] += word[nextblk][BLK_SIZE] + word[currblk]{NEXT_BLK} := word[nextblk]{NEXT_BLK} + if (prevblk) + word[prevblk]{NEXT_BLK} := currblk + else + memfreelist := currblk + + ' Insert in the middle of the free list if not contiguous + elseif (prevblk) + word[prevblk]{NEXT_BLK} := currblk + word[currblk]{NEXT_BLK} := nextblk + + ' Otherwise, insert at beginning of the free list + else + memfreelist := currblk + word[currblk]{NEXT_BLK} := nextblk + +PUB calloc(size) | ptr +'' Allocate a memory block of "size" bytes and initialize to zero. Return +'' a pointer to the memory block if successful, or zero if a large enough +'' memory block could not be found. + ptr := malloc(size) + if (ptr) + longfill(ptr, 0, (size + 3) >> 2) + return ptr + +{{ +┌──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┐ +│ TERMS OF USE: MIT License │ +├──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┤ +│Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation │ +│files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, │ +│modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software│ +│is furnished to do so, subject to the following conditions: │ +│ │ +│The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.│ +│ │ +│THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE │ +│WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR │ +│COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, │ +│ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. │ +└──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┘ +}} diff --git a/lerner/cserial.spin b/lerner/cserial.spin new file mode 100644 index 0000000..a4f0eaa --- /dev/null +++ b/lerner/cserial.spin @@ -0,0 +1,584 @@ +'****************************************************************************** +' Serial Driver for the C Function Library in Spin +' Author: Dave Hein +' Copyright (c) 2010 +' See end of file for terms of use. +'****************************************************************************** +'****************************************************************************** +' Revison History +' v1.0 - 4/2/2010 First official release +'****************************************************************************** +{{ + This is a modified version of Ghip Gracey's Full-Duplex Serial Driver. This + serial driver has the following new features: + + - Multiple serial ports may be started from any object or cog + - Any serial port is accessable from any object or cog using a device handle + - Transmit and receiver buffers can be different sizes + - Buffer sizes are defined by calling parameters + - Mode bit 4 enables the use of a lock to make the transmit multi-cog safe + + The original FullDuplexSerial routines are retained for compatibility. A + handle is maintained locally so that the caller does not have to provide the + buffers or handle for the first serial port. + + The enhanced methods use a handle, which is a pointer to a memory buffer that + contains the serial port's state information and transmit and receive buffers. + The size of the memory buffer is equal to header_size + rxsize + txsize in + bytes. The buffer must be long aligned. The transmit and receive buffer + sizes must be a power of 2. They can range anywhere from a value of 2 up to + the size of the available memory. +}} +CON +' Data structure byte offsets + ' long variables + tm_seconds = 0 + tm_clkticks = 4 + bit_ticks = 8 + ' word variables + rx_head = 12 + rx_tail = 14 + tx_head = 16 + tx_tail = 18 + rx_buffer = 20 + tx_buffer = 22 + rx_mask = 24 + tx_mask = 26 + ' byte variables + cog = 28 + lock = 29 + rx_pin = 30 + tx_pin = 31 + rxtx_mode = 32 + header_size = 36 + +' Data structure storage for the default serial port +DAT + handle1 long 0 + data_struct long 0[(header_size+16+16)/4] + +'' +''***************************************************************************** +''Original FullDuplexSerial routines for compatibility +''***************************************************************************** +PUB rxflush +'' Flush receive buffer + rxflush1(handle1) + +PUB rxcheck +'' Check if byte received (never waits) +'' returns -1 if no byte received, $00..$FF if byte + return rxcheck1(handle1) + +PUB rxtime(ms) +'' Wait ms milliseconds for a byte to be received +'' returns -1 if no byte received, $00..$FF if byte + return rxtime1(handle1, ms) + +PUB rx +'' Receive byte (may wait for byte) +'' returns $00..$FF + return rx1(handle1) + +PUB tx(txbyte) +'' Send byte (may wait for room in buffer) + tx1(handle1, txbyte) + +PUB str(stringptr) +'' Send string + str1(handle1, stringptr) + +PUB dec(value) +'' Print a decimal number + dec1(handle1, value) + +PUB hex(value, digits) +'' Print a hexadecimal number + hex1(handle1, value, digits) + +PUB bin(value, digits) +'' Print a binary number + bin1(handle1, value, digits) + +'' +''***************************************************************************** +''Enhanced routines +''***************************************************************************** +PUB kbhit + return kbhit1(handle1) + +PUB kbhit1(handle) + return word[handle+rx_tail] <> word[handle+rx_head] + +PUB rxflush1(handle) +'' Flush receive buffer + word[handle+rx_tail] := word[handle+rx_head] + +PUB rxcheck1(handle) : rxbyte | rx_tail1, rx_buffer1 +'' Check if byte received (never waits) +'' returns -1 if no byte received, $00..$FF if byte + rxbyte-- + rx_tail1 := word[handle+rx_tail] + if rx_tail1 <> word[handle+rx_head] + rx_buffer1 := word[handle+rx_buffer] + rxbyte := byte[rx_buffer1+rx_tail1] + word[handle+rx_tail] := (rx_tail1 + 1) & word[handle+rx_mask] + +PUB rxtime1(handle, ms) : rxbyte | t +'' Wait ms milliseconds for a byte to be received +'' returns -1 if no byte received, $00..$FF if byte + t := cnt + repeat until (rxbyte := rxcheck1(handle)) => 0 or (cnt - t) / (clkfreq / 1000) > ms + +PUB rx1(handle) : rxbyte +'' Receive byte (may wait for byte) +'' returns $00..$FF + repeat while (rxbyte := rxcheck1(handle)) < 0 + +PRI txchar(handle, txbyte) | tx_mask1, tx_buffer1, tx_head1, tx_head2 +'' Send byte (may wait for room in buffer) + tx_mask1 := word[handle+tx_mask] + tx_buffer1 := word[handle+tx_buffer] + tx_head1 := word[handle+tx_head] + tx_head2 := (tx_head1 + 1) & tx_mask1 + repeat until (word[handle+tx_tail] <> tx_head2) +{ + if (byte[handle+rxtx_mode] & %100000) and (txbyte == 10) + txbyte := 13 +} + byte[tx_buffer1+tx_head1] := txbyte + word[handle+tx_head] := tx_head2 + + if byte[handle+rxtx_mode] & %1000 + rx1(handle) + +PUB tx1(handle, txbyte) | uselock, locknum +'' Send byte - Use lock if mode bit 4 is set + uselock := byte[handle+rxtx_mode] & %10000 + + if (uselock) + locknum := byte[handle+lock] - 1 + repeat until not lockset(locknum) + + txchar(handle, txbyte) + + if (uselock) + lockclr(locknum) + +PUB str1(handle, stringptr) | uselock, locknum, value +'' Send string - Use lock if mode bit 4 is set + uselock := byte[handle+rxtx_mode] & %10000 + + if (uselock) + locknum := byte[handle+lock] - 1 + repeat until not lockset(locknum) + + repeat strsize(stringptr) + txchar(handle, byte[stringptr++]) + + if (uselock) + lockclr(locknum) + +PUB dec1(handle, value) | i +'' Print a decimal number + if (value < 0) + tx1(handle, "-") + if (value == NEGX) + tx1(handle, "2") + value += 2_000_000_000 + value := -value + + i := 1_000_000_000 + repeat while (i > value and i > 1) + i /= 10 + repeat while (i > 0) + tx1(handle, value/i + "0") + value //= i + i /= 10 + +PUB hex1(handle, value, digits) +'' Print a hexadecimal number + value <<= (8 - digits) << 2 + repeat digits + tx(lookupz((value <-= 4) & $F : "0".."9", "A".."F")) + +PUB bin1(handle, value, digits) +'' Print a binary number + value <<= 32 - digits + repeat digits + tx((value <-= 1) & 1 + "0") + +PUB gethandle1 +'' Get the local handle + return handle1 + +PUB dbprintf0(fmtstr) + dbprintf(fmtstr, @fmtstr) + +PUB dbprintf1(fmtstr, arg1) + dbprintf(fmtstr, @arg1) + +PUB dbprintf2(fmtstr, arg1, arg2) + dbprintf(fmtstr, @arg1) + +PUB dbprintf3(fmtstr, arg1, arg2, arg3) + dbprintf(fmtstr, @arg1) + +PUB dbprintf4(fmtstr, arg1, arg2, arg3, arg4) + dbprintf(fmtstr, @arg1) + +PUB dbprintf5(fmtstr, arg1, arg2, arg3, arg4, arg5) + dbprintf(fmtstr, @arg1) + +PUB dbprintf6(fmtstr, arg1, arg2, arg3, arg4, arg5, arg6) + dbprintf(fmtstr, @arg1) + +PUB dbprintf(fmtstr, arglist) | arg, val, digits + arg := long[arglist] + arglist += 4 + repeat while (val := byte[fmtstr++]) + if (val == "%") + digits := 0 + repeat + case (val := byte[fmtstr++]) + "d" : dec(arg) + "x" : + ifnot digits + digits := 8 + hex(arg, digits) + "b" : + ifnot digits + digits := 32 + bin(arg, digits) + "s" : str(arg) + "0".."9": + digits := (digits * 10) + val - "0" + next + 0 : return + other: tx(val) + quit + arg := long[arglist] + arglist += 4 + elseif (val == "\") + case (val := byte[fmtstr++]) + "n" : tx(13) + "r" : tx(10) + 0 : return + other: tx(val) + else + tx(val) + +PUB settime(seconds, clkticks) | handle + handle := handle1 + long[handle + tm_seconds] := seconds + long[handle + tm_clkticks] := clkticks | $80000000 + repeat while long[handle + tm_clkticks] & $c0000000 + +PUB gettime(pclkticks) | handle + handle := handle1 + long[handle + tm_clkticks] := $40000000 + repeat while long[handle + tm_clkticks] & $c0000000 + long[pclkticks] := long[handle + tm_clkticks] + return long[handle + tm_seconds] + +'' +''***************************************************************************** +''Original FullDuplexSerial routines for compatibility +''***************************************************************************** +PUB start(rxpin, txpin, mode, baudrate) +'' Start serial driver - starts a cog +'' returns false if no cog available +'' +'' mode bit 0 = invert rx +'' mode bit 1 = invert tx +'' mode bit 2 = open-drain/source tx +'' mode bit 3 = ignore tx echo on rx +'' mode bit 4 = use lock +'' mode bit 5 = convert LF to CR on tx + return start1(@data_struct, rxpin, txpin, mode, baudrate, 256, 16) + +'' +''***************************************************************************** +''Enhanced routines +''***************************************************************************** +PUB start1(handle, rxpin, txpin, mode, baudrate, rxsize, txsize) : okay +'' Start serial driver - starts a cog +'' returns false if no cog available +'' +'' mode bit 0 = invert rx +'' mode bit 1 = invert tx +'' mode bit 2 = open-drain/source tx +'' mode bit 3 = ignore tx echo on rx +'' mode bit 4 = use lock +'' mode bit 5 = convert LF to CR on tx + if (handle1) + if (handle1 == handle) + stop1(handle) + else + handle1 := handle + wordfill(handle+rx_head, 0, 4) + long[handle+tm_clkticks] := clkfreq + cnt + long[handle+tm_seconds] := 0 + byte[handle+rx_pin] := rxpin + byte[handle+tx_pin] := txpin + byte[handle+rxtx_mode] := mode + long[handle+bit_ticks] := clkfreq / baudrate + word[handle+rx_buffer] := handle + header_size + word[handle+tx_buffer] := handle + header_size + rxsize + word[handle+rx_mask] := rxsize - 1 + word[handle+tx_mask] := txsize - 1 + if (mode & %10000) + okay := byte[handle+lock] := locknew + 1 + if (okay == 0) + return 0 + okay := byte[handle+cog] := cognew(@entry, handle) + 1 + +PUB stop1(handle) | cog1 +'' Stop serial driver - frees a cog + cog1 := byte[handle+cog] + if cog1 + cogstop(cog1 - 1) + longfill(handle, 0, header_size >> 2) + +DAT + +'*********************************** +'* Assembly language serial driver * +'*********************************** + + org +' +' +' Entry +' +entry mov t1, par + add t1, #rx_pin + rdbyte t2,t1 'get rx_pin + mov rxbitmask,#1 + shl rxbitmask,t2 + + add t1,#1 'get tx_pin + rdbyte t2,t1 + mov txbitmask,#1 + shl txbitmask,t2 + + add t1,#1 'get rxtx_mode + rdbyte rxtxmode,t1 + + mov t1, par + add t1, #bit_ticks 'get bit_ticks + rdlong bitticks,t1 + + mov t1, par + add t1, #tm_seconds 'get tmseconds + rdlong tmseconds,t1 + + mov t1, par + add t1, #tm_clkticks 'get tmclkticks + rdlong tmclkticks, #0 'get clkfreq + + rdlong tmclkticks,t1 + + mov t1, par + add t1, #rx_buffer 'get rx_buffer ptr + rdword rxbuff,t1 + + add t1,#2 'get tx_buffer ptr + rdword txbuff,t1 + + add t1,#2 'get rx_mask + rdword rxmask,t1 + + add t1,#2 'get tx_mask + rdword txmask,t1 + + test rxtxmode,#%100 wz 'init tx pin according to mode + test rxtxmode,#%010 wc + if_z_ne_c or outa,txbitmask + if_z or dira,txbitmask + + mov txcode,#transmit 'initialize multitasking addresses + mov clcode,#clock + +' +' +' Receive +' +receive jmpret rxcode,txcode 'run a chunk of transmit code, then return + + test rxtxmode,#%001 wz 'wait for start bit on rx pin + test rxbitmask,ina wc + if_z_eq_c jmp #receive + + mov rxbits,#9 'ready to receive byte + mov rxcnt,bitticks + shr rxcnt,#1 + add rxcnt,cnt + +:bit add rxcnt,bitticks 'ready next bit period + +:wait jmpret rxcode,txcode 'run a chuck of transmit code, then return + + mov t1,rxcnt 'check if bit receive period done + sub t1,cnt + cmps t1,#0 wc + if_nc jmp #:wait + + test rxbitmask,ina wc 'receive bit on rx pin + rcr rxdata,#1 + djnz rxbits,#:bit + + shr rxdata,#32-9 'justify and trim received byte + and rxdata,#$FF + test rxtxmode,#%001 wz 'if rx inverted, invert byte + if_nz xor rxdata,#$FF + + mov t1, par + add t1, #rx_head + rdword t2, t1 'save received byte and inc head + add t2,rxbuff + wrbyte rxdata,t2 + sub t2,rxbuff + add t2,#1 + and t2,rxmask + wrword t2,t1 + + jmp #receive 'byte done, receive next byte +' +' +' Transmit +' +transmit jmpret txcode,clcode 'run a chunk of clock code, then return + + mov t1,par 'check for head <> tail + add t1,#tx_head + rdword t2,t1 + add t1,#2 + rdword t3,t1 + cmp t2,t3 wz + if_z jmp #transmit + + add t3,txbuff 'get byte and inc tail + rdbyte txdata,t3 + sub t3,txbuff + add t3,#1 + and t3,txmask + wrword t3,t1 + + or txdata,#$100 'ready byte to transmit + shl txdata,#2 + or txdata,#1 + mov txbits,#11 + mov txcnt,cnt + +:bit test rxtxmode,#%100 wz 'output bit on tx pin according to mode + test rxtxmode,#%010 wc + if_z_and_c xor txdata,#1 + shr txdata,#1 wc + if_z muxc outa,txbitmask + if_nz muxnc dira,txbitmask + add txcnt,bitticks 'ready next cnt + +:wait jmpret txcode,clcode 'run a chunk of clock code, then return + + mov t1,txcnt 'check if bit transmit period done + sub t1,cnt + cmps t1,#0 wc + if_nc jmp #:wait + + djnz txbits,#:bit 'another bit to transmit? + + jmp #transmit 'byte done, transmit next byte + +clock jmpret clcode,rxcode 'run the receive task + mov t2, par + add t2, #tm_clkticks + rdlong t1, t2 + shl t1, #1 wc + if_c jmp #setclock + shl t1, #1 wc + if_c jmp #getclock + mov t1, cnt + sub t1, tmclkticks + shl t1, #1 wc + if_c jmp #clock + add tmseconds, #1 + rdlong t1, #0 + add tmclkticks, t1 + jmp #clock + +setclock shl t1, #3 + shr t1, #4 + rdlong tmclkticks, #0 + add tmclkticks, cnt + sub tmclkticks, t1 + mov t2, par + add t2, #tm_seconds + rdlong tmseconds, t2 + mov t2, par + add t2, #tm_clkticks + wrlong t1, t2 + jmp #clock + +getclock rdlong t1, #0 + mov t2, cnt + add t2, t1 + sub t2, tmclkticks + mov t3, par + add t3, #tm_seconds + wrlong tmseconds, t3 + mov t3, par + add t3, #tm_clkticks + wrlong t2, t3 + jmp #clock + +' +' +' Uninitialized data +' +t1 res 1 +t2 res 1 +t3 res 1 + +rxtxmode res 1 +bitticks res 1 + +rxbitmask res 1 +rxbuff res 1 +rxdata res 1 +rxbits res 1 +rxcnt res 1 +rxcode res 1 +rxmask res 1 + +txbitmask res 1 +txbuff res 1 +txdata res 1 +txbits res 1 +txcnt res 1 +txcode res 1 +txmask res 1 + +tmclkticks res 1 +tmseconds res 1 +clcode res 1 + + fit $100 + +{{ +┌──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┐ +│ TERMS OF USE: MIT License │ +├──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┤ +│Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation │ +│files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, │ +│modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software│ +│is furnished to do so, subject to the following conditions: │ +│ │ +│The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.│ +│ │ +│THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE │ +│WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR │ +│COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, │ +│ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. │ +└──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┘ +}} diff --git a/lerner/exit.spin b/lerner/exit.spin new file mode 100644 index 0000000..dc5053f --- /dev/null +++ b/lerner/exit.spin @@ -0,0 +1,34 @@ +obj + sys : "sysdefs" + +dat + run_prog + long $a0bc65f0, $08bc6e32, $80fc6404, $08bc7032, $80fc6404, $08bc6032 + long $80fc6404, $08bc6232, $80fc63ff, $28fc6209, $08fc6600, $00fc6804 + long $083c6029, $083c5c2a, $083c582b, $08bc642b, $863c642c, $5c68000f + long $80fc6001, $80fc5d00, $80fc5d00, $e4fc620c, $087c6600, $007c6804 + long $04fc6a08, $04fc6c0a, $a0bc6236, $84bc6235, $28fc6202, $083c5a35 + long $80fc6a04, $e4fc621d, $083c5a36, $80fc6c04, $083c6e36, $80fc6c04 + long $083c7036, $0cfc6401, $60fc6407, $68bc5e32, $0c7c5e02, $00007fd8 + long $00007fdc, $00007fd4, $00000072, $00000000, $00000000, $0007c010 + + arg_list long 0[4] + +pub exit(retval) | i, cogspi + cogspi := long[sys#SPI_engine_cog] - 1 + arg_list[2] := long[sys#shell_sector] + arg_list[3] := long[sys#shell_size] + long[sys#return_value] := retval + + ' Stop all the cogs except this one and the SD SPI cog + repeat i from 0 to 7 + if (i <> cogid and i <> cogspi) + cogstop(i) + + ' Clear and return all the locks + repeat i from 0 to 7 + lockclr(i) + lockret(i) + + ' Start run_prog in this cog + coginit(cogid, @run_prog, @arg_list) diff --git a/lerner/lerner.spin b/lerner/lerner.spin new file mode 100644 index 0000000..9951aa2 --- /dev/null +++ b/lerner/lerner.spin @@ -0,0 +1,1541 @@ +'********************************************** +'* Spin code generated by cspin version 0.066 * +'********************************************** +{ +Lerner 0.08 +Copyright (c) 2011, Dave Hein +See end of file for terms of use +} +CON + _clkmode = xinput + _xinfreq = $4e495053 ' SPIN + +OBJ + c : "clib" + ser : "cserial" + 'i2c : "Basic_I2C_Driver" + spinix : "exit" + +' Constants +CON + TYPE_NOUN = 1 + TYPE_VERB = 2 + TYPE_PRONOUN = 3 + TYPE_ADJECT = 4 + TYPE_OTHER = 5 + TYPE_NAME = 6 + TYPE_ARTICLE = 7 + TYPE_QUERY = 8 + TYPE_RELATED = 32 + TYPE_EQUATE = 33 + TYPE_RELATE2 = 34 + TYPE_MASK = $3f + DELETE_MASK = $40 + MEMORY_MASK = 31 + BUF_SIZE = 10000 + +' Global variables +DAT + speak long 0 + seed long 1 + debugmode long 0 + changeflag long 0 + MemoryIndex long 0 + datstr byte "noun", 0, "verb", 0, "pronoun", 0, "adjective", 0 + byte "other", 0, "name", 0, "article", 0, "query", 0 + types long @datstr + 16, @datstr + 21, @datstr + 26, @datstr + 34 + long @datstr + 44, @datstr + 50, @datstr + 55, @datstr + 63 + +' Basic vocabulary + basicsize long 249 + basicvocab byte + byte $03,$49,$00,$00,$19,$02,$61,$6d,$00,$00,$6b,$07,$61,$00,$00,$00 + byte $01,$68,$75,$6d,$61,$6e,$00,$00,$00,$20,$00,$05,$00,$10,$00,$00 + byte $03,$79,$6f,$75,$00,$00,$38,$02,$61,$72,$65,$00,$00,$70,$06,$4c + byte $65,$72,$6e,$65,$72,$00,$00,$50,$20,$00,$27,$00,$2e,$00,$61,$02 + byte $69,$73,$00,$00,$75,$01,$70,$72,$6f,$67,$72,$61,$6d,$00,$00,$00 + byte $20,$00,$3f,$00,$45,$00,$66,$02,$65,$71,$75,$61,$6c,$73,$00,$01 + byte $17,$21,$00,$2e,$00,$00,$21,$00,$20,$00,$00,$21,$00,$27,$00,$7a + byte $21,$00,$05,$01,$04,$21,$00,$05,$00,$7f,$21,$00,$3f,$00,$00,$21 + byte $00,$27,$00,$00,$21,$00,$3f,$00,$00,$02,$65,$71,$75,$61,$6c,$00 + byte $01,$12,$21,$00,$57,$00,$00,$21,$01,$09,$00,$00,$08,$77,$68,$6f + byte $00,$01,$2b,$08,$77,$68,$61,$74,$00,$01,$30,$21,$01,$23,$00,$00 + byte $21,$01,$1c,$00,$00,$01,$63,$6f,$6d,$70,$75,$74,$65,$72,$00,$01 + byte $49,$02,$72,$75,$6e,$73,$00,$01,$74,$20,$01,$41,$00,$45,$00,$00 + byte $02,$64,$6f,$65,$73,$00,$01,$6a,$02,$72,$75,$6e,$00,$01,$6f,$02 + byte $64,$6f,$00,$01,$65,$21,$01,$50,$00,$00,$21,$01,$5f,$00,$00,$21 + byte $01,$41,$00,$00,$21,$01,$58,$00,$00 + +' **************************************************** +' Replace the following lines with the dumped output +' **************************************************** + version long 2 + size long 6923 + vocab byte + byte $03,$49,$00,$00,$14,$02,$61,$6d,$00,$00,$53,$01,$68,$75,$6d,$61 + byte $6e,$00,$00,$6d,$20,$00,$05,$00,$0b,$00,$2e,$01,$70,$72,$6f,$67 + byte $72,$61,$6d,$00,$01,$59,$01,$50,$72,$6f,$70,$00,$02,$0f,$20,$00 + byte $1b,$00,$26,$00,$45,$02,$63,$61,$6e,$00,$00,$00,$06,$73,$6c,$65 + byte $65,$70,$00,$03,$1d,$20,$00,$35,$00,$3c,$00,$00,$02,$61,$72,$65 + byte $00,$03,$26,$21,$00,$4c,$00,$5e,$02,$69,$73,$00,$03,$30,$21,$00 + byte $58,$00,$00,$01,$6d,$61,$6d,$6d,$61,$6c,$00,$03,$44,$20,$00,$58 + byte $00,$63,$00,$7e,$04,$6c,$69,$76,$69,$6e,$67,$00,$03,$62,$20,$00 + byte $58,$00,$74,$01,$05,$20,$00,$35,$00,$3c,$01,$15,$04,$74,$68,$69 + byte $6e,$6b,$00,$04,$07,$20,$00,$35,$01,$0c,$01,$25,$01,$62,$65,$69 + byte $6e,$67,$00,$31,$53,$22,$00,$58,$00,$74,$01,$1c,$01,$38,$01,$68 + byte $75,$6d,$61,$6e,$73,$00,$35,$2f,$21,$01,$2e,$01,$46,$02,$6c,$65 + byte $61,$72,$6e,$00,$2f,$4e,$20,$00,$35,$01,$3d,$00,$00,$06,$73,$6f + byte $66,$74,$77,$61,$72,$65,$00,$04,$13,$20,$00,$58,$01,$4d,$01,$6f + byte $01,$61,$70,$70,$6c,$69,$63,$61,$74,$69,$6f,$6e,$00,$04,$1a,$20 + byte $00,$58,$01,$60,$02,$02,$01,$70,$72,$6f,$67,$72,$61,$6d,$73,$00 + byte $14,$62,$21,$01,$76,$00,$00,$01,$70,$72,$6f,$70,$00,$04,$21,$21 + byte $02,$07,$02,$21,$01,$70,$72,$6f,$70,$65,$6c,$6c,$65,$72,$00,$05 + byte $7a,$21,$02,$14,$02,$33,$01,$50,$72,$6f,$70,$65,$6c,$6c,$65,$72 + byte $00,$06,$6a,$21,$02,$26,$02,$40,$02,$72,$75,$6e,$73,$00,$07,$73 + byte $20,$02,$38,$01,$4d,$02,$53,$06,$68,$61,$72,$64,$77,$61,$72,$65 + byte $00,$08,$01,$20,$00,$58,$02,$47,$02,$6d,$02,$68,$61,$73,$00,$08 + byte $33,$01,$63,$6f,$75,$6e,$74,$65,$72,$73,$00,$08,$45,$20,$02,$5a + byte $02,$61,$02,$7c,$02,$75,$73,$65,$73,$00,$08,$63,$20,$02,$74,$02 + byte $61,$03,$0b,$01,$63,$68,$69,$70,$00,$08,$73,$20,$00,$58,$03,$03 + byte $00,$00,$04,$64,$6f,$72,$6d,$61,$6e,$74,$00,$35,$74,$22,$00,$58 + byte $01,$1c,$03,$12,$00,$00,$21,$00,$05,$03,$2b,$21,$00,$58,$00,$00 + byte $21,$00,$05,$03,$35,$21,$00,$4c,$00,$00,$01,$61,$6e,$69,$6d,$61 + byte $6c,$00,$09,$39,$20,$00,$58,$03,$3a,$03,$4b,$20,$00,$58,$00,$74 + byte $00,$00,$04,$6d,$65,$74,$61,$62,$6f,$6c,$69,$7a,$69,$6e,$67,$00 + byte $2b,$5e,$20,$00,$58,$03,$52,$03,$76,$04,$62,$72,$65,$61,$74,$68 + byte $69,$6e,$67,$00,$30,$2e,$20,$00,$58,$03,$69,$00,$00,$04,$72,$65 + byte $61,$73,$6f,$6e,$00,$2b,$52,$20,$00,$58,$03,$7d,$04,$0e,$21,$03 + byte $7d,$00,$00,$20,$00,$58,$00,$1b,$00,$00,$20,$00,$58,$00,$1b,$00 + byte $00,$21,$02,$14,$04,$26,$21,$02,$26,$04,$2b,$21,$00,$26,$04,$37 + byte $01,$68,$75,$62,$00,$09,$40,$20,$02,$5a,$04,$30,$04,$48,$06,$6d + byte $65,$6d,$6f,$72,$79,$00,$09,$52,$20,$02,$5a,$04,$3e,$04,$62,$01 + byte $6d,$69,$63,$72,$6f,$63,$6f,$6e,$74,$72,$6f,$6c,$6c,$65,$72,$00 + byte $09,$79,$20,$00,$58,$04,$4f,$04,$71,$06,$50,$41,$53,$4d,$00,$0a + byte $0c,$20,$02,$38,$04,$69,$05,$07,$01,$69,$6e,$74,$65,$72,$70,$72 + byte $65,$74,$65,$72,$00,$0a,$57,$20,$02,$5a,$04,$78,$05,$15,$06,$49 + byte $2f,$4f,$00,$0b,$1a,$20,$02,$5a,$05,$0e,$05,$1c,$20,$00,$35,$00 + byte $3c,$05,$2b,$04,$66,$61,$73,$74,$00,$00,$00,$22,$00,$58,$05,$23 + byte $03,$03,$05,$45,$04,$65,$69,$67,$68,$74,$00,$2e,$12,$01,$63,$6f + byte $67,$73,$00,$0c,$53,$22,$02,$5a,$05,$34,$05,$3d,$05,$71,$02,$69 + byte $6e,$74,$65,$72,$70,$72,$65,$74,$73,$00,$00,$00,$06,$53,$70,$69 + byte $6e,$00,$0b,$21,$01,$62,$79,$74,$65,$63,$6f,$64,$65,$73,$00,$10 + byte $53,$22,$05,$4e,$05,$5c,$05,$64,$00,$00,$21,$02,$07,$05,$7f,$21 + byte $02,$26,$06,$04,$21,$00,$26,$06,$09,$20,$02,$38,$05,$23,$06,$10 + byte $20,$02,$38,$05,$5c,$06,$17,$20,$02,$5a,$05,$3d,$06,$25,$06,$52 + byte $41,$4d,$00,$0c,$58,$20,$02,$5a,$06,$1e,$06,$35,$01,$6c,$6f,$63 + byte $6b,$73,$00,$0c,$67,$20,$02,$5a,$06,$2c,$06,$44,$01,$70,$69,$6e + byte $73,$00,$0c,$7a,$20,$02,$5a,$06,$3c,$06,$52,$06,$52,$4f,$4d,$00 + byte $0d,$1d,$20,$02,$5a,$06,$4b,$06,$61,$04,$67,$6f,$6f,$64,$00,$2c + byte $0b,$22,$00,$58,$06,$59,$03,$03,$00,$00,$21,$02,$07,$06,$6f,$21 + byte $02,$14,$06,$74,$21,$00,$26,$07,$05,$04,$70,$6f,$77,$65,$72,$66 + byte $75,$6c,$00,$0d,$2e,$20,$00,$58,$06,$79,$07,$16,$01,$50,$38,$58 + byte $33,$32,$41,$00,$0d,$3f,$20,$00,$58,$07,$0c,$07,$1d,$20,$00,$58 + byte $05,$23,$07,$3a,$02,$67,$65,$6e,$65,$72,$61,$74,$65,$73,$00,$0d + byte $50,$04,$76,$69,$64,$65,$6f,$00,$0d,$60,$20,$07,$24,$07,$31,$07 + byte $4a,$04,$61,$75,$64,$69,$6f,$00,$0e,$03,$20,$07,$24,$07,$41,$07 + byte $5e,$04,$76,$65,$72,$73,$61,$74,$69,$6c,$65,$00,$0e,$16,$20,$00 + byte $58,$07,$51,$07,$65,$20,$05,$4e,$05,$5c,$00,$00,$02,$72,$75,$6e + byte $00,$0e,$1d,$21,$07,$6c,$00,$00,$04,$73,$74,$75,$66,$66,$00,$0e + byte $2f,$20,$00,$58,$07,$78,$08,$22,$04,$65,$6c,$65,$63,$74,$72,$69 + byte $63,$61,$6c,$00,$00,$00,$01,$63,$69,$72,$63,$75,$69,$74,$73,$00 + byte $00,$00,$22,$00,$58,$08,$08,$08,$16,$00,$00,$02,$68,$61,$76,$65 + byte $00,$0e,$36,$21,$08,$2b,$00,$00,$01,$72,$65,$67,$69,$73,$74,$65 + byte $72,$73,$00,$0e,$3b,$20,$00,$4c,$08,$38,$08,$57,$01,$63,$6f,$75 + byte $6e,$74,$65,$72,$00,$2b,$65,$21,$08,$4c,$00,$00,$02,$75,$73,$65 + byte $00,$0e,$42,$21,$08,$5c,$00,$00,$06,$73,$69,$6c,$69,$63,$6f,$6e + byte $00,$0e,$52,$20,$00,$58,$08,$68,$09,$05,$01,$63,$69,$72,$63,$75 + byte $69,$74,$00,$0f,$07,$20,$00,$58,$08,$7a,$09,$0c,$22,$00,$58,$08 + byte $08,$08,$7a,$09,$23,$04,$69,$6e,$74,$65,$67,$72,$61,$74,$65,$64 + byte $00,$31,$6d,$22,$00,$58,$09,$15,$08,$7a,$09,$32,$01,$49,$43,$00 + byte $31,$74,$20,$00,$58,$09,$2c,$00,$00,$20,$00,$58,$00,$74,$00,$00 + byte $20,$02,$5a,$06,$1e,$00,$00,$06,$73,$74,$6f,$72,$61,$67,$65,$00 + byte $14,$56,$20,$00,$58,$09,$47,$09,$63,$02,$73,$74,$6f,$72,$65,$73 + byte $00,$14,$5d,$20,$09,$59,$01,$76,$09,$72,$06,$64,$61,$74,$61,$00 + byte $12,$13,$20,$09,$59,$09,$6a,$00,$00,$20,$00,$58,$03,$03,$00,$00 + byte $06,$61,$73,$73,$65,$6d,$62,$6c,$79,$00,$14,$6e,$20,$00,$58,$0a + byte $00,$0a,$1f,$01,$6c,$61,$6e,$67,$75,$61,$67,$65,$00,$10,$3d,$20 + byte $00,$58,$0a,$13,$0a,$33,$04,$61,$73,$73,$65,$6d,$62,$6c,$65,$64 + byte $00,$14,$75,$20,$00,$58,$0a,$26,$0a,$3a,$20,$00,$58,$05,$23,$0a + byte $41,$22,$00,$58,$0a,$00,$0a,$13,$0a,$52,$06,$70,$61,$73,$6d,$00 + byte $31,$5c,$21,$0a,$4a,$00,$00,$20,$00,$58,$00,$1b,$0a,$7a,$02,$65 + byte $78,$65,$63,$75,$74,$65,$73,$00,$00,$00,$01,$69,$6e,$73,$74,$72 + byte $75,$63,$74,$69,$6f,$6e,$73,$00,$14,$7c,$20,$0a,$5e,$0a,$6a,$0b + byte $0c,$01,$6f,$70,$63,$6f,$64,$65,$73,$00,$15,$17,$20,$0a,$5e,$0b + byte $01,$0b,$13,$20,$00,$58,$01,$4d,$00,$00,$20,$02,$74,$06,$3c,$00 + byte $00,$20,$00,$58,$0a,$13,$0b,$33,$01,$6f,$62,$6a,$65,$63,$74,$73 + byte $00,$00,$00,$20,$02,$5a,$0b,$28,$0b,$3a,$20,$02,$74,$05,$64,$0b + byte $4d,$04,$63,$6f,$6d,$70,$69,$6c,$65,$64,$00,$11,$02,$20,$00,$58 + byte $0b,$41,$0b,$5d,$01,$73,$74,$61,$63,$6b,$00,$11,$09,$20,$02,$74 + byte $0b,$54,$0b,$6f,$04,$63,$6f,$6d,$70,$61,$63,$74,$00,$11,$2f,$20 + byte $00,$58,$0b,$64,$0c,$01,$01,$6d,$65,$74,$68,$6f,$64,$73,$00,$11 + byte $40,$20,$02,$74,$0b,$76,$0c,$08,$20,$02,$5a,$0b,$76,$0c,$1c,$01 + byte $76,$61,$72,$69,$61,$62,$6c,$65,$73,$00,$11,$4e,$20,$02,$5a,$0c + byte $0f,$0c,$23,$20,$02,$5a,$09,$6a,$0c,$32,$06,$73,$70,$69,$6e,$00 + byte $12,$1a,$21,$0c,$2a,$0c,$43,$01,$63,$6f,$6d,$70,$75,$74,$65,$72 + byte $00,$12,$55,$22,$00,$58,$0c,$37,$0a,$13,$00,$00,$01,$63,$6f,$67 + byte $00,$15,$2b,$21,$0c,$4c,$00,$00,$20,$00,$58,$04,$3e,$00,$00,$01 + byte $6c,$6f,$63,$6b,$00,$15,$4d,$21,$0c,$5f,$00,$00,$01,$63,$6f,$6e + byte $6e,$65,$63,$74,$6f,$72,$73,$00,$00,$00,$20,$00,$4c,$0c,$6c,$0d + byte $0a,$01,$70,$6c,$75,$67,$73,$00,$2c,$58,$20,$00,$4c,$0d,$01,$0d + byte $18,$01,$70,$69,$6e,$00,$35,$5c,$21,$0d,$11,$00,$00,$20,$00,$58 + byte $04,$3e,$00,$00,$04,$73,$74,$72,$6f,$6e,$67,$00,$00,$00,$20,$00 + byte $58,$0d,$24,$00,$00,$01,$70,$38,$78,$33,$32,$61,$00,$10,$02,$21 + byte $0d,$35,$00,$00,$02,$67,$65,$6e,$65,$72,$61,$74,$65,$00,$10,$0e + byte $21,$0d,$44,$00,$00,$04,$76,$69,$73,$69,$62,$6c,$65,$00,$21,$51 + byte $20,$00,$58,$0d,$55,$0d,$71,$01,$73,$69,$67,$6e,$61,$6c,$00,$00 + byte $00,$22,$00,$58,$08,$08,$0d,$67,$00,$00,$06,$73,$6f,$75,$6e,$64 + byte $00,$10,$13,$20,$00,$58,$0d,$7a,$00,$00,$04,$66,$6c,$65,$78,$69 + byte $62,$6c,$65,$00,$10,$2c,$20,$00,$58,$0e,$0a,$00,$00,$21,$02,$38 + byte $00,$00,$04,$73,$6f,$6d,$65,$74,$68,$69,$6e,$67,$00,$16,$5d,$20 + byte $00,$58,$0e,$22,$00,$00,$21,$02,$5a,$00,$00,$20,$00,$4c,$04,$3e + byte $00,$00,$21,$02,$74,$00,$00,$01,$6d,$69,$6e,$65,$72,$61,$6c,$00 + byte $16,$71,$20,$00,$58,$0e,$47,$0e,$64,$01,$65,$6c,$65,$6d,$65,$6e + byte $74,$00,$00,$00,$20,$00,$58,$0e,$59,$0e,$76,$06,$53,$69,$6c,$69 + byte $63,$6f,$6e,$00,$2c,$06,$21,$0e,$6b,$00,$00,$04,$65,$6c,$65,$63 + byte $74,$72,$69,$63,$00,$17,$42,$20,$00,$58,$0e,$7b,$00,$00,$07,$61 + byte $00,$00,$00,$03,$79,$6f,$75,$00,$0f,$24,$06,$4c,$65,$72,$6e,$65 + byte $72,$00,$0f,$30,$20,$00,$4c,$0f,$1a,$0f,$2b,$21,$0f,$1a,$00,$00 + byte $20,$00,$58,$00,$1b,$0f,$37,$21,$0f,$13,$0f,$3c,$20,$00,$58,$01 + byte $60,$0f,$43,$20,$00,$58,$02,$26,$0f,$4a,$20,$00,$58,$07,$0c,$0f + byte $51,$22,$00,$58,$0c,$37,$00,$1b,$0f,$5a,$20,$00,$35,$01,$3d,$0f + byte $79,$04,$61,$72,$74,$69,$66,$69,$63,$69,$61,$6c,$00,$00,$00,$01 + byte $65,$6e,$74,$69,$74,$79,$00,$00,$00,$22,$00,$58,$0f,$61,$0f,$6f + byte $00,$00,$20,$00,$58,$02,$14,$10,$09,$21,$07,$0c,$00,$00,$21,$07 + byte $24,$00,$00,$20,$00,$58,$07,$41,$10,$25,$04,$61,$75,$64,$69,$62 + byte $6c,$65,$00,$2c,$76,$20,$00,$58,$10,$1a,$00,$00,$20,$00,$58,$07 + byte $51,$00,$00,$06,$73,$79,$6e,$74,$61,$78,$00,$16,$00,$20,$02,$5a + byte $10,$33,$00,$00,$04,$69,$6e,$74,$65,$72,$70,$72,$65,$74,$65,$64 + byte $00,$00,$00,$20,$00,$4c,$10,$44,$10,$5a,$20,$00,$4c,$0b,$64,$10 + byte $69,$06,$63,$6f,$64,$65,$00,$16,$1b,$20,$00,$4c,$10,$61,$10,$79 + byte $01,$63,$6f,$64,$65,$73,$00,$00,$00,$22,$00,$4c,$0b,$64,$10,$70 + byte $00,$00,$20,$00,$58,$0a,$26,$00,$00,$20,$00,$58,$09,$47,$11,$10 + byte $20,$00,$58,$04,$3e,$11,$1f,$01,$46,$49,$46,$4f,$00,$2b,$71,$20 + byte $00,$58,$11,$17,$00,$00,$04,$73,$6d,$61,$6c,$6c,$00,$16,$2c,$20 + byte $00,$58,$11,$26,$00,$00,$01,$6d,$65,$74,$68,$6f,$64,$00,$16,$3e + byte $21,$11,$36,$00,$00,$02,$73,$74,$6f,$72,$65,$00,$16,$4a,$20,$11 + byte $45,$09,$6a,$11,$6a,$04,$73,$79,$6d,$62,$6f,$6c,$69,$63,$00,$00 + byte $00,$01,$6e,$61,$6d,$65,$73,$00,$31,$11,$22,$00,$4c,$11,$55,$11 + byte $61,$11,$7f,$01,$76,$61,$72,$69,$61,$62,$6c,$65,$00,$35,$2a,$21 + byte $11,$73,$00,$00,$06,$69,$6e,$66,$6f,$72,$6d,$61,$74,$69,$6f,$6e + byte $00,$2c,$12,$20,$00,$58,$12,$04,$00,$00,$21,$05,$5c,$00,$00,$02 + byte $65,$71,$75,$61,$6c,$73,$00,$12,$32,$02,$65,$71,$75,$61,$6c,$00 + byte $12,$37,$21,$12,$29,$00,$00,$21,$12,$1f,$00,$00,$08,$77,$68,$6f + byte $00,$12,$4b,$08,$77,$68,$61,$74,$00,$12,$50,$21,$12,$43,$00,$00 + byte $21,$12,$3c,$00,$00,$20,$02,$38,$00,$1b,$12,$5c,$20,$00,$58,$02 + byte $47,$12,$6d,$01,$64,$65,$76,$69,$63,$65,$00,$00,$00,$22,$00,$58 + byte $08,$08,$12,$63,$12,$76,$20,$02,$38,$01,$76,$12,$7d,$20,$02,$38 + byte $01,$4d,$00,$00,$02,$64,$6f,$65,$73,$00,$13,$12,$02,$64,$6f,$00 + byte $13,$17,$21,$13,$0c,$00,$00,$21,$13,$04,$00,$00,$06,$50,$61,$72 + byte $61,$6c,$6c,$61,$78,$00,$13,$33,$01,$63,$6f,$6d,$70,$61,$6e,$79 + byte $00,$13,$66,$20,$00,$58,$13,$28,$13,$43,$02,$6d,$61,$6b,$65,$73 + byte $00,$13,$75,$20,$13,$3a,$02,$07,$13,$53,$02,$73,$65,$6c,$6c,$73 + byte $00,$14,$1f,$20,$13,$4a,$02,$26,$00,$00,$01,$62,$75,$73,$69,$6e + byte $65,$73,$73,$00,$00,$00,$20,$00,$58,$13,$5a,$00,$00,$02,$6d,$61 + byte $6b,$65,$00,$14,$24,$21,$13,$6d,$14,$03,$02,$62,$75,$69,$6c,$64 + byte $00,$14,$33,$21,$13,$7a,$14,$12,$02,$62,$75,$69,$6c,$64,$73,$00 + byte $14,$42,$21,$14,$08,$00,$00,$02,$73,$65,$6c,$6c,$00,$14,$51,$21 + byte $14,$17,$00,$00,$21,$13,$3a,$14,$29,$21,$13,$7a,$14,$2e,$21,$14 + byte $08,$00,$00,$21,$14,$08,$14,$38,$21,$13,$6d,$14,$3d,$21,$13,$3a + byte $00,$00,$21,$13,$7a,$14,$47,$21,$13,$6d,$14,$4c,$21,$13,$3a,$00 + byte $00,$21,$13,$4a,$00,$00,$20,$00,$58,$04,$3e,$00,$00,$21,$11,$45 + byte $00,$00,$20,$00,$4c,$0a,$6a,$14,$69,$21,$00,$1b,$00,$00,$20,$00 + byte $58,$0a,$13,$00,$00,$20,$00,$58,$0b,$41,$00,$00,$20,$00,$4c,$10 + byte $61,$15,$12,$01,$69,$6e,$73,$74,$72,$75,$63,$74,$69,$6f,$6e,$00 + byte $33,$1f,$21,$15,$03,$00,$00,$20,$00,$4c,$0a,$6a,$00,$00,$01,$70 + byte $72,$6f,$63,$65,$73,$73,$6f,$72,$00,$16,$56,$20,$00,$58,$15,$1e + byte $15,$3a,$01,$63,$6f,$72,$65,$00,$00,$00,$20,$00,$58,$15,$32,$15 + byte $41,$20,$00,$35,$00,$3c,$15,$48,$21,$05,$3d,$00,$00,$21,$06,$2c + byte $15,$5f,$01,$73,$65,$6d,$61,$70,$68,$6f,$72,$65,$00,$00,$00,$20 + byte $00,$58,$15,$52,$15,$70,$04,$61,$74,$6f,$6d,$69,$63,$00,$00,$00 + byte $20,$00,$58,$15,$66,$00,$00,$01,$72,$75,$6c,$65,$73,$00,$00,$00 + byte $20,$02,$5a,$15,$77,$16,$14,$04,$73,$74,$72,$75,$63,$74,$75,$72 + byte $65,$00,$17,$59,$20,$02,$5a,$16,$07,$00,$00,$20,$00,$58,$0a,$6a + byte $00,$00,$04,$6c,$69,$74,$74,$6c,$65,$00,$2e,$0b,$20,$00,$58,$16 + byte $22,$00,$00,$01,$72,$6f,$75,$74,$69,$6e,$65,$00,$17,$60,$20,$00 + byte $58,$16,$33,$16,$45,$21,$0b,$76,$00,$00,$21,$09,$59,$00,$00,$01 + byte $43,$50,$55,$00,$1a,$77,$20,$00,$58,$16,$4f,$00,$00,$20,$00,$58 + byte $07,$78,$00,$00,$01,$73,$75,$62,$73,$74,$61,$6e,$63,$65,$00,$2c + byte $34,$20,$00,$58,$16,$64,$17,$04,$01,$63,$68,$65,$6d,$69,$63,$61 + byte $6c,$00,$2c,$3b,$20,$00,$58,$16,$78,$17,$14,$04,$73,$6f,$6c,$69 + byte $64,$00,$2c,$42,$20,$00,$58,$17,$0b,$17,$1b,$22,$00,$58,$16,$78 + byte $16,$64,$17,$24,$22,$00,$58,$17,$0b,$16,$64,$17,$2d,$22,$00,$58 + byte $17,$0b,$16,$78,$00,$00,$04,$73,$68,$6f,$63,$6b,$69,$6e,$67,$00 + byte $2b,$6a,$20,$00,$58,$17,$36,$00,$00,$04,$6f,$72,$67,$61,$6e,$69 + byte $7a,$61,$74,$69,$6f,$6e,$00,$2c,$5f,$20,$00,$58,$17,$49,$00,$00 + byte $20,$00,$58,$00,$1b,$17,$67,$22,$00,$58,$11,$26,$00,$1b,$00,$00 + byte $06,$53,$75,$6e,$64,$61,$79,$00,$18,$01,$01,$64,$61,$79,$00,$18 + byte $20,$20,$00,$58,$17,$7a,$00,$00,$04,$74,$77,$65,$6e,$74,$79,$2d + byte $66,$6f,$75,$72,$00,$32,$5f,$01,$68,$6f,$75,$72,$73,$00,$00,$00 + byte $22,$00,$58,$18,$08,$18,$17,$18,$31,$01,$64,$61,$79,$73,$00,$2d + byte $3d,$21,$18,$29,$00,$00,$06,$4d,$6f,$6e,$64,$61,$79,$00,$18,$40 + byte $20,$00,$58,$17,$7a,$00,$00,$06,$54,$75,$65,$73,$64,$61,$79,$00 + byte $18,$52,$20,$00,$58,$17,$7a,$00,$00,$06,$57,$65,$64,$6e,$65,$73 + byte $64,$61,$79,$00,$18,$66,$20,$00,$58,$17,$7a,$00,$00,$06,$54,$68 + byte $75,$72,$73,$64,$61,$79,$00,$18,$79,$20,$00,$58,$17,$7a,$00,$00 + byte $06,$46,$72,$69,$64,$61,$79,$00,$19,$0a,$20,$00,$58,$17,$7a,$00 + byte $00,$06,$53,$61,$74,$75,$72,$64,$61,$79,$00,$19,$1d,$20,$00,$58 + byte $17,$7a,$00,$00,$06,$4a,$61,$6e,$75,$61,$72,$79,$00,$19,$38,$01 + byte $6d,$6f,$6e,$74,$68,$00,$19,$49,$20,$00,$58,$19,$2f,$00,$00,$01 + byte $6d,$6f,$6e,$74,$68,$73,$00,$2e,$4f,$21,$19,$3f,$00,$00,$06,$4d + byte $61,$72,$63,$68,$00,$19,$57,$20,$00,$58,$19,$2f,$00,$00,$06,$41 + byte $70,$72,$69,$6c,$00,$19,$67,$20,$00,$58,$19,$2f,$00,$00,$06,$4d + byte $61,$79,$00,$19,$75,$20,$00,$58,$19,$2f,$00,$00,$06,$4a,$75,$6e + byte $65,$00,$1a,$04,$20,$00,$58,$19,$2f,$00,$00,$06,$4a,$75,$6c,$79 + byte $00,$1a,$13,$20,$00,$58,$19,$2f,$00,$00,$06,$41,$75,$67,$75,$73 + byte $74,$00,$1a,$24,$20,$00,$58,$19,$2f,$00,$00,$06,$53,$65,$70,$74 + byte $65,$6d,$62,$65,$72,$00,$1a,$38,$20,$00,$58,$19,$2f,$00,$00,$06 + byte $4f,$63,$74,$6f,$62,$65,$72,$00,$1a,$4a,$20,$00,$58,$19,$2f,$00 + byte $00,$06,$4e,$6f,$76,$65,$6d,$62,$65,$72,$00,$1a,$5d,$20,$00,$58 + byte $19,$2f,$00,$00,$06,$44,$65,$63,$65,$6d,$62,$65,$72,$00,$1a,$70 + byte $20,$00,$58,$19,$2f,$00,$00,$20,$00,$58,$15,$1e,$00,$00,$06,$46 + byte $65,$62,$72,$75,$61,$72,$79,$00,$1b,$0a,$20,$00,$58,$19,$2f,$00 + byte $00,$06,$57,$69,$6e,$74,$65,$72,$00,$1b,$25,$01,$73,$65,$61,$73 + byte $6f,$6e,$00,$1b,$56,$20,$00,$58,$1b,$1b,$1b,$34,$04,$63,$6f,$6c + byte $64,$00,$1c,$0d,$20,$00,$58,$1b,$2c,$1b,$3b,$22,$00,$58,$1b,$2c + byte $1b,$1b,$00,$00,$01,$74,$69,$6d,$65,$00,$00,$00,$01,$70,$65,$72 + byte $69,$6f,$64,$00,$00,$00,$22,$00,$58,$1b,$44,$1b,$4c,$1b,$75,$04 + byte $6f,$6e,$65,$2d,$66,$6f,$75,$72,$74,$68,$00,$33,$24,$01,$79,$65 + byte $61,$72,$00,$2e,$23,$22,$00,$58,$1b,$5f,$1b,$6d,$00,$00,$01,$74 + byte $65,$6d,$70,$65,$72,$61,$74,$75,$72,$65,$00,$00,$00,$20,$00,$58 + byte $1b,$7e,$00,$00,$06,$53,$75,$6d,$6d,$65,$72,$00,$1c,$1e,$20,$00 + byte $58,$1b,$1b,$1c,$2c,$04,$68,$6f,$74,$00,$1c,$3c,$20,$00,$58,$1c + byte $25,$1c,$33,$22,$00,$58,$1c,$25,$1b,$1b,$00,$00,$20,$00,$58,$1b + byte $7e,$00,$00,$06,$53,$70,$72,$69,$6e,$67,$00,$1c,$4d,$20,$00,$58 + byte $1b,$1b,$1c,$5c,$04,$6e,$69,$63,$65,$00,$1c,$63,$20,$00,$58,$1c + byte $54,$00,$00,$20,$00,$58,$06,$59,$1c,$76,$04,$70,$6c,$65,$61,$73 + byte $69,$6e,$67,$00,$2c,$66,$20,$00,$58,$1c,$6a,$00,$00,$06,$41,$75 + byte $74,$75,$6d,$6e,$00,$1d,$07,$20,$00,$58,$1b,$1b,$1d,$0e,$20,$00 + byte $58,$1c,$54,$00,$00,$01,$63,$61,$74,$00,$1d,$1c,$20,$00,$58,$00 + byte $63,$1d,$23,$20,$00,$58,$03,$3a,$1d,$3a,$02,$73,$61,$79,$73,$00 + byte $00,$00,$04,$6d,$65,$6f,$77,$00,$1d,$5f,$20,$1d,$2a,$1d,$32,$1d + byte $41,$20,$00,$35,$00,$3c,$1d,$48,$22,$00,$58,$11,$26,$00,$63,$1d + byte $58,$01,$70,$65,$74,$00,$32,$26,$20,$00,$58,$1d,$51,$00,$00,$20 + byte $00,$58,$0d,$7a,$00,$00,$07,$61,$6e,$00,$00,$00,$01,$64,$6f,$67 + byte $00,$1d,$73,$20,$00,$58,$03,$3a,$1d,$7a,$20,$00,$58,$00,$63,$1e + byte $09,$04,$77,$6f,$6f,$66,$00,$1e,$31,$20,$1d,$2a,$1e,$01,$1e,$10 + byte $20,$00,$35,$00,$3c,$1e,$17,$20,$00,$58,$1d,$51,$1e,$28,$01,$74 + byte $72,$69,$63,$6b,$73,$00,$00,$00,$22,$00,$35,$01,$3d,$1e,$1e,$00 + byte $00,$20,$00,$58,$0d,$7a,$00,$00,$01,$70,$6c,$61,$6e,$74,$00,$1e + byte $41,$20,$00,$58,$00,$74,$1e,$51,$04,$67,$72,$65,$65,$6e,$00,$1e + byte $61,$20,$00,$58,$1e,$48,$00,$00,$01,$63,$6f,$6c,$6f,$72,$00,$1e + byte $71,$20,$00,$58,$1e,$58,$00,$00,$04,$6c,$69,$67,$68,$74,$00,$21 + byte $09,$20,$00,$58,$1e,$68,$1e,$78,$22,$00,$58,$0d,$55,$1e,$68,$00 + byte $00,$01,$74,$72,$65,$65,$00,$1f,$09,$20,$00,$58,$1e,$38,$1f,$1a + byte $01,$6c,$65,$61,$76,$65,$73,$00,$00,$00,$20,$02,$5a,$1f,$10,$1f + byte $2a,$01,$72,$6f,$6f,$74,$73,$00,$00,$00,$20,$02,$5a,$1f,$21,$00 + byte $00,$07,$74,$68,$65,$00,$00,$00,$01,$73,$6b,$79,$00,$1f,$47,$04 + byte $62,$6c,$75,$65,$00,$1f,$5f,$20,$00,$58,$1f,$3f,$1f,$58,$04,$63 + byte $6c,$6f,$75,$64,$73,$00,$1f,$6f,$20,$02,$5a,$1f,$4e,$00,$00,$20 + byte $00,$58,$1e,$58,$00,$00,$04,$77,$68,$69,$74,$65,$00,$20,$47,$20 + byte $00,$4c,$1f,$66,$1f,$7d,$04,$77,$65,$74,$00,$20,$7d,$20,$00,$4c + byte $1f,$76,$20,$0d,$01,$63,$6c,$6f,$75,$64,$00,$21,$04,$21,$20,$04 + byte $20,$26,$02,$63,$6f,$6e,$74,$61,$69,$6e,$00,$00,$00,$04,$77,$61 + byte $74,$65,$72,$00,$25,$33,$20,$20,$12,$20,$1d,$20,$40,$02,$70,$72 + byte $6f,$64,$75,$63,$65,$00,$00,$00,$06,$72,$61,$69,$6e,$00,$30,$03 + byte $20,$20,$2d,$20,$38,$00,$00,$20,$00,$58,$1e,$58,$20,$6b,$02,$63 + byte $6f,$6e,$74,$61,$69,$6e,$73,$00,$00,$00,$04,$61,$6c,$6c,$00,$00 + byte $00,$01,$63,$6f,$6c,$6f,$72,$73,$00,$00,$00,$22,$20,$4e,$20,$5a + byte $20,$61,$00,$00,$04,$6d,$6f,$69,$73,$74,$00,$2d,$04,$20,$00,$58 + byte $20,$74,$00,$00,$21,$1f,$4e,$00,$00,$20,$00,$58,$0d,$55,$21,$1d + byte $06,$72,$61,$64,$69,$61,$74,$69,$6f,$6e,$00,$00,$00,$20,$00,$58 + byte $21,$10,$21,$24,$22,$00,$58,$0d,$55,$21,$10,$21,$40,$04,$65,$6c + byte $65,$63,$74,$72,$6f,$6d,$61,$67,$6e,$65,$74,$69,$63,$00,$00,$00 + byte $22,$00,$58,$21,$2d,$21,$10,$00,$00,$04,$73,$65,$65,$6e,$00,$2b + byte $2b,$20,$00,$58,$21,$49,$00,$00,$01,$63,$6f,$77,$00,$21,$5f,$20 + byte $00,$58,$00,$63,$21,$6d,$04,$6d,$6f,$6f,$00,$22,$41,$20,$1d,$2a + byte $21,$66,$21,$74,$20,$00,$35,$00,$3c,$22,$0c,$02,$65,$61,$74,$73 + byte $00,$22,$4f,$04,$67,$72,$61,$73,$73,$00,$22,$54,$20,$21,$7b,$22 + byte $03,$22,$1a,$04,$62,$69,$67,$00,$27,$27,$22,$00,$58,$22,$13,$00 + byte $63,$22,$2b,$01,$66,$61,$72,$6d,$00,$32,$3f,$22,$00,$58,$22,$23 + byte $03,$3a,$22,$3c,$01,$63,$6f,$77,$73,$00,$33,$03,$21,$22,$34,$00 + byte $00,$20,$00,$58,$0d,$7a,$00,$00,$02,$65,$61,$74,$00,$22,$6b,$21 + byte $22,$48,$00,$00,$20,$00,$58,$1e,$48,$22,$5b,$20,$00,$58,$1e,$38 + byte $22,$62,$22,$00,$58,$1e,$48,$1e,$38,$00,$00,$21,$21,$7b,$00,$00 + byte $06,$4d,$65,$72,$63,$75,$72,$79,$00,$23,$05,$01,$70,$6c,$61,$6e + byte $65,$74,$00,$23,$25,$20,$00,$58,$22,$7b,$23,$0c,$20,$00,$58,$1c + byte $25,$23,$13,$22,$00,$58,$1c,$25,$22,$7b,$00,$00,$01,$77,$6f,$72 + byte $6c,$64,$00,$23,$7c,$20,$00,$58,$23,$1c,$23,$36,$01,$73,$70,$68 + byte $65,$72,$65,$00,$24,$0c,$20,$00,$58,$23,$2c,$23,$3d,$22,$00,$58 + byte $22,$13,$23,$2c,$23,$5b,$04,$63,$65,$6c,$65,$73,$74,$69,$61,$6c + byte $00,$00,$00,$01,$62,$6f,$64,$79,$00,$00,$00,$22,$00,$58,$23,$46 + byte $23,$53,$23,$75,$02,$6f,$72,$62,$69,$74,$73,$00,$00,$00,$01,$73 + byte $75,$6e,$00,$35,$39,$20,$23,$64,$23,$6e,$00,$00,$20,$00,$58,$22 + byte $7b,$00,$00,$04,$72,$6f,$75,$6e,$64,$00,$24,$32,$20,$00,$58,$24 + byte $03,$24,$1d,$01,$6f,$62,$6a,$65,$63,$74,$00,$29,$4e,$22,$00,$58 + byte $24,$03,$24,$13,$00,$00,$04,$63,$69,$72,$63,$75,$6c,$61,$72,$00 + byte $2b,$24,$20,$00,$58,$24,$26,$00,$00,$06,$4d,$61,$72,$73,$00,$24 + byte $41,$20,$00,$58,$22,$7b,$24,$4f,$04,$72,$65,$64,$00,$24,$66,$20 + byte $00,$58,$24,$48,$24,$56,$20,$00,$58,$1b,$2c,$24,$5d,$22,$00,$58 + byte $24,$48,$22,$7b,$00,$00,$20,$00,$58,$1e,$58,$00,$00,$06,$45,$61 + byte $72,$74,$68,$00,$24,$76,$20,$00,$58,$22,$7b,$24,$7d,$20,$02,$5a + byte $20,$1d,$25,$0e,$01,$6f,$63,$65,$61,$6e,$73,$00,$26,$04,$20,$02 + byte $5a,$25,$04,$25,$1d,$06,$6c,$61,$6e,$64,$00,$26,$21,$20,$02,$5a + byte $25,$15,$25,$24,$20,$00,$58,$23,$1c,$00,$00,$04,$73,$6f,$66,$74 + byte $00,$00,$00,$20,$00,$58,$25,$2b,$25,$3a,$20,$00,$58,$1f,$76,$25 + byte $4b,$01,$6c,$69,$71,$75,$69,$64,$00,$2e,$04,$20,$00,$58,$25,$41 + byte $25,$59,$06,$48,$32,$4f,$00,$30,$35,$20,$00,$58,$25,$52,$25,$6c + byte $06,$68,$79,$64,$72,$6f,$67,$65,$6e,$00,$30,$3c,$20,$20,$4e,$25 + byte $60,$25,$7d,$06,$6f,$78,$79,$67,$65,$6e,$00,$30,$43,$20,$20,$4e + byte $25,$73,$00,$00,$20,$08,$2b,$20,$1d,$26,$14,$01,$6f,$63,$65,$61 + byte $6e,$00,$26,$36,$21,$26,$0b,$00,$00,$04,$68,$61,$72,$64,$00,$26 + byte $4b,$20,$00,$58,$26,$19,$26,$2f,$04,$64,$72,$79,$00,$00,$00,$20 + byte $00,$58,$26,$28,$00,$00,$20,$00,$58,$1f,$76,$26,$3d,$21,$25,$04 + byte $00,$00,$04,$72,$69,$67,$69,$64,$00,$26,$60,$20,$00,$58,$26,$42 + byte $00,$00,$04,$69,$6e,$66,$6c,$65,$78,$69,$62,$6c,$65,$00,$2b,$32 + byte $20,$00,$58,$26,$52,$00,$00,$06,$56,$65,$6e,$75,$73,$00,$26,$70 + byte $20,$00,$58,$22,$7b,$26,$77,$20,$00,$58,$1c,$25,$00,$00,$06,$4a + byte $75,$70,$69,$74,$65,$72,$00,$27,$09,$20,$00,$58,$22,$7b,$27,$10 + byte $20,$00,$58,$1b,$2c,$27,$17,$20,$00,$58,$22,$13,$00,$00,$04,$6c + byte $61,$72,$67,$65,$00,$27,$2e,$20,$00,$58,$27,$1e,$00,$00,$20,$00 + byte $58,$22,$13,$00,$00,$06,$53,$61,$74,$75,$72,$6e,$00,$27,$3f,$20 + byte $00,$58,$22,$7b,$27,$46,$20,$00,$58,$1b,$2c,$27,$63,$04,$62,$65 + byte $61,$75,$74,$69,$66,$75,$6c,$00,$00,$00,$01,$72,$69,$6e,$67,$73 + byte $00,$2f,$61,$22,$02,$5a,$27,$4d,$27,$5a,$00,$00,$06,$4e,$65,$70 + byte $74,$75,$6e,$65,$00,$27,$77,$20,$00,$58,$22,$7b,$27,$7e,$20,$00 + byte $58,$1b,$2c,$00,$00,$06,$55,$72,$61,$6e,$75,$73,$00,$28,$0f,$20 + byte $00,$58,$22,$7b,$28,$16,$20,$00,$58,$1b,$2c,$00,$00,$06,$50,$6c + byte $75,$74,$6f,$00,$28,$2d,$02,$77,$61,$73,$00,$00,$00,$20,$28,$26 + byte $22,$7b,$28,$34,$20,$00,$58,$1b,$2c,$28,$44,$04,$64,$77,$61,$72 + byte $66,$00,$00,$00,$22,$00,$58,$28,$3b,$22,$7b,$00,$00,$01,$69,$6e + byte $74,$65,$72,$72,$75,$70,$74,$00,$28,$61,$04,$62,$61,$64,$00,$29 + byte $47,$20,$00,$58,$28,$5a,$28,$77,$04,$69,$6e,$65,$66,$66,$69,$63 + byte $69,$65,$6e,$74,$00,$00,$00,$20,$00,$58,$28,$68,$29,$09,$02,$64 + byte $69,$73,$72,$75,$70,$74,$00,$00,$00,$20,$00,$35,$28,$7e,$29,$1e + byte $04,$64,$69,$73,$72,$75,$70,$74,$69,$76,$65,$00,$00,$00,$20,$00 + byte $58,$29,$10,$29,$34,$04,$63,$6f,$6d,$70,$6c,$69,$63,$61,$74,$65 + byte $64,$00,$00,$00,$20,$00,$58,$29,$25,$00,$00,$04,$68,$6f,$72,$72 + byte $69,$62,$6c,$65,$00,$2d,$0b,$20,$00,$58,$29,$3b,$00,$00,$20,$02 + byte $5a,$0b,$76,$00,$00,$06,$45,$6e,$67,$6c,$69,$73,$68,$00,$29,$60 + byte $20,$00,$58,$0a,$13,$29,$67,$22,$00,$58,$00,$0b,$0a,$13,$00,$00 + byte $01,$6e,$6f,$75,$6e,$00,$00,$00,$02,$73,$61,$79,$00,$00,$00,$01 + byte $53,$75,$6e,$00,$2a,$0e,$01,$73,$74,$61,$72,$00,$2a,$30,$20,$00 + byte $58,$2a,$06,$2a,$15,$20,$00,$58,$1c,$25,$2a,$27,$04,$6e,$65,$61 + byte $72,$65,$73,$74,$00,$30,$72,$22,$00,$58,$2a,$1c,$2a,$06,$00,$00 + byte $20,$00,$58,$1c,$25,$2a,$41,$04,$62,$72,$69,$67,$68,$74,$00,$00 + byte $00,$20,$00,$58,$2a,$37,$2a,$51,$02,$65,$6d,$69,$74,$73,$00,$00 + byte $00,$20,$2a,$48,$1e,$68,$2a,$60,$04,$76,$65,$72,$79,$00,$00,$00 + byte $22,$00,$58,$2a,$58,$1c,$25,$2a,$69,$22,$00,$58,$23,$46,$23,$53 + byte $00,$00,$01,$73,$79,$6d,$62,$6f,$6c,$73,$00,$2b,$05,$01,$74,$61 + byte $67,$73,$00,$2b,$16,$20,$00,$4c,$2a,$7d,$00,$00,$01,$6c,$61,$62 + byte $65,$6c,$73,$00,$2b,$1d,$20,$00,$4c,$2b,$0c,$00,$00,$20,$00,$4c + byte $2a,$7d,$00,$00,$20,$00,$58,$24,$03,$00,$00,$20,$00,$58,$0d,$55 + byte $00,$00,$20,$00,$58,$26,$19,$00,$00,$02,$6c,$69,$6b,$65,$00,$00 + byte $00,$02,$6d,$61,$64,$65,$00,$00,$00,$01,$61,$77,$61,$72,$65,$00 + byte $00,$00,$20,$00,$58,$01,$0c,$2b,$59,$21,$01,$0c,$00,$00,$20,$00 + byte $58,$00,$74,$00,$00,$21,$02,$61,$00,$00,$20,$00,$58,$08,$08,$00 + byte $00,$20,$00,$58,$09,$47,$2b,$78,$20,$00,$58,$04,$3e,$2b,$7f,$20 + byte $00,$58,$0b,$54,$00,$00,$21,$08,$68,$00,$00,$20,$00,$58,$1c,$54 + byte $00,$00,$20,$00,$58,$09,$6a,$2c,$26,$06,$6b,$6e,$6f,$77,$6c,$65 + byte $64,$67,$65,$00,$2c,$2d,$20,$00,$58,$2c,$19,$00,$00,$20,$00,$58 + byte $12,$04,$00,$00,$20,$00,$58,$07,$78,$00,$00,$20,$00,$58,$16,$64 + byte $00,$00,$20,$00,$58,$26,$42,$2c,$51,$04,$66,$69,$72,$6d,$00,$30 + byte $4a,$20,$00,$58,$2c,$49,$00,$00,$20,$00,$4c,$0c,$6c,$00,$00,$20 + byte $00,$58,$16,$07,$00,$00,$20,$00,$58,$1c,$54,$00,$00,$04,$68,$65 + byte $61,$72,$64,$00,$2c,$7d,$20,$00,$58,$2c,$6d,$00,$00,$20,$00,$58 + byte $10,$1a,$00,$00,$20,$00,$58,$1f,$76,$00,$00,$20,$00,$58,$28,$5a + byte $00,$00,$04,$73,$65,$76,$65,$6e,$00,$2d,$25,$01,$6e,$75,$6d,$62 + byte $65,$72,$00,$00,$00,$20,$00,$58,$2d,$1b,$00,$00,$01,$77,$65,$65 + byte $6b,$00,$2d,$34,$22,$00,$58,$2d,$12,$18,$29,$00,$00,$21,$17,$7a + byte $00,$00,$04,$6f,$6e,$65,$00,$2d,$49,$20,$00,$58,$2d,$1b,$2d,$62 + byte $04,$73,$69,$6e,$67,$6c,$65,$00,$00,$00,$01,$69,$74,$65,$6d,$00 + byte $2d,$74,$22,$00,$58,$2d,$50,$2d,$5a,$00,$00,$01,$74,$68,$69,$6e + byte $67,$00,$00,$00,$20,$00,$58,$2d,$6b,$00,$00,$04,$66,$6c,$75,$69 + byte $64,$00,$00,$00,$20,$00,$58,$2d,$7b,$00,$00,$20,$00,$58,$11,$26 + byte $00,$00,$20,$00,$58,$2d,$1b,$00,$00,$04,$74,$77,$65,$6c,$76,$65 + byte $00,$2e,$48,$22,$02,$5a,$2e,$19,$19,$3f,$2e,$3f,$04,$66,$6f,$75 + byte $72,$00,$2e,$54,$01,$73,$65,$61,$73,$6f,$6e,$73,$00,$00,$00,$22 + byte $02,$5a,$2e,$2c,$2e,$34,$00,$00,$20,$00,$58,$2d,$1b,$00,$00,$21 + byte $19,$2f,$00,$00,$20,$00,$58,$2d,$1b,$00,$00,$04,$74,$77,$6f,$00 + byte $2e,$62,$20,$00,$58,$2d,$1b,$00,$00,$04,$74,$68,$72,$65,$65,$00 + byte $2e,$72,$20,$00,$58,$2d,$1b,$00,$00,$04,$66,$69,$76,$65,$00,$2f + byte $01,$20,$00,$58,$2d,$1b,$00,$00,$04,$73,$69,$78,$00,$2f,$0f,$20 + byte $00,$58,$2d,$1b,$00,$00,$04,$6e,$69,$6e,$65,$00,$2f,$1e,$20,$00 + byte $58,$2d,$1b,$00,$00,$04,$74,$65,$6e,$00,$2f,$2c,$20,$00,$58,$2d + byte $1b,$00,$00,$04,$65,$6c,$65,$76,$65,$6e,$00,$2f,$3d,$20,$00,$58 + byte $2d,$1b,$00,$00,$02,$6c,$65,$61,$72,$6e,$73,$00,$35,$34,$21,$2f + byte $44,$00,$00,$01,$73,$74,$72,$75,$63,$74,$75,$72,$65,$73,$00,$00 + byte $00,$22,$00,$4c,$24,$26,$2f,$53,$00,$00,$06,$43,$00,$2f,$6f,$22 + byte $00,$58,$0c,$37,$0a,$13,$00,$00,$04,$66,$61,$6c,$6c,$69,$6e,$67 + byte $00,$30,$18,$22,$00,$58,$2f,$78,$20,$1d,$00,$00,$04,$64,$72,$6f + byte $70,$70,$69,$6e,$67,$00,$30,$51,$20,$00,$58,$30,$0c,$00,$00,$04 + byte $72,$65,$73,$70,$69,$72,$61,$74,$69,$6f,$6e,$00,$31,$00,$20,$00 + byte $58,$30,$1f,$00,$00,$20,$00,$58,$20,$1d,$00,$00,$20,$00,$58,$0e + byte $59,$00,$00,$20,$00,$58,$0e,$59,$00,$00,$20,$00,$58,$26,$42,$00 + byte $00,$20,$00,$58,$2f,$78,$00,$00,$01,$72,$6f,$63,$6b,$00,$30,$60 + byte $20,$00,$58,$0e,$47,$00,$00,$04,$63,$6c,$6f,$73,$65,$73,$74,$00 + byte $30,$79,$20,$00,$58,$30,$67,$00,$00,$20,$00,$58,$2a,$1c,$00,$00 + byte $20,$00,$58,$03,$69,$00,$00,$01,$74,$69,$74,$6c,$65,$73,$00,$36 + byte $04,$20,$00,$4c,$31,$07,$00,$00,$01,$68,$6f,$75,$72,$00,$31,$34 + byte $04,$73,$69,$78,$74,$79,$00,$31,$3d,$01,$6d,$69,$6e,$75,$74,$65 + byte $73,$00,$31,$4e,$22,$00,$58,$31,$20,$31,$29,$00,$00,$20,$00,$58 + byte $2d,$1b,$00,$00,$01,$6d,$69,$6e,$75,$74,$65,$00,$31,$7d,$21,$31 + byte $44,$00,$00,$22,$00,$58,$00,$74,$0f,$6f,$00,$00,$21,$04,$69,$00 + byte $00,$04,$63,$6f,$6d,$62,$69,$6e,$65,$64,$00,$35,$61,$20,$00,$58 + byte $31,$61,$00,$00,$22,$00,$58,$09,$15,$08,$7a,$00,$00,$21,$31,$29 + byte $32,$0d,$01,$73,$65,$63,$6f,$6e,$64,$73,$00,$00,$00,$22,$02,$5a + byte $31,$20,$32,$02,$00,$00,$04,$64,$6f,$6d,$65,$73,$74,$69,$63,$61 + byte $74,$65,$64,$00,$33,$11,$22,$00,$58,$32,$16,$03,$3a,$00,$00,$04 + byte $61,$67,$72,$69,$63,$75,$6c,$74,$75,$72,$61,$6c,$00,$00,$00,$22 + byte $00,$58,$32,$2f,$13,$5a,$32,$51,$01,$63,$72,$6f,$70,$73,$00,$00 + byte $00,$20,$02,$5a,$32,$48,$32,$58,$20,$02,$5a,$22,$34,$00,$00,$20 + byte $00,$58,$2d,$1b,$00,$00,$01,$61,$64,$6a,$65,$63,$74,$69,$76,$65 + byte $00,$00,$00,$01,$6e,$61,$6d,$65,$00,$00,$00,$01,$76,$65,$72,$62 + byte $00,$00,$00,$21,$21,$58,$00,$00,$04,$74,$61,$6d,$65,$64,$00,$33 + byte $18,$20,$00,$58,$33,$08,$00,$00,$20,$00,$58,$32,$16,$00,$00,$21 + byte $0a,$6a,$00,$00,$20,$00,$58,$2d,$1b,$00,$00,$01,$62,$79,$74,$65 + byte $00,$33,$3b,$01,$62,$69,$74,$73,$00,$00,$00,$22,$00,$58,$05,$34 + byte $33,$33,$33,$4d,$01,$62,$79,$74,$65,$73,$00,$34,$5c,$21,$33,$44 + byte $00,$00,$01,$6e,$79,$62,$62,$6c,$65,$00,$33,$5c,$22,$00,$58,$2e + byte $2c,$33,$33,$00,$00,$01,$77,$6f,$72,$64,$00,$33,$78,$04,$73,$69 + byte $78,$74,$65,$65,$6e,$00,$34,$18,$22,$00,$58,$33,$6d,$33,$33,$34 + byte $0a,$01,$77,$6f,$72,$64,$73,$00,$34,$57,$21,$34,$01,$34,$0f,$22 + byte $00,$58,$2e,$5b,$33,$44,$00,$00,$20,$00,$58,$2d,$1b,$00,$00,$01 + byte $6c,$6f,$6e,$67,$00,$34,$35,$04,$74,$68,$69,$72,$74,$79,$2d,$74 + byte $77,$6f,$00,$34,$50,$22,$00,$58,$34,$27,$33,$33,$34,$3e,$22,$00 + byte $58,$2e,$5b,$34,$01,$34,$47,$22,$00,$58,$2e,$2c,$33,$44,$00,$00 + byte $20,$00,$58,$2d,$1b,$00,$00,$21,$33,$65,$00,$00,$21,$33,$2b,$00 + byte $00,$01,$62,$69,$74,$00,$34,$7b,$04,$62,$69,$6e,$61,$72,$79,$00 + byte $00,$00,$01,$64,$69,$67,$69,$74,$00,$35,$12,$22,$00,$58,$34,$68 + byte $34,$72,$00,$00,$02,$72,$65,$70,$72,$65,$73,$65,$6e,$74,$73,$00 + byte $00,$00,$20,$35,$04,$2d,$1b,$35,$23,$01,$73,$79,$6d,$62,$6f,$6c + byte $00,$00,$00,$20,$00,$58,$35,$19,$00,$00,$21,$0c,$0f,$00,$00,$21 + byte $00,$0b,$00,$00,$21,$01,$3d,$00,$00,$20,$00,$58,$2a,$06,$00,$00 + byte $01,$6d,$6f,$6f,$6e,$00,$35,$48,$20,$23,$64,$22,$7b,$00,$00,$04 + byte $69,$6e,$74,$65,$72,$70,$72,$65,$74,$00,$00,$00,$21,$06,$3c,$00 + byte $00,$20,$00,$58,$09,$15,$00,$00,$04,$73,$6c,$65,$65,$70,$69,$6e + byte $67,$00,$35,$7b,$20,$00,$58,$35,$68,$00,$00,$22,$00,$58,$01,$1c + byte $03,$12,$00,$00,$20,$00,$4c,$11,$61,$00,$00 + byte 0[3077] + +VAR + long Memory[MEMORY_MASK + 1] + +PUB start(argc, argv) + c.start + main(argc, argv) + spinix.exit(0) + +' Generate a random number +PUB rand + return (?seed) & $7fffffff + +' Check if a key has been pressed +PUB kbhit + return ser.kbhit + +' Return the system tick count +PUB GetTicks + return cnt + +' Convert ASCII character to upper case +PUB toupper(val) + if (val => "a" and val =< "z") + val -= "a" - "A" + return val + +' Find the next character that does not match val +PUB SkipChar(str, val) + repeat while (byte[str]) + if (byte[str] <> val) + quit + str++ + return str + +' Find the next character that matches val +PUB FindChar(str, val) + repeat while (byte[str]) + if (byte[str] == val) + quit + str++ + return str + +' Convert the space-delimited string to a list of tokens +PUB tokenize(str, tokens) | ptr, num, len, val + num := 0 + len := strsize(str) + val := byte[str][len - 1] + if (len == 0) + return 0 + if (val < "A" or(val > "Z" and val < "a") or val > "z") + byte[str][len + 1] := 0 + byte[str][len] := byte[str][len - 1] + byte[str][len - 1] := " " + repeat while (byte[str]) + str := SkipChar(str, " ") + if (byte[str] == 0) + quit + long[tokens][num++] := str + ptr := FindChar(str, " ") + if (byte[ptr]) + byte[ptr++] := 0 + str := ptr + return num + +' Find the word "str" in the vocab table +PUB FindWord(str) | ptr + ptr := @vocab + repeat while (ptr < @vocab + size) + if ((byte[ptr] & TYPE_MASK) == TYPE_RELATED) + ptr += 7 + elseif ((byte[ptr] & TYPE_MASK) == TYPE_RELATE2) + ptr += 9 + elseif ((byte[ptr] & TYPE_MASK) == TYPE_EQUATE) + ptr += 5 + else + if ((byte[ptr] & DELETE_MASK) == 0) + if (c.strcmp(str, ptr + 1) == 0) + return ptr + ptr += strsize(ptr) + 3 + return 0 + +' Reset the vocab table +PUB ResetVocab | buf[25] + c.printf0(string("< Ary you sure you want to forget everything?\n")) + c.printf0(string("> ")) + c.gets(@buf) + if (toupper(byte[@buf][0]) == "Y") + size := 0 + changeflag := 1 + c.printf0(string("< Memory erased\n")) + else + c.printf0(string("< Reset aborted\n")) + +' Reset to a basic vocab table +PUB ResetBasicVocab | buf[25] + c.printf0(string("< Ary you sure you want to reset to a basic word list?\n")) + c.printf0(string("> ")) + c.gets(@buf) + if (toupper(byte[@buf][0]) == "Y") + size := basicsize + c.memcpy(@vocab, @basicvocab, size) + changeflag := 1 + c.printf0(string("< Memory reset to basic words\n")) + else + c.printf0(string("< Reset aborted\n")) + +' Dump out a Spin formatted version of the vocab table +PUB DumpHexWords | i, ptr + i := 0 + ptr := @vocab + c.printf1(string(" version long %d\n"), version) + c.printf1(string(" size long %d\n"), size) + c.printf0(string(" vocab byte\n")) + c.printf0(string(" byte ")) + repeat while (ptr < @vocab + size) + c.printf1(string("$%2.2x"), byte[ptr++]) + if (ptr == @vocab + size) + c.printf0(string("\n")) + elseif ((i++) // 16 == 15) + c.printf0(string("\n byte ")) + else + c.printf0(string(",")) + c.printf1(string(" byte 0[%d]\n"), BUF_SIZE - size) + +' Dump an ASCII formatted version of the vocab table +PUB DumpAsciiWords(name, num) | i, ptr1, index, index2, index3, ptr + i := 0 + ptr := @vocab + if (num > 1) + ptr := FindWord(name) + if (not ptr) + c.printf1(string("$ %s not found\n"), name) + return + repeat while (ptr < @vocab + size) + if ((byte[ptr] & TYPE_MASK) == TYPE_RELATED) + ptr += 7 + elseif ((byte[ptr] & TYPE_MASK) == TYPE_RELATE2) + ptr += 9 + elseif ((byte[ptr] & TYPE_MASK) == TYPE_EQUATE) + ptr += 5 + else + if ((byte[ptr] & DELETE_MASK) == 0) + c.printf2(string("$ %s - %s"), ptr + 1, types[byte[ptr][0] - 1]) + ptr1 := ptr + strsize(ptr) + 1 + repeat while (ptr1 < @vocab + size and(byte[ptr1][0] or byte[ptr1][1])) + index :=(byte[ptr1][0] << 7) | byte[ptr1][1] + ptr1 := @vocab + index + if ((byte[ptr1][0] & TYPE_MASK) == TYPE_RELATED) + if ((byte[ptr1][0] & DELETE_MASK) == 0) + index :=(byte[ptr1][1] << 7) | byte[ptr1][2] + index2 :=(byte[ptr1][3] << 7) | byte[ptr1][4] + c.printf2(string(" (%s %s)"), @vocab + index + 1, @vocab + index2 + 1) + ptr1 += 5 + elseif ((byte[ptr1][0] & TYPE_MASK) == TYPE_RELATE2) + if ((byte[ptr1][0] & DELETE_MASK) == 0) + index :=(byte[ptr1][1] << 7) | byte[ptr1][2] + index2 :=(byte[ptr1][3] << 7) | byte[ptr1][4] + index3 :=(byte[ptr1][5] << 7) | byte[ptr1][6] + c.printf3(string(" (%s %s %s)"), @vocab + index + 1, @vocab + index2 + 1, @vocab + index3 + 1) + ptr1 += 7 + elseif ((byte[ptr1][0] & TYPE_MASK) == TYPE_EQUATE) + if ((byte[ptr1][0] & DELETE_MASK) == 0) + index :=(byte[ptr1][1] << 7) | byte[ptr1][2] + c.printf1(string(" (=%s)"), @vocab + index + 1) + ptr1 += 3 + else + c.printf1(string("$ Invalid type = %2.2x\n"), byte[ptr1][0]) + c.printf0(string("\n")) + if (num > 1) + quit + ptr += strsize(ptr) + 3 + +' Add a word to the vocab table +PUB AddWord(attrib, str) | ptr, ptr1 + ptr := @vocab + size + ptr1 := ptr + if (size + strsize(str) + 4 > BUF_SIZE) + c.printf0(string("$ Out of memory\n")) + return + if (debugmode) + c.printf2(string("AddWord %s - %s\n"), str, types[attrib - 1]) + byte[ptr++] := attrib + c.strcpy(ptr, str) + ptr += strsize(ptr) + 1 + byte[ptr++] := 0 + byte[ptr] := 0 + size += strsize(str) + 4 + changeflag := 1 + +' Determine the attribute of an unknown word, and add it to the vocab table +PUB UnknownWord(tokens, num, i) | str, buf[25], ptr, caseflag + str := long[tokens][i] + caseflag := 0 + if (i == 0) + if (byte[str][0] => "A" and byte[str][0] =< "Z") + caseflag := 1 + byte[str][0] += "a" - "A" + if (FindWord(str)) + return 0 + byte[str][0] -= "a" - "A" + AddWord(TYPE_NAME, str) + return 0 + else + if (num > 2) + if (c.strcmp(long[tokens][1], string("equal")) == 0 or c.strcmp(long[tokens][1], string("equals")) == 0) + ptr := FindWord(long[tokens][2]) + if (ptr) + AddWord(byte[ptr], long[tokens][0]) + return 0 + else + AddWord(TYPE_ADJECT, long[tokens][0]) + return 0 + else + ptr := FindWord(long[tokens][i - 1]) + if (ptr == 0) + SomethingWrong(long[tokens][i - 1]) + else + if (byte[ptr][0] == TYPE_ARTICLE and(i == 1 or i == num - 1)) + AddWord(TYPE_NOUN, str) + return 0 + elseif (i < 3 and(byte[ptr][0] == TYPE_NOUN or byte[ptr][0] == TYPE_NAME)) + AddWord(TYPE_VERB, str) + return 0 + elseif ((i > 1 and byte[ptr][0] == TYPE_VERB) or i > 2) + if (byte[str][0] => "A" and byte[str][0] =< "Z") + AddWord(TYPE_NAME, str) + return 0 + elseif (i < num - 1 or byte[ptr][0] == TYPE_VERB) + AddWord(TYPE_ADJECT, str) + return 0 + if (i == num - 1 and num => 4) + ptr := FindWord(long[tokens][i - 2]) + if (ptr and byte[ptr][0] == TYPE_ARTICLE) + AddWord(TYPE_NOUN, str) + return 0 + else + AddWord(TYPE_ADJECT, str) + return 0 + c.printf1(string("< I don't know %s\n"), str) + repeat while (1) + c.printf0(string("< Is it a noun, verb, pronoun, adjective, Name, Article, query or ignore?\n")) + c.printf0(string("> ")) + c.gets(@buf) + if (caseflag and byte[@buf][0] <> "N" and c.strcmp(str, string("I"))) + byte[str][0] += "a" - "A" + if (byte[@buf][0] == "n") + AddWord(TYPE_NOUN, str) + quit + elseif (byte[@buf][0] == "v") + AddWord(TYPE_VERB, str) + quit + elseif (byte[@buf][0] == "a") + AddWord(TYPE_ADJECT, str) + quit + elseif (byte[@buf][0] == "p") + AddWord(TYPE_PRONOUN, str) + quit + elseif (byte[@buf][0] == "N") + AddWord(TYPE_NAME, str) + quit + elseif (byte[@buf][0] == "A") + AddWord(TYPE_ARTICLE, str) + quit + elseif (byte[@buf][0] == "q") + AddWord(TYPE_QUERY, str) + quit + elseif (byte[@buf][0] == "i") + return 1 + c.printf0(string("I don't understand\n")) + return 0 + +' Delete a word and all references to it from the vocab table +PUB DeleteWord(tokens, num) | ptr, buf[25], index, index2 + if (num < 2) + c.printf0(string("< I don't know what to forget\n")) + return + ptr := FindWord(long[tokens][1]) + if (ptr == 0) + c.printf1(string("< I don't know %s\n"), long[tokens][1]) + return + repeat while (1) + c.printf1(string("< Do you want me to forget %s?\n"), long[tokens][1]) + c.printf0(string("> ")) + c.gets(@buf) + if (byte[@buf][0] == "y") + index := ptr - @vocab + ' Delete the word + byte[ptr] |= DELETE_MASK + ' Delete the chain + repeat while (ptr := GetNextItem(ptr)) + byte[ptr] |= DELETE_MASK + ' Delete any reference to this word + ptr := @vocab + repeat while (ptr < @vocab + size) + if ((byte[ptr] & TYPE_MASK) == TYPE_EQUATE) + if ((byte[ptr] & DELETE_MASK) == 0) + index2 :=(byte[ptr][1] << 7) | byte[ptr][2] + if (index == index2) + byte[ptr] |= DELETE_MASK + ptr += 5 + elseif ((byte[ptr] & TYPE_MASK) == TYPE_RELATED) + if ((byte[ptr] & DELETE_MASK) == 0) + index2 :=(byte[ptr][1] << 7) | byte[ptr][2] + if (index == index2) + byte[ptr] |= DELETE_MASK + index2 :=(byte[ptr][3] << 7) | byte[ptr][4] + if (index == index2) + byte[ptr] |= DELETE_MASK + ptr += 7 + elseif ((byte[ptr] & TYPE_MASK) == TYPE_RELATE2) + if ((byte[ptr] & DELETE_MASK) == 0) + index2 :=(byte[ptr][1] << 7) | byte[ptr][2] + if (index == index2) + byte[ptr] |= DELETE_MASK + index2 :=(byte[ptr][3] << 7) | byte[ptr][4] + if (index == index2) + byte[ptr] |= DELETE_MASK + index2 :=(byte[ptr][5] << 7) | byte[ptr][6] + if (index == index2) + byte[ptr] |= DELETE_MASK + ptr += 9 + else + ptr += strsize(ptr) + 3 + changeflag := 1 + c.printf1(string("< %s forgotten\n"), long[tokens][1]) + return + elseif (byte[@buf][0] == "n") + c.printf1(string("< %s is not forgotten\n"), long[tokens][1]) + return + else + c.printf0(string("< I don't understand\n")) + +' Could find a word that was expected. Print a message. +PUB SomethingWrong(str) + c.printf0(string("Something's wrong\n")) + c.printf1(string("I couldn't find %s\n"), str) +' Equate the first word to the second word + +PUB AddEquate(ptr1, ptr2) | index, ptr3 + ptr3 := ptr1 + strsize(ptr1) + 1 + if (size + 5 > BUF_SIZE) + c.printf0(string("< Out of memory\n")) + return + repeat while (ptr3 < @vocab + size and(byte[ptr3][0] or byte[ptr3][1])) + index :=(byte[ptr3][0] << 7) | byte[ptr3][1] + ptr3 := @vocab + index + if ((byte[ptr3][0] & TYPE_MASK) == TYPE_EQUATE) + if ((byte[ptr3][0] & DELETE_MASK) == 0) + index :=(byte[ptr3][1] << 7) | byte[ptr3][2] + if (ptr2 == @vocab + index) + return + ptr3 += 3 + elseif ((byte[ptr3][0] & TYPE_MASK) == TYPE_RELATED) + ptr3 += 5 + elseif ((byte[ptr3][0] & TYPE_MASK) == TYPE_RELATE2) + ptr3 += 7 + else + c.printf1(string("Unknown type = %2.2x\n"), byte[ptr3][0]) + byte[ptr3][0] := size >> 7 + byte[ptr3][1] := size & 127 + ptr3 := @vocab + size + byte[ptr3++] := TYPE_EQUATE + index := ptr2 - @vocab + byte[ptr3][0] := index >> 7 + byte[ptr3][1] := index & 127 + byte[ptr3][2] := 0 + byte[ptr3][3] := 0 + size += 5 + changeflag := 1 + +' Get the next item in a chain +PUB GetNextItem(ptr) | index + if (ptr == 0) + return 0 + repeat while (ptr < @vocab + size) + if ((byte[ptr][0] & TYPE_MASK) == TYPE_EQUATE) + ptr += 3 + elseif ((byte[ptr][0] & TYPE_MASK) == TYPE_RELATED) + ptr += 5 + elseif ((byte[ptr][0] & TYPE_MASK) == TYPE_RELATE2) + ptr += 7 + else + ptr += strsize(ptr) + 1 + index :=(byte[ptr][0] << 7) | byte[ptr][1] + if (index == 0) + quit + ptr := @vocab + index + if ((byte[ptr] & DELETE_MASK) == 0) + return ptr + return 0 + +' Get the next equate item in a chain +PUB GetNextEquate(ptr) + repeat while (ptr := GetNextItem(ptr)) + if (byte[ptr] == TYPE_EQUATE) + return ptr + return 0 + +' Match two word entries and their equates in the vocab table +PUB Match(ptr1, ptr2) | index, ptr3, name1, name2 + name1 := ptr1 + 1 + repeat while (1) + ptr3 := ptr2 + name2 := ptr2 + 1 + repeat while (1) + if (c.strcmp(name1, name2) == 0) + return 1 + ptr3 := GetNextEquate(ptr3) + if (not ptr3) + quit + index :=(byte[ptr3][1] << 7) | byte[ptr3][2] + name2 := @vocab + index + 1 + ptr1 := GetNextEquate(ptr1) + if (not ptr1) + quit + index :=(byte[ptr1][1] << 7) | byte[ptr1][2] + name1 := @vocab + index + 1 + return 0 + +' Remove articles from a token list +PUB RemoveArticles(tokens, num) | i, j, ptr + i := 0 + repeat while (i < num) + ptr := FindWord(long[tokens][i]) + if (byte[ptr] == 0) + c.printf1(string("RemoveArticles: Can't find %s\n"), long[tokens][i]) + i++ + next + if (byte[ptr] == TYPE_ARTICLE) + num-- + j := i + repeat while (j < num) + long[tokens][j] := long[tokens][j + 1] + j++ + next + i++ + return num + +' Locate exact matches of a word and a verb in a related field +PUB FindWords(word1, word2) | ptr1, ptr2, index + ptr1 := FindWord(word1) + repeat while (ptr1) + if (byte[ptr1][0] == TYPE_RELATED) + index :=(byte[ptr1][1] << 7) | byte[ptr1][2] + ptr2 := @vocab + index + if (c.strcmp(ptr2 + 1, word2) == 0) + return ptr1 + ptr1 := GetNextItem(ptr1) + return 0 + +' Return true if val is a vowel +PUB isvowel(val) + val := toupper(val) + if (val == "A" or val == "E" or val == "I" or val == "O" or val == "U") + return 1 + return 0 + +PUB AddToMemory(name, ptr1) | ptr0 + ptr0 := FindWord(name) + if (ptr0 == 0 and byte[name][0] => "A" and byte[name][0] =< "Z") + byte[name][0] += "a" - "A" + ptr0 := FindWord(name) + byte[name][0] -= "a" - "A" + if (ptr0 == 0) + return + Memory[MemoryIndex] := ptr0 + Memory[MemoryIndex + 1] := ptr1 + MemoryIndex :=(MemoryIndex + 2) & MEMORY_MASK + +PUB PrintMemory | ptr0, ptr1, index + index := MemoryIndex + repeat while (1) + ptr0 := Memory[index] + ptr1 := Memory[index + 1] + if (ptr0 and ptr1) + if ((byte[ptr0] & DELETE_MASK) == 0 and(byte[ptr1] & DELETE_MASK) == 0) + PrintStatement(ptr0 + 1, ptr1, 0) + index :=(index + 2) & MEMORY_MASK + if (index == MemoryIndex) + quit + c.printf0(string("$\n")) + +PUB CheckMemory(ptr0, ptr1) | ptr0a, ptr1a, index + index := MemoryIndex + repeat while (1) + ptr0a := Memory[index] + ptr1a := Memory[index + 1] + if (ptr0 == ptr0a and ptr1 == ptr1a) + return 1 + index :=(index + 2) & MEMORY_MASK + if (index == MemoryIndex) + quit + return 0 + +' Determine whether the word is plural, singular or something else +PUB GetPlural(name) | len, ptr1 + len := strsize(name) + ptr1 := FindWord(name) + 'printf("GetPlural %s\n", name); + if (c.strcmp(name, string("I")) == 0) + return - 1 + if (len < 2) + return 0 + if (c.strcmp(name, string("you")) == 0 or c.strcmp(name, string("You")) == 0) + return 1 + if (ptr1 and byte[ptr1] <> TYPE_NOUN and byte[ptr1] <> TYPE_ADJECT) + return 0 + if (byte[name][len - 1] == "s" and byte[name][len - 2] <> "s") + return 1 + return 0 + +PUB GetVerb(plural, ptr1) | verbptr, isptr + isptr := FindWord(string("is")) + if (isptr and Match(isptr, ptr1)) + if (plural == 1) + verbptr := string("are") + elseif (plural == - 1) + verbptr := string("am") + else + verbptr := string("is") + else + verbptr := ptr1 + 1 + 'printf("GetVerb %d %s %s\n", plural, ptr1+1, verbptr); + return verbptr + +' Print a statement formed from the name and the field pointed to by ptr1 +' Substitue I for you, and You for I in the name +PUB PrintStatement(name, ptr1, remember) | ptr, nullstr, Astr, index, index2, index3, namestr[25], astr_0, plural, verbptr + byte[@nullstr] := 0 + Astr := @byte[@nullstr] + astr_0 := @byte[@nullstr] + if (remember) + if (c.strcmp(name, string("you")) == 0) + name := string("I") + elseif (c.strcmp(name, string("I")) == 0) + name := string("you") + c.strcpy(@namestr, name) + plural := GetPlural(@namestr) + ptr := FindWord(@namestr) + if (ptr) + if (byte[ptr][0] == TYPE_NOUN and plural == 0 and(byte[ptr1][0] == TYPE_RELATED or byte[ptr1][0] == TYPE_RELATE2)) + if (isvowel(byte[@namestr][0])) + Astr := string("An ") + else + Astr := string("A ") + if (not byte[Astr][0] and byte[@namestr][0] => "a" and byte[@namestr][0] =< "z") + byte[@namestr][0] -= "a" - "A" + if (remember) + c.printf0(string("< ")) + else + c.printf0(string("$ ")) + if (byte[ptr1][0] == TYPE_RELATED) + index :=(byte[ptr1][1] << 7) | byte[ptr1][2] + index2 :=(byte[ptr1][3] << 7) | byte[ptr1][4] + if (vocab[index2] == TYPE_NOUN and GetPlural(@vocab + index2 + 1) == 0) + if (isvowel(vocab[index2 + 1])) + astr_0 := string("an ") + else + astr_0 := string("a ") + verbptr := GetVerb(plural, @vocab + index) + c.printf5(string("%s%s %s %s%s\n"), Astr, @namestr, verbptr, astr_0, @vocab + index2 + 1) + if speak + c.fprintf5(speak, string("%s%s %s %s%s\n"), Astr, @namestr, verbptr, astr_0, @vocab + index2 + 1) + elseif (byte[ptr1][0] == TYPE_RELATE2) + index :=(byte[ptr1][1] << 7) | byte[ptr1][2] + index2 :=(byte[ptr1][3] << 7) | byte[ptr1][4] + index3 :=(byte[ptr1][5] << 7) | byte[ptr1][6] + if (vocab[index3] == TYPE_NOUN and GetPlural(@vocab + index3 + 1) == 0) + if (isvowel(vocab[index2 + 1])) + astr_0 := string("an ") + else + astr_0 := string("a ") + verbptr := GetVerb(plural, @vocab + index) + c.printf6(string("%s%s %s %s%s %s\n"), Astr, @namestr, verbptr, astr_0, @vocab + index2 + 1, @vocab + index3 + 1) + if speak + c.fprintf6(speak, string("%s%s %s %s%s %s\n"), Astr, @namestr, verbptr, astr_0, @vocab + index2 + 1, @vocab + index3 + 1) + elseif (byte[ptr1][0] == TYPE_EQUATE) + index :=(byte[ptr1][1] << 7) | byte[ptr1][2] + c.printf2(string("%s is %s\n"), @namestr, @vocab + index + 1) + if speak + c.fprintf2(speak, string("%s is %s\n"), @namestr, @vocab + index + 1) + else + c.printf2(string("%s is a %s\n"), @namestr, types[byte[ptr1][0] - 1]) + if speak + c.fprintf2(speak, string("%s is a %s\n"), @namestr, types[byte[ptr1][0] - 1]) + if (remember) + AddToMemory(@namestr, ptr1) + +PUB PrintQuestion(ptr1) | nullstr, Astr, isptr, plural, verbptr + byte[@nullstr] := 0 + Astr := @byte[@nullstr] + isptr := FindWord(string("is")) + plural := GetPlural(ptr1 + 1) + verbptr := GetVerb(plural, isptr) + if (byte[ptr1][0] == TYPE_NOUN and plural <> 1) + if (isvowel(byte[ptr1][1])) + Astr := string("an ") + else + Astr := string("a ") + c.printf3(string("< What %s %s%s?\n"), verbptr, Astr, ptr1 + 1) + if speak + c.fprintf3(speak, string("What %s %s%s\n"), verbptr, Astr, ptr1 + 1) + +' Process a list of tokens received from the user +PUB Process(tokens, num, question) | i, index, index2, index3, ptr[4], ptr1, ptr0, ptrx, count, counta, ptry, ptrya, match_0, equalflag, tempflag + match_0 := 0 + equalflag := 0 + tempflag := 0 + ' Equate the two words if it's an equal statement + if (num == 3) + if (not question and(c.strcmp(long[tokens][1], string("equal")) == 0 or c.strcmp(long[tokens][1], string("equals")) == 0)) + if (debugmode) + c.printf0(string("equals mode\n")) + equalflag := 1 + ptr0 := FindWord(long[tokens][0]) + ptr1 := FindWord(long[tokens][2]) + if (not ptr0) + SomethingWrong(long[tokens][0]) + return + if (not ptr1) + SomethingWrong(long[tokens][2]) + return + AddEquate(ptr0, ptr1) + AddEquate(ptr1, ptr0) + return + ' Check if the first word is do/does/did. + ' If so, remove the first word and make it a question. + if (ptr0 := FindWord(string("do"))) + if (debugmode) + c.printf0(string("does query\n")) + ptr1 := FindWord(long[tokens][0]) + if (not ptr1) + SomethingWrong(long[tokens][0]) + return + if (Match(ptr0, ptr1)) + question := 1 + num-- + i := 0 + repeat while (i < num) + long[tokens][i] := long[tokens][i + 1] + i++ + ' Remove the articles + num := RemoveArticles(tokens, num) + ' There must be three items in the list or it is too short or too long + if (num < 3) + c.printf0(string("I don't understand\n")) + c.printf0(string("Use more words\n")) + return + if (num > 4) + c.printf0(string("I don't understand\n")) + c.printf0(string("Use less words\n")) + return + ' Locate the tokens in the vocab table + i := 0 + repeat while (i < num) + ptr[i] := FindWord(long[tokens][i]) + if (ptr[i] == 0) + SomethingWrong(long[tokens][i]) + return + i++ + ' Change the word type if commanded to do so + if (num == 3 and c.strcmp(long[tokens][1], string("is")) == 0) + if (c.strcmp(long[tokens][2], string("noun")) == 0) + byte[ptr[0]][0] := TYPE_NOUN + return + elseif (c.strcmp(long[tokens][2], string("name")) == 0) + byte[ptr[0]][0] := TYPE_NAME + return + elseif (c.strcmp(long[tokens][2], string("adjective")) == 0) + byte[ptr[0]][0] := TYPE_ADJECT + return + elseif (c.strcmp(long[tokens][2], string("verb")) == 0) + byte[ptr[0]][0] := TYPE_VERB + return + ' If the first word is a query, the second word must be is/am/are + if (byte[ptr[0]][0] == TYPE_QUERY) + if (debugmode) + c.printf0(string("query mode\n")) + question := 1 + ptr1 := FindWord(string("is")) + tempflag := 0 + 'if (num != 3 || !ptr1 || !Match(ptr1, ptr[1])) + if (num == 3) + if (ptr1) + if (Match(ptr1, ptr[1])) + tempflag := 1 + if (not tempflag) + c.printf0(string("I don't understand\n")) + return + ' Scan related entries and equate's related entries + ptr1 := ptr[2] + ptr0 := ptr1 + ptrx := ptr1 + ptry := ptr1 + count := 0 + ' variables for items not in short term memory + ptrya := 0 + counta := 0 + if (debugmode) + PrintStatement(long[tokens][2], ptr1, 0) + repeat while (1) + repeat while (1) + ptr1 := GetNextItem(ptr1) + if (not ptr1) + quit + if (byte[ptr1][0] <> TYPE_RELATED and byte[ptr1][0] <> TYPE_RELATE2) + next + 'index = (ptr1[1] << 7) | ptr1[2]; + 'index2 = (ptr1[3] << 7) | ptr1[4]; + if (debugmode) + PrintStatement(long[tokens][2], ptr1, 0) + if (CheckMemory(ptr[2], ptr1) == 0) + counta++ + if ((rand // counta) == 0) + ptrya := ptr1 + if (ptrya == 0) + count++ + if ((rand // count) == 0) + ptry := ptr1 + if (ptrya == 0) + ptrya := ptry + ptr0 := GetNextEquate(ptr0) + if (not ptr0) + quit + index :=(byte[ptr0][1] << 7) | byte[ptr0][2] + ptrx := @vocab + index + ptr1 := ptrx + if (ptrya) + if (byte[ptrya][0] == TYPE_RELATED or byte[ptrya][0] == TYPE_RELATE2 or byte[ptrya][1] == TYPE_EQUATE) + PrintStatement(long[tokens][2], ptrya, 1) + else + c.printf0(string("< I don't know\n")) + return + ' If not an equal statement or a question check if first word is a verb + ' Is so, swap the first and second tokens + if (not equalflag and not question and byte[ptr[0]][0] == TYPE_VERB) + if (debugmode) + c.printf0(string("Lead verb query\n")) + question := 1 + ptr1 := ptr[0] + ptr[0] := ptr[1] + ptr[1] := ptr1 + ptr1 := long[tokens][0] + long[tokens][0] := long[tokens][1] + long[tokens][1] := ptr1 + ' Look for a match to the three words. If it is a question and + ' a match is found print "Yes" + ptr1 := ptr[0] + ptr0 := ptr1 + ptrx := ptr1 + repeat while (1) + repeat while (1) + ptr1 := GetNextItem(ptr1) + if (not ptr1) + quit + if (byte[ptr1][0] <> TYPE_RELATED and byte[ptr1][0] <> TYPE_RELATE2) + next + index :=(byte[ptr1][1] << 7) | byte[ptr1][2] + index2 :=(byte[ptr1][3] << 7) | byte[ptr1][4] + tempflag := Match(ptr[1], @vocab + index) and Match(ptr[2], @vocab + index2) + if (num == 4) + index3 :=(byte[ptr1][5] << 7) | byte[ptr1][6] + tempflag := tempflag and Match(ptr[3], @vocab + index3) + if (debugmode) + c.printf7(string("%s (%s,%s) (%s,%s) (%s,%s)\n"), ptrx + 1, ptr[1] + 1, @vocab + index + 1, ptr[2] + 1, @vocab + index2 + 1, ptr[3], @vocab + index3 + 1) + else + if (debugmode) + c.printf5(string("%s (%s,%s) (%s,%s)\n"), ptrx + 1, ptr[1] + 1, @vocab + index + 1, ptr[2] + 1, @vocab + index2 + 1) + if (tempflag) + if (not question) + c.printf0(string("< I know that\n")) + else + c.printf0(string("< Yes\n")) + return + ptr0 := GetNextEquate(ptr0) + if (not ptr0) + quit + index :=(byte[ptr0][1] << 7) | byte[ptr0][2] + ptrx := @vocab + index + ptr1 := ptrx + ' Print "No" if it was a question and no match found + if (question) + c.printf0(string("< No\n")) + return + ' Add relationship if there was no match and it's not a question + if (not match_0) + if (size + 1 +(num << 1) > BUF_SIZE) + c.printf0(string("< Out of memory\n")) + return + ptr1 := ptr[0] + repeat while (ptr0 := GetNextItem(ptr1)) + ptr1 := ptr0 + if (byte[ptr1][0] == TYPE_RELATED) + ptr1 += 5 + elseif (byte[ptr1][0] == TYPE_RELATE2) + ptr1 += 7 + elseif (byte[ptr1][0] == TYPE_EQUATE) + ptr1 += 3 + else + ptr1 += strsize(ptr1) + 1 + byte[ptr1][0] := size >> 7 + byte[ptr1][1] := size & 127 + ptr1 := @vocab + size + if (num == 3) + byte[ptr1][0] := TYPE_RELATED + else + byte[ptr1][0] := TYPE_RELATE2 + index := ptr[1] - @vocab + byte[ptr1][1] := index >> 7 + byte[ptr1][2] := index & 127 + index := ptr[2] - @vocab + byte[ptr1][3] := index >> 7 + byte[ptr1][4] := index & 127 + if (num == 3) + byte[ptr1][5] := 0 + byte[ptr1][6] := 0 + size += 7 + else + index := ptr[3] - @vocab + byte[ptr1][5] := index >> 7 + byte[ptr1][6] := index & 127 + byte[ptr1][7] := 0 + byte[ptr1][8] := 0 + size += 9 + changeflag := 1 + ' Check if predicate is undefined + index := 0 + ptr1 := ptr[2] + if (byte[ptr1][0] < 32) + ptr0 := ptr1 + strsize(ptr1) + 1 + index :=(byte[ptr0][0] << 7) | byte[ptr0][1] + if (index == 0) + PrintQuestion(ptr1) + else + c.printf0(string("< I will remember that\n")) + +' Wait from 3 to 10 seconds for input. Print a random fact from +' the vocab table if no input received. +PUB WaitForInput | waittime, starttime, ptr1, ptr2, ptr1a, ptr2a, index, count, FirstTime + waittime :=((rand // 8) + 3) * clkfreq + starttime := GetTicks + c.printf0(string("> ")) + repeat while (not kbhit) + ' Scan all the entries in the vocab table + if (GetTicks - starttime > waittime) + count := 0 + ptr1 := @vocab + ptr1a := ptr2a := 0 + repeat while (ptr1 < @vocab + size) + if ((byte[ptr1][0] & TYPE_MASK) == TYPE_RELATED) + ptr1 += 7 + elseif ((byte[ptr1][0] & TYPE_MASK) == TYPE_RELATE2) + ptr1 += 9 + elseif ((byte[ptr1][0] & TYPE_MASK) == TYPE_EQUATE) + ptr1 += 5 + elseif (byte[ptr1][0] & DELETE_MASK) + ptr1 += strsize(ptr1) + 3 + else + ptr2 := ptr1 + FirstTime := 1 + repeat while ((ptr2 := GetNextItem(ptr2)) or FirstTime) + FirstTime := 0 + if (ptr2 == 0 and byte[ptr1][0] <> TYPE_NAME and byte[ptr1][0] <> TYPE_NOUN and byte[ptr1][0] <> TYPE_ADJECT) + quit + if (ptr2 == 0 or byte[ptr2][0] == TYPE_RELATED) + count++ + if ((rand // count) == 0) + if (count == 1 or CheckMemory(ptr1, ptr2) == 0) + ptr1a := ptr1 + ptr2a := ptr2 + ptr1 += strsize(ptr1) + 3 + ' Print the entry that was randomly selected + if (ptr1a and ptr2a) + if (byte[ptr2a][0] == TYPE_RELATED) + c.putchar(8) + c.putchar(8) + PrintStatement(ptr1a + 1, ptr2a, 1) + c.printf0(string("> ")) + elseif (ptr1a) + c.putchar(8) + c.putchar(8) + PrintQuestion(ptr1a) + c.printf0(string("> ")) + waittime :=((rand // 10) + 5) * clkfreq + starttime := GetTicks + +' Save the vocab table +PUB SaveVocab | addr, i, val, val1 + c.printf0(string("$ Save disabled\n")) +{ + addr := @size + i :=(size + 7) >> 2 + repeat while (i > 0) + val := long[addr] + if (val <> i2c.ReadLong(28, $a0, addr)) + i2c.WriteLong(28, $a0, addr, val) + repeat while (i2c.WriteWait(28, $a0, addr)) + addr += 4 + i-- +} + +PUB CheckCommand(tokens, num, buffer) | ptr1, retval, caseshift + retval := 1 + caseshift :=(byte[long[tokens][0]][0] => "A" and byte[long[tokens][0]][0] =< "Z") + if (num > 2) + return 0 + if (caseshift) + byte[long[tokens][0]][0] += "a" - "A" + if (c.strcmp(long[tokens][0], string("help")) == 0) + c.printf0(string("$ Type a sentence, or enter one of these commands\n")) + c.printf0(string("$ vocab [word] - Print the words I know\n")) + c.printf0(string("$ dump - Print the words as hex codes\n")) + c.printf0(string("$ forget - Remove a word from memroy\n")) + c.printf0(string("$ save - Save the words to EEPROM\n")) + c.printf0(string("$ reset - Reset the word table\n")) + c.printf0(string("$ basic - Reset the word table to a basic list\n")) + c.printf0(string("$ history - Print the short term memory\n")) + c.printf0(string("$ goodbye - Exit the program\n")) + elseif (c.strcmp(long[tokens][0], string("goodbye")) == 0) + if (changeflag := 0) + repeat while (1) + c.printf0(string("< Should I save?\n")) + c.printf0(string("> ")) + c.gets(buffer) + ptr1 := SkipChar(buffer, " ") + if (toupper(byte[ptr1][0]) == "Y") + SaveVocab + quit + elseif (toupper(byte[ptr1][0]) == "N") + quit + c.printf0(string("< I don't undertand\n")) + c.printf0(string("< Goodbye\n")) + waitcnt(cnt+clkfreq/10) + 'abort(0) + spinix.exit(0) + elseif (c.strcmp(long[tokens][0], string("vocab")) == 0) + DumpAsciiWords(long[tokens][1], num) + elseif (c.strcmp(long[tokens][0], string("dump")) == 0) + DumpHexWords + elseif (c.strcmp(long[tokens][0], string("forget")) == 0) + DeleteWord(tokens, num) + elseif (c.strcmp(long[tokens][0], string("save")) == 0) + SaveVocab + elseif (c.strcmp(long[tokens][0], string("reset")) == 0) + ResetVocab + elseif (c.strcmp(long[tokens][0], string("basic")) == 0) + ResetBasicVocab + elseif (c.strcmp(long[tokens][0], string("history")) == 0) + PrintMemory + elseif (c.strcmp(long[tokens][0], string("debug")) == 0) + debugmode := not debugmode + else + retval := 0 + ' Restore the case if needed + if (caseshift) + byte[long[tokens][0]][0] -= "a" - "A" + return retval + +' This routine set the linefeed enable flag based on the config word +' The config word is the first long after the last argument +PUB config_linefeed(argc, argv) | ptr + ptr := long[argv][argc-1] + ptr += strsize(ptr) + 1 + ptr := ((ptr + 3) >> 2) << 2 + c.set_linefeed(long[ptr] & 1) + +PUB main(argc, argv) | i, num, ptr1, ignore, question, caseshift, buffer[25], tokens[50] + 'speak := c.getstdout + 'speak := c.openserial(16, 16, 0, 9600, 64, 64) + config_linefeed(argc, argv) + c.printf0(string("Lerner 0.08 -- Spinix version\n")) + c.printf0(string("Save command disabled in this version\n\n")) + 'i2c.Initialize(28) + i := 0 + repeat while (i =< MEMORY_MASK) + Memory[i] := 0 + i++ + 'printf("< Hello\n"); + ptr1 := FindWords(string("you"), string("are")) + if (ptr1) + PrintStatement(string("you"), ptr1, 1) + ' Loop on each user input + repeat while (1) + WaitForInput + c.gets(@buffer) + num := tokenize(@buffer, @tokens) + if (num == 0) + next + if (CheckCommand(@tokens, num, @buffer)) + next + ' Check for a question mark at the end + if (c.strcmp(tokens[num - 1], string("?")) == 0) + question := 1 + num-- + else + question := 0 + ' Check if all the words are in the vocab table + ignore := 0 + i := 0 + repeat while (i < num) + if (FindWord(tokens[i]) == 0) + if (ignore := UnknownWord(@tokens, num, i)) + quit + i++ + if (ignore) + next + ' Process the user input + Process(@tokens, num, question) + return 0 +{ ++-----------------------------------------------------------------------------+ +| TERMS OF USE: MIT License | ++-----------------------------------------------------------------------------+ +|Permission is hereby granted, free of charge, to any person obtaining a copy | +|of this software and associated documentation files (the "Software"), to deal| +|in the Software without restriction, including without limitation the rights | +|to use, copy, modify, merge, publish, distribute, sublicense, and/or sell | +|copies of the Software, and to permit persons to whom the Software is | +|furnished to do so, subject to the following conditions: | +| | +|The above copyright notice and this permission notice shall be included in | +|all copies or substantial portions of the Software. | +| | +|THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | +|IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | +|FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE | +|AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | +|LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,| +|OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE| +|SOFTWARE. | ++-----------------------------------------------------------------------------+ +} diff --git a/lerner/sysdefs.spin b/lerner/sysdefs.spin new file mode 100644 index 0000000..61fe3ca --- /dev/null +++ b/lerner/sysdefs.spin @@ -0,0 +1,112 @@ +'****************************************************************************** +' Copyright (c) 2011, 2012, Dave Hein +' See end of file for terms of use. +'****************************************************************************** +CON + 'start = $7e50 ' Extra space for the stand-alone loader + start = $7c00 ' Extra space for the stand-alone loader + + ' SD CLIB data + rendezvous = $7e50 + + ' Exported variables + environ_vars = $7e50 + environ_vars_end = $7ed3 + + ' Argv parameter area + argv_parms = $7ed4 + + ' Additional system parameters + return_value = $7f94 + vga_cog = $7f98 + vga_handle = $7f9c + sd_pins = $7fa0 + config = $7fa4 + shell_sector = $7fa8 + + ' System time + unixtime = $7fac + cycle0 = $7fb0 + timezone = $7fb4 +{ + ' I2C Driver + i2c_cog = $7fb8 + i2c_cmd = $7fbc + i2c_parm = $7fc0 + + ' Kernel data + filelock = $7fc4 + filecmd = $7fc8 + fileparm = $7fcc +} + 'Shell variables + scriptline = $7fb8 + ifflag = $7fbc + whileflag = $7fc0 + shell_size = $7fc4 + shell_level = $7fc8 + bootflag = $7fcc + + 'File I/O + spi_engine_cog = $7fd0 + spi_command = $7fd4 + spi_block_index = $7fd8 + spi_buffer_address = $7fdc + + ' Basic CLIB data + ' Serial data + serial = $7fe0 + stdio = $7fe4 + stdin = $7fe8 + stdout = $7fec + + ' Malloc data + memlocknum = $7ff0 + memfreelist = $7ff4 + malloclist = $7ff8 + laststackaddr = $7ffc + + ' Stack check word + checkword = $dead1eaf + + ' Process types + proc_type_spin = 1 + proc_type_pasm = 2 + proc_type_capp = 3 + proc_type_driver = $80 + + ' Run modes + run_shell_wait = $00 + run_shell_nowait = $08 + run_kill_caller = $10 + run_at_address0 = $20 + run_c_program = $40 + run_spin_program = $80 + run_stand_alone = $100 + +PUB main + return + +{{ ++-----------------------------------------------------------------------------+ +| TERMS OF USE: MIT License | ++-----------------------------------------------------------------------------+ +|Permission is hereby granted, free of charge, to any person obtaining a copy | +|of this software and associated documentation files (the "Software"), to deal| +|in the Software without restriction, including without limitation the rights | +|to use, copy, modify, merge, publish, distribute, sublicense, and/or sell | +|copies of the Software, and to permit persons to whom the Software is | +|furnished to do so, subject to the following conditions: | +| | +|The above copyright notice and this permission notice shall be included in | +|all copies or substantial portions of the Software. | +| | +|THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | +|IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | +|FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE | +|AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | +|LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,| +|OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE| +|SOFTWARE. | ++-----------------------------------------------------------------------------+ +}} diff --git a/license.txt b/license.txt new file mode 100644 index 0000000..f08ca01 --- /dev/null +++ b/license.txt @@ -0,0 +1,26 @@ ++-----------------------------------------------------------------------------+ +|The spinix software is distributed under the terms of the MIT license as | +|shown below. | +| | +|Copyright (c) 2011, 2012, 2013, Dave Hein | ++-----------------------------------------------------------------------------+ +| TERMS OF USE: MIT License | ++-----------------------------------------------------------------------------+ +|Permission is hereby granted, free of charge, to any person obtaining a copy | +|of this software and associated documentation files (the "Software"), to deal| +|in the Software without restriction, including without limitation the rights | +|to use, copy, modify, merge, publish, distribute, sublicense, and/or sell | +|copies of the Software, and to permit persons to whom the Software is | +|furnished to do so, subject to the following conditions: | +| | +|The above copyright notice and this permission notice shall be included in | +|all copies or substantial portions of the Software. | +| | +|THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | +|IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | +|FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE | +|AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | +|LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,| +|OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE| +|SOFTWARE. | ++-----------------------------------------------------------------------------+ diff --git a/manpages/alias.txt b/manpages/alias.txt new file mode 100644 index 0000000..e7e947b --- /dev/null +++ b/manpages/alias.txt @@ -0,0 +1,24 @@ +ALIAS(1) User Commands ALIAS(1) + +NAME + alias - create an alias + +SYNOPSIS + alias [name=value] + +DESCRIPTION + The alias command will create an alias symbol that will be used + generate a shell command. If not parameters are specified the list + of aliases will be printed. + +AUTHOR + Dave Hein + +COPYRIGHT + Copyright (c) 2013 Dave Hein + MIT License (See license.txt in the root directory) + This is free software: you are free to change and redistribute it. + There is no warranty, to the extent permitted by law. + + +SPINIX utility October 2013 ALIAS(1) diff --git a/manpages/boot.txt b/manpages/boot.txt new file mode 100644 index 0000000..5f68c2a --- /dev/null +++ b/manpages/boot.txt @@ -0,0 +1,34 @@ +BOOT(1) Spinix OS BOOT(1) + +NAME + boot - spinix boot program + +DESCRIPTION + The spinix boot program resides in the Propeller boot EEPROM. When run + for the first time, the boot program prompts the user for the I/O pins + used to interface to the SD card. The pin values are stored back into + EEPROM so that the user does not need to specify them in subsequent + start ups. + + The boot program starts up the serial and SD drivers. The serial and + SD drivers and the kernel each run in their own cog. + + The boot program reads the file _shell.var, and set the SHELL + environment variable to the contents of this file. It then verifies + that the shell program exists, and starts the shell. + + +SEE ALSO + For more information see the man pages for spinix and shell. + +AUTHOR + Dave Hein + +COPYRIGHT + Copyright (c) 2013 Dave Hein + MIT License (See license.txt in the root directory) + This is free software: you are free to change and redistribute it. + There is no warranty, to the extent permitted by law. + + +SPINIX OS September 2013 BOOT(1) diff --git a/manpages/cat.txt b/manpages/cat.txt new file mode 100644 index 0000000..73d4b2b --- /dev/null +++ b/manpages/cat.txt @@ -0,0 +1,25 @@ +CAT(1) User Commands CAT(1) + +NAME + cat - concatenate files and print on the standard output + +SYNOPSIS + cat [FILE]... + +DESCRIPTION + Concatenate FILE(s), or standard input to the standard output. When no + files are specified the standard input is used. Input from the standard + input is terminated by typing a ^D. + + +AUTHOR + Dave Hein + +COPYRIGHT + Copyright (c) 2011, 2012, Dave Hein + MIT License (See license.txt in the root directory) + This is free software: you are free to change and redistribute it. + There is no warranty, to the extent permitted by law. + + +SPINIX utility March 2012 CAT(1) diff --git a/manpages/cd.txt b/manpages/cd.txt new file mode 100644 index 0000000..f01dce7 --- /dev/null +++ b/manpages/cd.txt @@ -0,0 +1,24 @@ +CD(1) User Commands CD(1) + +NAME + cd - change the working directory + +SYNOPSIS + cd [directory] + +DESCRIPTION + The cd utility changes the working directory in the current shell if + the directory path exists. Otherwise, the current working directory + will remain the same. + +AUTHOR + Dave Hein + +COPYRIGHT + Copyright (c) 2011, 2012, Dave Hein + MIT License (See license.txt in the root directory) + This is free software: you are free to change and redistribute it. + There is no warranty, to the extent permitted by law. + + +SPINIX utility March 2012 CD(1) diff --git a/manpages/chmod.txt b/manpages/chmod.txt new file mode 100644 index 0000000..9d14526 --- /dev/null +++ b/manpages/chmod.txt @@ -0,0 +1,24 @@ +CHMOD(1) User Commands CHMOD(1) + +NAME + chmod - change file mode bits + +SYNOPSIS + chmod [MODE] [FILE] + +DESCRIPTION + Changes the mode bits of a FILE based on the MODE string. MODE is + either an octal number or contains the characters +-drwx. If an octal + number is specified, the bits map to the drwx field. + +AUTHOR + Dave Hein + +COPYRIGHT + Copyright (c) 2011, 2012, Dave Hein + MIT License (See license.txt in the root directory) + This is free software: you are free to change and redistribute it. + There is no warranty, to the extent permitted by law. + + +SPINIX utility March 2012 CHMOD(1) diff --git a/manpages/config.txt b/manpages/config.txt new file mode 100644 index 0000000..f70f0b3 --- /dev/null +++ b/manpages/config.txt @@ -0,0 +1,49 @@ +CONFIG(1) User Commands CONFIG(1) + +NAME + config - configure the system + +SYNOPSIS + config [PARM] + +DESCRIPTION + The config utility is used to control how information is displayed on + the console terminal. The config utility writes the value of PARM to + the config location in Spinix. PARM is expressed as a hexadecimal + number. + + The config value indicates the number of display lines, the terminal + type and whether a newline sends the line-feed and carriage-return + characters to the console. The format of PARM is "NNTL", where "NN" is + a 2-digit hex number indicating the number of display lines, "T" is a + single hex digit with a value of 0, 1 or 2 for TTY, PST or ANSI + terminal mode. "L" is a single hex digit with the following values: + + 0 - Do not send any characters + 1 - Send a LF character + 2 - Send a CR character + 3 - Send a CR character followed by a LF + + The "linefeed" command may also be used to enable/disable whether a + line-feed character is sent by a newline. + +EXAMPLES + The following shows various examples of terminal configurations: + + config 2 - TTY terminal with no line specification + config 12 - PST terminal with no line specification + config 23 - ANSI terminal with no line specification + config 1823 - 24-line ANSI terminal with CR and LF + config 2812 - 40-line PST terminal with CR + +AUTHOR + Dave Hein + +COPYRIGHT + Copyright (c) 2011, 2012, Dave Hein + MIT License (See license.txt in the root directory) + This is free software: you are free to change and redistribute it. + There is no warranty, to the extent permitted by law. + + +SPINIX utility March 2012 CONFIG(1) diff --git a/manpages/cp.txt b/manpages/cp.txt new file mode 100644 index 0000000..9e4dc46 --- /dev/null +++ b/manpages/cp.txt @@ -0,0 +1,22 @@ +CP(1) User Commands CP(1) + +NAME + cp - copy files + +SYNOPSIS + cp [FILE1] [FILE2] + +DESCRIPTION + Copy FILE1 to FILE2 + +AUTHOR + Dave Hein + +COPYRIGHT + Copyright (c) 2011, 2012, Dave Hein + MIT License (See license.txt in the root directory) + This is free software: you are free to change and redistribute it. + There is no warranty, to the extent permitted by law. + + +SPINIX utility March 2012 CP(1) diff --git a/manpages/date.txt b/manpages/date.txt new file mode 100644 index 0000000..6b77f26 --- /dev/null +++ b/manpages/date.txt @@ -0,0 +1,30 @@ +DATE(1) User Commands DATE(1) + +NAME + date - display or set the system date and time + +SYNOPSIS + date [-s [yyyy/mm/dd] [hh:mm[:ss]]] + +DESCRIPTION + If no parameters are specified the date and time will be displayed + in the yyyy/mm/dd hh:mm:ss format. If the -s option is specified + without any additional parameters the unix epoch time will be printed, + which is the number of seconds since the beginning of 1970. + + If the -s option is specified with additional parameters the date + and/or time can be set by specifying either the date, time or both. + The date and time can be specified in either order, and the seconds + parameter is optional when specifying the time. + +AUTHOR + Dave Hein + +COPYRIGHT + Copyright (c) 2013, Dave Hein + MIT License (See license.txt in the root directory) + This is free software: you are free to change and redistribute it. + There is no warranty, to the extent permitted by law. + + +SPINIX utility September 2013 DATE(1) diff --git a/manpages/declare.txt b/manpages/declare.txt new file mode 100644 index 0000000..d462606 --- /dev/null +++ b/manpages/declare.txt @@ -0,0 +1,31 @@ +DECLARE(1) User Commands DECLARE(1) + +NAME + declare - declare one or more environment variables + +SYNOPSIS + declare [-p] [-x] [name[=[value]]...] + +DESCRIPTION + If no parameters are specified, export will print all of the + environment variables. If only the -p option is provided the variables + will be printed preceed by "declare -F ", where F will be x for + exported variables, and - for non-exported variables. If -x is + specified only the exported variables will be printed. + + If one or more names is provided the variables will be declared. If + the name is followed by the "=" character the following value will be + assigned to the variable. If the -x option is specified before the + list of names the variables will be exported. + +AUTHOR + Dave Hein + +COPYRIGHT + Copyright (c) 2013, Dave Hein + MIT License (See license.txt in the root directory) + This is free software: you are free to change and redistribute it. + There is no warranty, to the extent permitted by law. + + +SPINIX utility March 2013 DECLARE(1) diff --git a/manpages/diag.txt b/manpages/diag.txt new file mode 100644 index 0000000..611a8a9 --- /dev/null +++ b/manpages/diag.txt @@ -0,0 +1,36 @@ +DIAG(1) User Commands DIAG(1) + +NAME + diag - perform diagnostics + +SYNOPSIS + diag + +DESCRIPTION + diag implements the following commands: + + help - Print commands + exit - Exit diag + stack - Print the amount of unused stack space + malloc - Print the malloc and free lists + peek address - Print value at address in EEPROM + poke address num - Write value at address in EEPROM + dump address num - Print num values statring at address in EEPROM + peekm address - Print value at address in memory + pokem address num - Write value at address in memory + dumpm address num - Print num values statring at address in memory + filename - Execute an application file + +LIMITATIONS + +AUTHOR + Dave Hein + +COPYRIGHT + Copyright (c) 2011, 2012, Dave Hein + MIT License (See license.txt in the root directory) + This is free software: you are free to change and redistribute it. + There is no warranty, to the extent permitted by law. + + +SPINIX utility November 2013 DIAG(1) diff --git a/manpages/diaghelp.txt b/manpages/diaghelp.txt new file mode 100644 index 0000000..10520a2 --- /dev/null +++ b/manpages/diaghelp.txt @@ -0,0 +1,11 @@ +help - Print commands +exit - Exit diag +stack - Print the amount of unused stack space +malloc - Print the malloc and free lists +peek address - Print value at address in EEPROM +poke address num - Write value at address in EEPROM +dump address num - Print num values statring at address in EEPROM +peekm address - Print value at address in memory +pokem address num - Write value at address in memory +dumpm address num - Print num values statring at address in memory +filename - Execute an application file diff --git a/manpages/diff.txt b/manpages/diff.txt new file mode 100644 index 0000000..3e8e7dc --- /dev/null +++ b/manpages/diff.txt @@ -0,0 +1,23 @@ +DIFF(1) User Commands DIFF(1) + +NAME + diff - compare files line by line + +SYNOPSIS + diff [FILE1] [FILE2] + +DESCRIPTION + Compares FILE1 with FILE2 line by line, and prints the differences to + the standard output. + +AUTHOR + Dave Hein + +COPYRIGHT + Copyright (c) 2011, 2012, Dave Hein + MIT License (See license.txt in the root directory) + This is free software: you are free to change and redistribute it. + There is no warranty, to the extent permitted by law. + + +SPINIX utility March 2012 DIFF(1) diff --git a/manpages/dos2unix.txt b/manpages/dos2unix.txt new file mode 100644 index 0000000..22a1ae4 --- /dev/null +++ b/manpages/dos2unix.txt @@ -0,0 +1,26 @@ +DOS2UNIX(1) User Commands DOS2UNIX(1) + +NAME + dos2unix - convert from from dos to unix format + +SYNOPSIS + dos2unix [FILE] + +DESCRIPTION + The dos2unix utility converts a text file from the DOS format to the + Unix format. A line in a DOS text file is terminated by the carriage- + return and line-feed characters. A Unix file is terminated only by + the line-feed character. + + +AUTHOR + Dave Hein + +COPYRIGHT + Copyright (c) 2011, 2012, Dave Hein + MIT License (See license.txt in the root directory) + This is free software: you are free to change and redistribute it. + There is no warranty, to the extent permitted by law. + + +SPINIX utility March 2012 DOS2UNIX(1) diff --git a/manpages/echo.txt b/manpages/echo.txt new file mode 100644 index 0000000..9f4f3a6 --- /dev/null +++ b/manpages/echo.txt @@ -0,0 +1,22 @@ +ECHO(1) User Commands ECHO(1) + +NAME + echo - display a line of text + +SYNOPSIS + echo [STRING]... + +DESCRIPTION + Echo the STRING(s) to standard output. + +AUTHOR + Dave Hein + +COPYRIGHT + Copyright (c) 2011, 2012, Dave Hein + MIT License (See license.txt in the root directory) + This is free software: you are free to change and redistribute it. + There is no warranty, to the extent permitted by law. + + +SPINIX utility March 2012 ECHO(1) diff --git a/manpages/ed.txt b/manpages/ed.txt new file mode 100644 index 0000000..1109bc4 --- /dev/null +++ b/manpages/ed.txt @@ -0,0 +1,313 @@ +ED(1) User Commands ED(1) + +NAME + ed - text editor + +SYNOPSIS + ed [file] + +DESCRIPTION + ed is a line-oriented text editor. It is used to create, display, + modify and otherwise manipulate text files. This version of ed + implements most of the commands in the standard ed utility. It does + not allow wildcard or escape characters in the search or replacement + strings, and it cannot execute shell commands. It also does not + implement the undo command, and it only allows one command per line. + + There are two non-standard commands supported that provide assistance + in using ed. The 'h' command will print help information that + summarizes the commands available in ed. A 'V' command was added to + provide an enhanced visualization mode, which prints the text around the + current line after executing a command. + + If invoked with a file argument, a copy of file is read into the + editor's buffer. Changes are made to this copy and not directly to file + itself. Upon quitting ed, any changes not explicitly saved with a 'w' + command are lost. + + Editing is done in two distinct modes: command and input. When first + invoked, ed is in command mode. In this mode commands are read from the + standard input and executed to manipulate the contents of the editor + buffer. A typical command might look like: + + ,s/old/new/g + + which replaces all occurrences of the string old with new. + + When an input command, such as 'a' (append), 'i' (insert) or 'c' + (change), is given, ed enters input mode. This is the primary means of + adding text to a file. In this mode, no commands are available; + instead, the standard input is written directly to the editor buffer. + Lines consist of text up to and including a newline character. Input + mode is terminated by entering a single period (.) on a line. + + All ed commands operate on whole lines or ranges of lines; e.g., the 'd' + command deletes lines; the 'm' command moves lines, and so on. It is + possible to modify only a portion of a line by means of replacement, as + in the example above. However even here, the 's' command is applied to + whole lines at a time. + + In general, ed commands consist of zero or more line addresses, followed + by a single character command and possibly additional parameters; i.e., + commands have the structure: + + [address [,address]]command[parameters] + + The address(es) indicate the line or range of lines to be affected by + the command. If fewer addresses are given than the command accepts, + then default addresses are supplied. + + + OPTIONS + file Specifies the name of a file to read. The default filename is + set to the specified file. + + + LINE ADDRESSING + An address represents the number of a line in the buffer. ed maintains + a current address which is typically supplied to commands as the default + address when none is specified. When a file is first read, the + current address is set to the last line of the file. In general, the + current address is set to the last line affected by a command. + + A line address is constructed from one of the bases in the list below, + optionally followed by a numeric offset. The offset may include any + combination of digits, operators (i.e. + and -). Addresses are read + from left to right, and their values are computed relative to the + current address. + + One exception to the rule that addresses represent line numbers is the + address 0 (zero). This means "before the first line," and is legal + wherever it makes sense. + + An address range is two addresses separated either by a comma or + semicolon. The value of the first address in a range cannot exceed the + value of the second. If only one address is given in a range, then the + second address is set to the given address. If only one address is + expected, then the last address is used. + + Each address in a comma-delimited range is interpreted relative to the + current address. In a semicolon-delimited range, the first address is + used to set the current address, and the second address is + interpreted relative to the first. + + + The following address symbols are recognized. + + + . The current line (address) in the buffer. + + + $ The last line in the buffer. + + + n The nth, line in the buffer where n is a number in the range + [0,$]. + + + - The previous line. This is equivalent to -1. + + + -n The nth previous line, where n is a non-negative number. + + + + The next line. This is equivalent to +1. + + + +n The nth next line, where n is a non-negative number. + + + , The first through last lines in the buffer. This is equivalent + to the address range 1,$. + + + ; The current through last lines in the buffer. This is + equivalent to the address range .,$. + + + /re/ The next line containing the regular expression re. The search + wraps to the beginning of the buffer and continues down to the + current line, if necessary. // repeats the last search. + + + ?re? The previous line containing the regular expression re. The + search wraps to the end of the buffer and continues up to the + current line, if necessary. ?? repeats the last search. + + + REGULAR EXPRESSIONS + Regular expressions are patterns used in selecting text. For example, + the ed command + + g/string/ + + prints all lines containing string. Regular expressions are also used + by the 's' command for selecting old text to be replaced with new. This + version of ed does not use any wildcard or escape characters. + + COMMANDS + All ed commands are single characters, though some require additional + parameters. ed commands must fit in a single line, and cannot be + continued to another line. + + In general, only one command is allowed per line. The only exceptions + are the 'g' and 'v' commands, which can be followed by an 's', 'p', 'l' + or 'n' command. + + ed recognizes the following commands. The commands are shown together + with the default address or address range supplied if none is specified + (in parenthesis). + + (.)a Appends text to the buffer after the addressed line, which may + be the address 0 (zero). Text is entered in input mode. The + current address is set to last line entered. + + (.,.)c Changes lines in the buffer. The addressed lines are deleted + from the buffer, and text is appended in their place. Text is + entered in input mode. The current address is set to last line + entered. + + (.,.)d Deletes the addressed lines from the buffer. If there is a line + after the deleted range, then the current address is set to this + line. Otherwise the current address is set to the line before + the deleted range. + + e file Edits file, and sets the default filename. If file is not + specified, then the default filename is used. Any lines in the + buffer are deleted before the new file is read. The current + address is set to the last line read. + + E file Edits file unconditionally. This is similar to the e command, + except that unwritten changes are discarded without warning. + The current address is set to the last line read. + + f file Sets the default filename to file. If file is not specified, + then the default filename is printed. + + (1,$)g/re/command + Applies command to each of the addressed lines matching a + regular expression re. The current address is set to the line + currently matched before command is executed. At the end of the + 'g' command, the current address is set to the last line + affected by command. The allowable commands are 's', 'p', 'l' + and 'n'. The 'p' command is used if command is not specified. + + h Print help information, which summarizes the commands available. + Note that this is different than the standard usage for 'h', + which prints an explanation of the last error. + + (.)i Inserts text in the buffer before the current line. Text is + entered in input mode. The current address is set to the last + line entered. + + (.,.+1)j + Joins the addressed lines. The addressed lines are deleted from + the buffer and replaced by a single line containing their joined + text. The current address is set to the resultant line. + + (.,.)l Prints the addressed lines unambiguously. The current address + is set to the last line printed. + + (.,.)m(.) + Moves lines in the buffer. The addressed lines are moved to + after the right-hand destination address, which may be the + address 0 (zero). The current address is set to the new address + of the last line moved. + + (.,.)n Prints the addressed lines along with their line numbers. The + current address is set to the last line printed. + + (.,.)p Prints the addressed lines. The current address is set to the + last line printed. + + P Toggles the command prompt on and off. The command prompt is by + default turned off. + + q Quits ed. + + Q Quits ed unconditionally. This is similar to the q command, + except that unwritten changes are discarded without warning. + + ($)r file + Reads file to after the addressed line. If file is not + specified, then the default filename is used. If there was no + default filename prior to the command, then the default filename + is set to file. Otherwise, the default filename is unchanged. + The current address is set to the last line read. + + (.,.)s/re/replacement/ + (.,.)s/re/replacement/g + Replaces text in the addressed lines matching a regular + expression re with replacement. By default, only the first + match in each line is replaced. If the 'g' (global) suffix is + given, then every match to be replaced. It is an error if no + substitutions are performed on any of the addressed lines. The + current address is set to the last line affected. + + (.,.)t(.) + Copies (i.e., transfers) the addressed lines to after the right- + hand destination address, which may be the address 0 (zero). + The current address is set to the last line copied. + + (1,$)v/re/command-list + Applies command-list to each of the addressed lines not matching + a regular expression re. This is similar to the 'g' command. + + + Vn If n is specified, the visualization mode is enabled and the + number of lines printed is set to n. Otherwise, the + visualization mode it toggled on or off. The visualization mode + is a non-standard feature that prints the text in the edit + buffer before and after the current line. + + (1,$)w file + Writes the addressed lines to file. Any previous contents of + file is lost without warning. If there is no default filename, + then the default filename is set to file, otherwise it is + unchanged. If no filename is specified, then the default + filename is used. The current address is unchanged. + + (.)x Copies (puts) the contents of the cut buffer to after the + addressed line. The current address is set to the last line + copied. + + (.,.)y Copies (yanks) the addressed lines to the cut buffer. The cut + buffer is overwritten by subsequent 'y', 'j', 'd', or 'c' + commands. The current address is unchanged. + + (.+1)zn Scrolls n lines at a time starting at addressed line. If n is + not specified, then the current window size is used. The + current address is set to the last line printed. + + ($)= Prints the line number of the addressed line. + + (.+1)newline + Prints the addressed line, and sets the current address to that + line. + +SEE ALSO + B. W. Kernighan and P. J. Plauger, Software Tools in Pascal , Addison- + Wesley, 1981. + +LIMITATIONS + ed is limited to 200 characters per line. If an input line is greater + than 200 characters it will be broken up into multiple lines. The input + file must terminate lines with a newline (LF) character. Files will be + written using the UNIX LF line termination. The size of the file is + limited by the amount of buffer memory available to ed. If ed + encounters an error in a command line it will print '?' and return to + command mode. Attempting to quit ed or edit another file before writing + a modified buffer results in an error. If the command is entered a + second time, it succeeds, but any changes to the buffer are lost. + +AUTHOR + Dave Hein + +COPYRIGHT + Copyright (c) 2011, 2012, Dave Hein + MIT License (See license.txt in the root directory) + This is free software: you are free to change and redistribute it. + There is no warranty, to the extent permitted by law. + + +SPINIX utility March 2012 ED(1) diff --git a/manpages/edhelp.txt b/manpages/edhelp.txt new file mode 100644 index 0000000..fa789fe --- /dev/null +++ b/manpages/edhelp.txt @@ -0,0 +1,44 @@ +ED Commands + +Line Addresses +n - nth line +. - current line +$ - last line +- - previous line +-n - nth previous line ++ - next line ++n - nth next line +n,m - lines n through m +, - first through last lines +; - current through last lines +/str/ - next line containing str +?str? - previous line containing str + +Editing Commands +(.)a - append text after the addressed line +(.,.)c - replace the addressed lines with text +(.,.)d - delete the addressed lines +e file - discard all lines and edit the specified file +E file - unconditionally discard all lines and edit specified file +f file - set the current file name and print the name +(1,$)g/str/cmd - execute a command on lines containing str +h - print help information +(.)i - insert text before the addressed line +(.,.+1)j - join the addressed lines +(.,.)l - print the addressed lines unambiguously +(.,.)m(.) - move the addressed lines to after the right-hand address +(.,.)n - print the addresses lines with line numbers +(.,.)p - print the addresses lines +P - toggle the prompt mode +q - quit +($)r file - read the specified file after the addressed line +(.,.)s/st1/st2/g - substitute occurances of st1 with st2 +(.,.)t(.) - copy the addressed lines to after the right-hand address +(1,$)v/str/cmd - execute a command on lines not containing str +Vn - set visualization mode to n lines or toggle on/off if no n +(1,$)w file - write the addressed lines to the specified file +(.)x - insert lines from the cut buffer after the addressed line +(.,.)y - yank the addressed lines to the cut buffer +(.+1)zn - scroll and print n lines from the addressed line +($)= - print the line number of the addressed line +(.+1) - print the addressed line diff --git a/manpages/export.txt b/manpages/export.txt new file mode 100644 index 0000000..e036fdd --- /dev/null +++ b/manpages/export.txt @@ -0,0 +1,29 @@ +EXPORT(1) User Commands EXPORT(1) + +NAME + export - export one or more environment variables + +SYNOPSIS + export [name[=[value]]...] + +DESCRIPTION + If no parameters are specified, export will print all of the exported + environment variables. If one or more varible are supplied they will + become exported variables. If the variable had been previously + declared as a non-exported variable it will be exported with its + current value unless the "=" character is included. If a value is + specified after the "=" character it will become the new value of the + variable. If no value follows the "=" character the variable will be + assigned a NULL value. + +AUTHOR + Dave Hein + +COPYRIGHT + Copyright (c) 2013, Dave Hein + MIT License (See license.txt in the root directory) + This is free software: you are free to change and redistribute it. + There is no warranty, to the extent permitted by law. + + +SPINIX utility March 2013 EXPORT(1) diff --git a/manpages/getmod.txt b/manpages/getmod.txt new file mode 100644 index 0000000..a756082 --- /dev/null +++ b/manpages/getmod.txt @@ -0,0 +1,22 @@ +GETMOD(1) User Commands GETMOD(1) + +NAME + getmod - print the mode of a file + +SYNOPSIS + getmode [FILE] + +DESCRIPTION + Print the mode of FILE. + +AUTHOR + Dave Hein + +COPYRIGHT + Copyright (c) 2011, 2012, Dave Hein + MIT License (See license.txt in the root directory) + This is free software: you are free to change and redistribute it. + There is no warranty, to the extent permitted by law. + + +SPINIX utility March 2012 GETMOD(1) diff --git a/manpages/halt.txt b/manpages/halt.txt new file mode 100644 index 0000000..3994b71 --- /dev/null +++ b/manpages/halt.txt @@ -0,0 +1,23 @@ +HALT(1) User Commands HALT(1) + +NAME + halt - halt the system + +SYNOPSIS + halt + +DESCRIPTION + All of the precesses are stopped, and the processor is put in an idle + state. + +AUTHOR + Dave Hein + +COPYRIGHT + Copyright (c) 2011, 2012, Dave Hein + MIT License (See license.txt in the root directory) + This is free software: you are free to change and redistribute it. + There is no warranty, to the extent permitted by law. + + +SPINIX utility March 2012 HALT(1) diff --git a/manpages/head.txt b/manpages/head.txt new file mode 100644 index 0000000..2e363f3 --- /dev/null +++ b/manpages/head.txt @@ -0,0 +1,26 @@ +HEAD(1) User Commands HEAD(1) + +NAME + head - print the beginning lines of a file + +SYNOPSIS + head [OPTION] [FILE] + +DESCRIPTION + The first 10 lines of FILE are printed to the standard output if OPTION + is not present. A different number of lines can be specified with a + "-n" OPTION, where n is the postivie number. The standard input is used + if FILE is not specified. In the case of the standard input, head will + terminate after reading "n" lines. + +AUTHOR + Dave Hein + +COPYRIGHT + Copyright (c) 2011, 2012, Dave Hein + MIT License (See license.txt in the root directory) + This is free software: you are free to change and redistribute it. + There is no warranty, to the extent permitted by law. + + +SPINIX utility March 2012 HEAD(1) diff --git a/manpages/history.txt b/manpages/history.txt new file mode 100644 index 0000000..2c56bca --- /dev/null +++ b/manpages/history.txt @@ -0,0 +1,25 @@ +HISTORY(1) User Commands HISTORY(1) + +NAME + history - print the shell command history + +SYNOPSIS + history [-c] [n] + +DESCRIPTION + If no parameters are specified, the history file containing the + previous shell commands will be printed out. If a number is specified + only the last "n" lines will be printed. If the -c option is specified + the history file will be deleted. + +AUTHOR + Dave Hein + +COPYRIGHT + Copyright (c) 2013 Dave Hein + MIT License (See license.txt in the root directory) + This is free software: you are free to change and redistribute it. + There is no warranty, to the extent permitted by law. + + +SPINIX utility October 2013 HISTORY(1) diff --git a/manpages/lerner.txt b/manpages/lerner.txt new file mode 100644 index 0000000..4d463d0 --- /dev/null +++ b/manpages/lerner.txt @@ -0,0 +1,53 @@ +LERNER(1) User Commands LERNER(1) + +NAME + lerner - conversational program that learns + +SYNOPSIS + lerner + +DESCRIPTION + Lerner is a stand-alone conversational program that can learn by + building a vocabulary. It supports a simple grammatical syntax + containing a subject, verb and predicate. The predicate can contain + an optional adjective. The syntax is as follows: + + [article] subject verb [article] [adjective] predicate + + Each field must consist of a single word. The articles, "a", "an" and + "the" are optional. Statements can be made questions by adding a "?" + at the end. Questions can also start with the words "who", "what", + "does", "do", "is" and "are". The question mark is optional when + starting a question with these words. + + Words must be spelled with the same capitalization as they were + defined. One exception is at the beginning of a sentence where the + first letter can be capitalized even if the word was defined in + lower-case. + + Lerner supports the following commands: + + vocab [word] - Print the words I know + dump - Print the words as hex codes + forget - Remove a word from memory + save - Save the words to EEPROM + reset - Reset the word table + basic - Reset the word table to a basic list + history - Print the short term memory + goodbye - Exit the program + help - Print this list of commands + + Lerner normally saves its vocabulary to the EEPROM. However, the save + function has been disabled in this version. + +AUTHOR + Dave Hein + +COPYRIGHT + Copyright (c) 2011, 2012, Dave Hein + MIT License (See license.txt in the root directory) + This is free software: you are free to change and redistribute it. + There is no warranty, to the extent permitted by law. + + +SPINIX Program March 2012 LERNER(1) diff --git a/manpages/let.txt b/manpages/let.txt new file mode 100644 index 0000000..20dc2d1 --- /dev/null +++ b/manpages/let.txt @@ -0,0 +1,42 @@ +LET(1) User Commands LET(1) + +NAME + let - print a manual entry + +SYNOPSIS + let name=val1[op1val2...] + +DESCRIPTION + Sets the value of the named shell variable to the value of an + arithmetic expression. The arithmetic expression is evaluated from + left to right. The allowable operators are as follows: + + >= - Greater than or equal + <= - Less than or equal + == - Equal + != - Not equal + && - Logical and + || - Logical or + >> - Arithmetic shift right + << - Shift left + + - Add + - - Subtract + * - Multiply + / - Divide + & - Bitwise and + | - Bitwise or + < - Less than + > - Greater than + % - Modulus + +AUTHOR + Dave Hein + +COPYRIGHT + Copyright (c) 2013, Dave Hein + MIT License (See license.txt in the root directory) + This is free software: you are free to change and redistribute it. + There is no warranty, to the extent permitted by law. + + +SPINIX utility March 2013 LET(1) diff --git a/manpages/linefeed.txt b/manpages/linefeed.txt new file mode 100644 index 0000000..eb38942 --- /dev/null +++ b/manpages/linefeed.txt @@ -0,0 +1,26 @@ +LINEFEED(1) User Commands LINEFEED(1) + +NAME + linefeed - enable or disable linefeeds + +SYNOPSIS + linefeed [PARM] + +DESCRIPTION + The linefeed utility is used to enable or disable line-feeds when + printing the newline character. PARM can be either "on" or "off" to + enable or disable line-feeds. + + The "config" command may also be used to perform a similar function. + +AUTHOR + Dave Hein + +COPYRIGHT + Copyright (c) 2011, 2012, Dave Hein + MIT License (See license.txt in the root directory) + This is free software: you are free to change and redistribute it. + There is no warranty, to the extent permitted by law. + + +SPINIX utility March 2012 LINEFEED(1) diff --git a/manpages/ls.txt b/manpages/ls.txt new file mode 100644 index 0000000..b201e02 --- /dev/null +++ b/manpages/ls.txt @@ -0,0 +1,29 @@ +LS(1) User Commands LS(1) + +NAME + ls - list files + +SYNOPSIS + ls [OPTIONS] [DIRECTORIES] + +DESCRIPTION + List the files in the DIRECTORIES to the standard output. If no + DIRECTORIES are specified, the current directory is listed. If OPTIONS + is not specified, only the file names will be listed. OPTIONS can be + one or more of the following: + + -l - Long list that shows the attributes and lengths + -a - Show all files including hidden files that start with . and _ + -e - Show the executable type + +AUTHOR + Dave Hein + +COPYRIGHT + Copyright (c) 2011, 2012, Dave Hein + MIT License (See license.txt in the root directory) + This is free software: you are free to change and redistribute it. + There is no warranty, to the extent permitted by law. + + +SPINIX utility March 2012 LS(1) diff --git a/manpages/man.txt b/manpages/man.txt new file mode 100644 index 0000000..830cb4c --- /dev/null +++ b/manpages/man.txt @@ -0,0 +1,23 @@ +MAN(1) User Commands MAN(1) + +NAME + man - print a manual entry + +SYNOPSIS + man [FILE] + +DESCRIPTION + Prints the manual entry for a FILE to the standard output. man uses + the same display mode as "more". + +AUTHOR + Dave Hein + +COPYRIGHT + Copyright (c) 2011, 2012, Dave Hein + MIT License (See license.txt in the root directory) + This is free software: you are free to change and redistribute it. + There is no warranty, to the extent permitted by law. + + +SPINIX utility March 2012 MAN(1) diff --git a/manpages/mkdir.txt b/manpages/mkdir.txt new file mode 100644 index 0000000..483df96 --- /dev/null +++ b/manpages/mkdir.txt @@ -0,0 +1,22 @@ +MKDIR(1) User Commands MKDIR(1) + +NAME + mkdir - make a directory + +SYNOPSIS + mkdir [DIRECTORY] + +DESCRIPTION + Creates a directory named DIRECTORY. + +AUTHOR + Dave Hein + +COPYRIGHT + Copyright (c) 2011, 2012, Dave Hein + MIT License (See license.txt in the root directory) + This is free software: you are free to change and redistribute it. + There is no warranty, to the extent permitted by law. + + +SPINIX utility March 2012 MKDIR(1) diff --git a/manpages/more.txt b/manpages/more.txt new file mode 100644 index 0000000..486d73b --- /dev/null +++ b/manpages/more.txt @@ -0,0 +1,26 @@ +MORE(1) User Commands MORE(1) + +NAME + more - display a file page by page + +SYNOPSIS + more [FILE] + +DESCRIPTION + Displays FILE page by page. The following commands are used: + + - Display next page + - Display next line + q - Quit + +AUTHOR + Dave Hein + +COPYRIGHT + Copyright (c) 2011, 2012, Dave Hein + MIT License (See license.txt in the root directory) + This is free software: you are free to change and redistribute it. + There is no warranty, to the extent permitted by law. + + +SPINIX utility March 2012 MORE(1) diff --git a/manpages/mv.txt b/manpages/mv.txt new file mode 100644 index 0000000..942d3ce --- /dev/null +++ b/manpages/mv.txt @@ -0,0 +1,22 @@ +ECHO(1) User Commands ECHO(1) + +NAME + mv - move a file + +SYNOPSIS + mv [OLDNAME] [NEWNAME] + +DESCRIPTION + Moves a file from OLDNAME to NEWNAME. + +AUTHOR + Dave Hein + +COPYRIGHT + Copyright (c) 2011, 2012, Dave Hein + MIT License (See license.txt in the root directory) + This is free software: you are free to change and redistribute it. + There is no warranty, to the extent permitted by law. + + +SPINIX utility March 2012 ECHO(1) diff --git a/manpages/od.txt b/manpages/od.txt new file mode 100644 index 0000000..59ecf03 --- /dev/null +++ b/manpages/od.txt @@ -0,0 +1,26 @@ +OD(1) User Commands OD(1) + +NAME + od - octal dump + +SYNOPSIS + od [OPTION] [FILE] + +DESCRIPTION + Dumps the contents of FILE to the standard output in octal if OPTION is + not specified, or if OPTION is "-o". If OPTION is "-x" the file will + be dumped in hexadecimal, and if OPTION is "-a" it will be dumped in + ASCII. In ASCII mode, any non-printable characters will be displayed + as escacped characters. + +AUTHOR + Dave Hein + +COPYRIGHT + Copyright (c) 2011, 2012, Dave Hein + MIT License (See license.txt in the root directory) + This is free software: you are free to change and redistribute it. + There is no warranty, to the extent permitted by law. + + +SPINIX utility March 2012 OD(1) diff --git a/manpages/pfth.txt b/manpages/pfth.txt new file mode 100644 index 0000000..5edf360 --- /dev/null +++ b/manpages/pfth.txt @@ -0,0 +1,40 @@ +PFTH(1) User Commands PFTH(1) + +NAME + pfth - interpret and execute Forth programs + +SYNOPSIS + pfth [FILE]... + +DESCRIPTION + pfth is a Forth interpreter that can load Forth source files from the + SD card and execute them. One or more Forth files can be specified + on the command, which will be loaded when pfth starts up. + + pfth supports up to two simultaneous opened files. This allows one + file to be read while another one is written, or two files can be read + at the same time. This also allows including a file from another file. + + Simple versions of the ls, cat, rm and cp commands are supported under + pfth. The Forth source files used by pfth are included in the /forth + directory. It also includes a Forth versin of the ted line editor, + some SD utilities and other sample programs. + + The user can exit pfth and return to the Spinix shell by typing the + bye command. + +LIMITATIONS + pfth processes a maximum of six command-line parameters. No more than + two files can be opened at the same time. + +AUTHOR + Dave Hein + +COPYRIGHT + Copyright (c) 2013, Dave Hein + MIT License (See license.txt in the root directory) + This is free software: you are free to change and redistribute it. + There is no warranty, to the extent permitted by law. + + +SPINIX utility August 2013 PFTH(1) diff --git a/manpages/printenv.txt b/manpages/printenv.txt new file mode 100644 index 0000000..de4dcf7 --- /dev/null +++ b/manpages/printenv.txt @@ -0,0 +1,24 @@ +PRINTENV(1) User Commands PRINTENV(1) + +NAME + printenv - print the exported environment variables + +SYNOPSIS + printenv [name...] + +DESCRIPTION + If no parameters are specified, printenv will print all of the exported + environment variables. If one or more varible names are supplied only + the values of the listed variables will be printed. + +AUTHOR + Dave Hein + +COPYRIGHT + Copyright (c) 2013, Dave Hein + MIT License (See license.txt in the root directory) + This is free software: you are free to change and redistribute it. + There is no warranty, to the extent permitted by law. + + +SPINIX utility March 2013 PRINTENV(1) diff --git a/manpages/pwd.txt b/manpages/pwd.txt new file mode 100644 index 0000000..1e0d3eb --- /dev/null +++ b/manpages/pwd.txt @@ -0,0 +1,22 @@ +PWD(1) User Commands PWD(1) + +NAME + pwd - print working directory + +SYNOPSIS + pwd + +DESCRIPTION + Prints the current working directory to the standard output. + +AUTHOR + Dave Hein + +COPYRIGHT + Copyright (c) 2011, 2012, Dave Hein + MIT License (See license.txt in the root directory) + This is free software: you are free to change and redistribute it. + There is no warranty, to the extent permitted by law. + + +SPINIX utility March 2012 PWD(1) diff --git a/manpages/rb.txt b/manpages/rb.txt new file mode 100644 index 0000000..c67025e --- /dev/null +++ b/manpages/rb.txt @@ -0,0 +1,22 @@ +RB(1) User Commands RB(1) + +NAME + rb - receive binary + +SYNOPSIS + rb + +DESCRIPTION + Receives a binary file from the serial port using the YMODEM protocol. + +AUTHOR + Dave Hein + +COPYRIGHT + Copyright (c) 2011, 2012, Dave Hein + MIT License (See license.txt in the root directory) + This is free software: you are free to change and redistribute it. + There is no warranty, to the extent permitted by law. + + +SPINIX utility March 2012 RB(1) diff --git a/manpages/reboot.txt b/manpages/reboot.txt new file mode 100644 index 0000000..56561a5 --- /dev/null +++ b/manpages/reboot.txt @@ -0,0 +1,22 @@ +REBOOT(1) User Commands REBOOT(1) + +NAME + reboot + +SYNOPSIS + reboot + +DESCRIPTION + Immediately reboots the processor. + +AUTHOR + Dave Hein + +COPYRIGHT + Copyright (c) 2011, 2012, Dave Hein + MIT License (See license.txt in the root directory) + This is free software: you are free to change and redistribute it. + There is no warranty, to the extent permitted by law. + + +SPINIX utility March 2012 REBOOT(1) diff --git a/manpages/rm.txt b/manpages/rm.txt new file mode 100644 index 0000000..9eb98b2 --- /dev/null +++ b/manpages/rm.txt @@ -0,0 +1,22 @@ +RM(1) User Commands RM(1) + +NAME + rm - remove a file + +SYNOPSIS + rm [FILE] + +DESCRIPTION + Remove FILE. + +AUTHOR + Dave Hein + +COPYRIGHT + Copyright (c) 2011, 2012, Dave Hein + MIT License (See license.txt in the root directory) + This is free software: you are free to change and redistribute it. + There is no warranty, to the extent permitted by law. + + +SPINIX utility March 2012 RM(1) diff --git a/manpages/sb.txt b/manpages/sb.txt new file mode 100644 index 0000000..145eae4 --- /dev/null +++ b/manpages/sb.txt @@ -0,0 +1,22 @@ +SB(1) User Commands SB(1) + +NAME + sb - send binary + +SYNOPSIS + sb [FILE] + +DESCRIPTION + Send FILE to the serial port using the YMODEM protocol. + +AUTHOR + Dave Hein + +COPYRIGHT + Copyright (c) 2011, 2012, Dave Hein + MIT License (See license.txt in the root directory) + This is free software: you are free to change and redistribute it. + There is no warranty, to the extent permitted by law. + + +SPINIX utility March 2012 SB(1) diff --git a/manpages/set.txt b/manpages/set.txt new file mode 100644 index 0000000..397995b --- /dev/null +++ b/manpages/set.txt @@ -0,0 +1,24 @@ +SET(1) User Commands SET(1) + +NAME + set - print the environment variables + +SYNOPSIS + set + +DESCRIPTION + set prints all of the shell variables, which include exported variables + and non-exported variables. + + +AUTHOR + Dave Hein + +COPYRIGHT + Copyright (c) 2013, Dave Hein + MIT License (See license.txt in the root directory) + This is free software: you are free to change and redistribute it. + There is no warranty, to the extent permitted by law. + + +SPINIX utility March 2013 SET(1) diff --git a/manpages/shell.txt b/manpages/shell.txt new file mode 100644 index 0000000..46c5f44 --- /dev/null +++ b/manpages/shell.txt @@ -0,0 +1,101 @@ +SHELL(1) User Commands SHELL(1) + +NAME + shell - Spinix shell program + +SYNOPSIS + shell + +DESCRIPTION + Provides the command-line interface for the Spinix operating system. + The shell program can execute the following commands: + + pwd - print the current working directory + cd - change working directory + echo - echo the command line parameters + FILE - execute FILE + + Executable files can be one of the following: + + - Spinix Spin application + - Spinix C application + - Stand-alone C or Spin program + - script file + + Spinix examines the first long of a file to determine what type of + executable it is. A Spin app contains "SPIN" and a C app contains + "CAPP". C apps must use the LMM memory model. + + A stand-alone C or Spin program is signalled by a clock frequency + between 80 and 120 MHz. Spinix will modify the clock frequency to + match the current clock frequency when the stand-along program is run. + It does not modify any other portion of the program. + + C and Spin programs must be less than or equal to 31.5K bytes since the + spinix shared memory is located in the top 512 bytes of memory. + Program files must consist of contiguous sectors, since the loader does + not implement cluster chaining. This can be ensured by using 32K + clusters. + + Standard I/O can be redirected by using the ">", "<" and ">>" comand- + line modifiers. + + The first line of a script file must start with "#shell". Script file + can call another script file up to 26 levels deep. + + Shell script files support the following commands: + + if - conditionally execute based on following condition + then - must be on the next line after an "if" line + fi - terminates the body of an if statment + while - begin conditional loop based on following condition + do - must be on the next line after a "while" line + done - terminats the body of a while statement + exit - exit the script file + + An if statement may contain an executable file, such as + + if spinit file + or + if test $a -ne 0 + + The if statement will test the return value from the executable file, + which is referenced by $?. A short-hand version of using test is to + use bracket, so that the "if test" statement above can be written as + + if [ $a -ne 0 ] + + The shell allows defining variables using the "=" operator, such as + + a=123 + + There must be no spaces before or after the equal. Double quotes + can be used to assign a value containing spaces, such as + + b="This is a test" + + Variables can be accessed by prepending a "$" character to the name. + From the previous examples, a and b can be printed using echo by + typing + + echo $a $b + + Spinix provides other variables, which are $?, $#, $1, $2, etc. + $? contains the return value from the last program that has been + executed. $# contains the number of command-line parameters, and + $1, $2, etc. contain the values of the command-line parameters. + + +LIMITATIONS + +AUTHOR + Dave Hein + +COPYRIGHT + Copyright (c) 2011, 2012, 2013, Dave Hein + MIT License (See license.txt in the root directory) + This is free software: you are free to change and redistribute it. + There is no warranty, to the extent permitted by law. + + +SPINIX utility March 2012 SHELL(1) diff --git a/manpages/spasm.txt b/manpages/spasm.txt new file mode 100644 index 0000000..8ff1249 --- /dev/null +++ b/manpages/spasm.txt @@ -0,0 +1,351 @@ +SPASM(1) User Commands SPASM(1) + +NAME + spasm - spin assembler + +SYNOPSIS + spasm [-l] [-d] FILE + +DESCRIPTION + Spasm assembles the source file given by FILE. The file name may + include the extension, or .spa will be assumed if no extension is + specified. The assembled binary program will written to a file with + the same root name plus a .bin extension. If -l is specified, it will + print an assembly listing. If -d is specified, spasm will print debug + information. + + Spasm is an assembly language that defines mnemonics for Spin + bytecodes. Spin bytecodes are executed by the Spin interpreter, which + is loaded at boot time from the internal ROM. The Spin interpreter is + a stack-based virtual machine that uses the stack to store parameters + that are used by operators, such as add, sub, etc. The operators store + their results back onto the stack. There are other operators that + transfer data back and forth between the stack and hub RAM. + + The Spin bytecodes are listed below. They are separated into four + major groups -- lower, memory, math and extened operators. The lower + group contains a mix of operators, including those that handle program + flow, lookup, lookdown, case, and several other miscellaneous functions. + The math operators implement the various math and logic functions that + use two arguments. + + The memory operators implement load, store, execute and address + functions. A load operation reads data from the hub RAM and pushes it + onto the stack. A store operation pops a value off the stack and + stores it in RAM. The address operator is used to push the absolute + address of a hub RAM location onto the stack. + + The execute operation is used to execute an operation directly on a RAM + location. The result can be optionally pushed to the stack. The + operations that can be executed directly on a hub RAM location include + the standard 32 math operations plus a small number of extended + operations. The extended operators include pre and post increment and + decrement, sign extension and the random function. + + The format of the memory mnemonics is as follows: + + + operation = {ld, st, ex, la}, + size = {b, w, l}, + type = {i, l, o, a, v, s} + mode = {c, x, 0, 1, m1, p} + + The operations are load, store, execute and load address as stated + earlier. The size refers to byte, word and long. The types are + immediate, local, object, absolute, variable and stack. The modes + are compact, indexed, zero, one, minus one and packed. + + As an example, the instruction ldwi means load-word-immediate. It + will load an immediate value onto the stack. The instruction stba + will store a byte at the absolute address residing in the stack. + + There are compact instructions that use a single byte to address + the first 8 long values residing in the method's stack frame or in + an object's variable space. These use the size, type and mode + characters "llc". As an example, the method result value can be + set with the "stllc 0" instruction. The fourth long in the objet's + variable section could be loaded with "ldllc 12". + + When an execute memory operation is specified it is followed by one of + the math operators or an extended operator. An execute instruction may + also specify the "load" extended operator to save the result on the + stack. Examples of using execute instructions are as follows: + + exlo $8 add ' Add value on stack to the object long at offset $8 + exwv $20 preinc ' Increment the VAR word at offset $20 + exll $10 postdec load ' Decrement stack location $10 and load + + Spasm also includes psuedo-ops, which are shortened versions of the + memory mnenomics. The psuedo-ops are made up only of the operation + and size fields, plus the "x" charater if it is indexed. The + psuedo-ops are mapped to explicit opcodes depending on the context + of the operand. As an example "ldl 1" maps into "ldli1", and + "ldl result" maps into "ldllc 0". + + The Spasm opcodes are listed below. The offsets used for the jump + instructions are signed offsets that are relative to the address of the + next instruction. A "jmp 0" instruction will just execute the next + instruction. + + Signed offsets are encoded in either one or two bytes depending + on the value of the most significant bit in the first byte. If the + MSB is zero the remaining seven bits are treated as a signed 7-bit + number in the range from -64 to 63. If the MSB is non-zero, the + remaining seven bits are used as the most significant bits of a 15-bit + signed number, and the next byte provides the eight least sigficant + bits. The 15-bit signed offset has a range from -16384 to 16383. + + The memory opcodes use an unsigned offset that can also be encoded in + one or two bytes. The MSB is used to determine whether it is one or + two bytes just like for signed offset. However, since it is unsigned, + the range for the one-byte offset is 0 to 128, and for two bytes it is + 0 to 32768. + + Lower Opcodes + ------------- + 00 ldfrmr - Load call frame with return value required + 01 ldfrm - Load call frame + 02 ldfrmar - Load call frame with abort trap & return value + 03 ldfrma - Load call frame with abort trap + 04 jmp offset - Jump to signed object offset + 05 call meth - Call method + 06 callobj meth, obj - Call method in object + 07 callobjx meth, obj - Call method in object with index + 08 tjz offset - Test and jump if zero + 09 djnz offset - Decrement and jump if not zero + 0a jz offset - Jump if zero + 0b jnz offset - Jump if not zero + 0c casedone - Case done without a match + 0d casevalue - Execute if value matches case value + 0e caserange - Execute if case value within range + 0f lookdone - Look up/down done without a match + 10 lookupval - + 11 lookdnval + 12 lookuprng + 13 lookdnrng + 14 pop - Discard N bytes from the stack + 15 run - Prepare new spin cog stack for execution + 16 strsize - Determine the size of a string + 17 strcomp - Compare two strings + 18 bytefill - Fill memory with a constant byte value + 19 wordfill - Fill memory with a constant word value + 1a longfill - Fill memory with a constant long value + 1b waitpeq - Wait till pins are equal to a value + 1c bytemove - Copy bytes to a new location + 1d wordmove - Copy words to a new location + 1e longmove - Copy longs to a new location + 1f waitpne - Wait till pins are not equal to value + 20 clkset - Change the clock frequency and mode + 21 cogstop - Stop the specified cog + 22 lockret - Return a lock + 23 waitcnt - Wait for cnt to equal a specified value + 24 ldlsx + 25 stlsx + 26 exlsx + 27 waitvid - Wait on video + 28 coginitret - Start a cog and return the cog number + 29 locknewret - Allocate a lock and return the lock number + 2a locksetret - Set a lock and return the previous value + 2b lockclrret - Clear a lock and return the previous value + 2c coginit - Start a cog + 2d locknew - Allocate a lock with no return value + 2e lockset - Set a lock with no return value + 2f lockclr - Clear a lock with no return value + 30 abort - Perform an abort to the next abort trap + 31 abortval - Perform an abort and return a value + 32 ret - Return without loading a return value + 33 retval - Return and load a return value on the stack + 34 ldlim1 - Load a minus 1 + 35 ldli0 - Load zero + 36 ldli1 - Load 1 + 37 ldlip value - Load a packed-byte constant + 38 ldbi value - Load a single-byte constant + 39 ldwi value - Load a two-byte constant + 3a ldmi value - Load a three-byte constant + 3b ldli value - Load a four-byte constant + 3c --- - Unused opcode + 3d ldregbit - Load a bit from a register + 3d stregbit - Store a bit to a register + 3e ldregbits - Load bits from a register + 3e stregbits - Store bit to a register + 3f ldreg - Load a register + 3f streg - Store a register + + Compact Memory Opcodes + -------------- + 40 ldlvc offset - Load long variable compact + 41 stlvc offset - Store long variable compact + 42 exlvc offset - Execute on a long variable compact + 43 lalvc offset - Load address of a long variable compact + 60 ldllc offset - Load long local compact + 61 stllc offset - Store long local compact + 62 exllc offset - Execute on a long local compact + 63 lallc offset - Load address of a long local compact + + Byte Memory Opcodes + -------------- + 80 ldba - Load byte absolute + 81 stba - Store byte absolute + 82 exba - Execute on a byte absolute + 83 laba - Load address of a byte absolute + 84 ldbo offset - Load byte object offset + 85 stbo offset + 86 exbo offset + 87 labo offset + 88 ldbv offset - Load byte variable offset + 89 stbv offset + 8a exbv offset + 8b labv offset + 8c ldbl offset - Load byte local offset + 8d stbl offset + 8e exbl offset + 8f labl offset + 90 ldbax - Load byte absolute with index + 91 stbax + 92 exbax + 93 labax + 94 ldbox offset - Load byte object offset with index + 95 stbox offset + 96 exbox offset + 97 labox offset + 98 ldbvx offset - Load byte variable offset with index + 99 stbvx offset + 9a exbvx offset + 9b labvx offset + 9c ldblx offset - Load byte local offset with index + 9d stblx offset + 9e exblx offset + 9f lablx offset + + Word Memory Opcodes + ------------------- + a0 ldwa - Load word absolute + a1 stwa + a2 exwa + a3 lawa + a4 ldwo offset + a5 stwo offset + a6 exwo offset + a7 lawo offset + a8 ldwv offset + a9 stwv offset + aa exwv offset + ab lawv offset + ac ldwl offset + ad stwl offset + ae exwl offset + af lawl offset + b0 ldwax - Load word absolute with index + b1 stwax + b2 exwax + b3 lawax + b4 ldwox offset + b5 stwox offset + b6 exwox offset + b7 lawox offset + b8 ldwvx offset + b9 stwvx offset + ba exwvx offset + bb lawvx offset + bc ldwlx offset + bd stwlx offset + be exwlx offset + bf lawlx offset + + Long Memory Opcodes + ------------------- + c0 ldla - Load long absolute + c1 stla + c2 exla + c3 lala + c4 ldlo offset + c5 stlo offset + c6 exlo offset + c7 lalo offset + c8 ldlv offset + c9 stlv offset + ca exlv offset + cb lalv offset + cc ldll offset + cd stll offset + ce exll offset + cf lall offset + d0 ldlax - Load long absolute with index + d1 stlax + d2 exlax + d3 lalax + d4 ldlox offset + d5 stlox offset + d6 exlox offset + d7 lalox offset + d8 ldlvx offset + d9 stlvx offset + da exlvx offset + db lalvx offset + dc ldllx offset + dd stllx offset + de exllx offset + df lallx offset + + Math Opcodes + ------------ + e0 ror - Rotate right + e1 rol - Rotate left + e2 shr - Shift right + e3 shl - Shift left + e4 min - Maximum + e5 max - Minimum + e6 neg - Negate + e7 com - Compliment + e8 and - Bitwise and + e9 abs - Absolute value + ea or - Bitwise or + eb xor - Bitwise exclusive or + ec add - Add + ed sub - Subtract + ee sar - Shift arithmetic right + ef rev - Bit reverse + f0 andl - Logical and + f1 encode - Shift "1" left + f2 orl - Logical or + f3 decode - Find left-most "1" bit + f4 mul - Multiply + f5 mulh - Multiply high + f6 div - Divide + f7 mod - Modulus + f8 sqrt - Square root + f9 cmplt - Less than + fa cmpgt - Greater than + fb cmpne - Not equal + fc cmpeq - Equal + fd cmple - Less than or equal + fe cmpge - Greater than or equal + ff notl - Logical not + + Extended opcodes + ---------------- + 00 load - Load the value + 02 repeat - Repeat index from first to last + 06 repeats - Repeat index from first to last with step + 08 randf - Forward random number + 0c randr - Reverse random number + 10 sexb - Sign extend byte + 14 sexw - Sign extend word + 18 postclr - Post clear to zero + 1c postset - Post set to all ones + 26 preinc - Pre-increment + 2e postinc - Post-increment + 36 predec - Pre-decrement + 3e postdec - Post-decrement + +AUTHOR + Dave Hein + +COPYRIGHT + Copyright (c) 2011, 2012, Dave Hein + MIT License (See license.txt in the root directory) + This is free software: you are free to change and redistribute it. + There is no warranty, to the extent permitted by law. + + +SPASM March 2012 SPASM(1) diff --git a/manpages/spc.txt b/manpages/spc.txt new file mode 100644 index 0000000..922104a --- /dev/null +++ b/manpages/spc.txt @@ -0,0 +1,26 @@ +SPC(1) User Commands SPC(1) + +NAME + spc - spinix compiler collection + +SYNOPSIS + spc FILE + +DESCRIPTION + spc is a script file that compiles a Spin program into a spinix + executable by running the spinit, spasm and splink utilities. At each + step, spc will check to see if the step ran correctly before starting + the next one. In the final step, spc will link the compiled program + with the clibsd object to produce a spinix executable. + +AUTHOR + Dave Hein + +COPYRIGHT + Copyright (c) 2011, 2012, 2013, Dave Hein + MIT License (See license.txt in the root directory) + This is free software: you are free to change and redistribute it. + There is no warranty, to the extent permitted by law. + + +SPC November 2013 SPC(1) diff --git a/manpages/spinit.txt b/manpages/spinit.txt new file mode 100644 index 0000000..33a7e35 --- /dev/null +++ b/manpages/spinit.txt @@ -0,0 +1,33 @@ +SPINIT(1) User Commands SPINIT(1) + +NAME + spinit - spin compiler + +SYNOPSIS + spinit [-d] FILE + +DESCRIPTION + spinit compiles a Spin program, and generates spasm code. spinit + handles a subset of the Spin language. It dese not compile PASM code, + and it does not implement the following Spin keywords: + + CASE, LOOKUP, LOOKUPZ, LOOKDOWN, LOOKDOWNZ, VAR, CONSTANT + FROM, TO, STEP, FLOAT, ROUND, TRUNC + + spinit also does not allow arrays to be defined. + + If the -d option is specified, spinit will print out debug information + as it compiles. + +AUTHOR + Dave Hein + +COPYRIGHT + Copyright (c) 2011, 2012, 2013, Dave Hein + MIT License (See license.txt in the root directory) + This is free software: you are free to change and redistribute it. + There is no warranty, to the extent permitted by law. + + +SPINIT November 2013 SPINIT(1) + diff --git a/manpages/spinix.txt b/manpages/spinix.txt new file mode 100644 index 0000000..049d43b --- /dev/null +++ b/manpages/spinix.txt @@ -0,0 +1,37 @@ +SPINIX(1) Spinix OS SPINIX(1) + +NAME + spinix - spinix operating system + +DESCRIPTION + Spinix is a linux-like operating system that runs on a Parallax + Propeller processor interfaced to an SD card. It consists of a boot + program, shell and many applications. + + The boot program resides in the Propeller boot EEPROM. When run for + the first time, the boot program prompts the user for the I/O pins used + to interface to the SD card. The boot program then starts up the shell + program. + + Spinix maintains a rendevous area of memory at the top of memory. The + rendevous area contains mail boxes that are used to talk to the + console and SD driver. + + The shell provides a user interface that accepts commands from the + user, and executes programs that are store on the SD card. + +SEE ALSO + For more information see the man pages on the boot program and the + shell. + +AUTHOR + Dave Hein + +COPYRIGHT + Copyright (c) 2013 Dave Hein + MIT License (See license.txt in the root directory) + This is free software: you are free to change and redistribute it. + There is no warranty, to the extent permitted by law. + + +SPINIX OS September 2013 SPINIX(1) diff --git a/manpages/splink.txt b/manpages/splink.txt new file mode 100644 index 0000000..a140a73 --- /dev/null +++ b/manpages/splink.txt @@ -0,0 +1,30 @@ +SPLINK(1) User Commands SPLINK(1) + +NAME + splink - spin linker + +SYNOPSIS + splink FILE1 FILE2 FILE3 [-d] + +DESCRIPTION + splink links FILE1 with FILE2 and writes the results to FILE3. FILE1 + is the top object file, and it must contain only one Spin object. The + second file may contain one or more objects. The objects listed in the + OBJ section of the top object must be in the same order as those in the + second file. + +LIMITATIONS + splink does not support VAR sections. + +AUTHOR + Dave Hein + +COPYRIGHT + Copyright (c) 2011, 2012, 2013, Dave Hein + MIT License (See license.txt in the root directory) + This is free software: you are free to change and redistribute it. + There is no warranty, to the extent permitted by law. + + +SPLINK November 2013 SPLINK(1) + diff --git a/manpages/tail.txt b/manpages/tail.txt new file mode 100644 index 0000000..08568e6 --- /dev/null +++ b/manpages/tail.txt @@ -0,0 +1,23 @@ +TAIL(1) User Commands TAIL(1) + +NAME + tail - display a file tail + +SYNOPSIS + tail [OPTIONS] [FILE] + +DESCRIPTION + Display the last 10 lines of a file to the standard output if OPTION is + not specified. An OPTION of "-n" will display the last n lines. + +AUTHOR + Dave Hein + +COPYRIGHT + Copyright (c) 2011, 2012, Dave Hein + MIT License (See license.txt in the root directory) + This is free software: you are free to change and redistribute it. + There is no warranty, to the extent permitted by law. + + +SPINIX utility March 2012 TAIL(1) diff --git a/manpages/tar.txt b/manpages/tar.txt new file mode 100644 index 0000000..0a70b99 --- /dev/null +++ b/manpages/tar.txt @@ -0,0 +1,24 @@ +TAR(1) User Commands TAR(1) + +NAME + tar - process a file archive + +SYNOPSIS + tar [OPTION] [FILE] [FILELIST] + +DESCRIPTION + Create a tar file named FILE if OPTION is "-cf". FILELIST is a list of + files to be added to the tar file. If OPTION is "-xf", files are + extracted from the tar file, FILE. FILELIST is not used in this case. + +AUTHOR + Dave Hein + +COPYRIGHT + Copyright (c) 2011, 2012, Dave Hein + MIT License (See license.txt in the root directory) + This is free software: you are free to change and redistribute it. + There is no warranty, to the extent permitted by law. + + +SPINIX utility March 2012 TAR(1) diff --git a/manpages/ted.txt b/manpages/ted.txt new file mode 100644 index 0000000..60093a2 --- /dev/null +++ b/manpages/ted.txt @@ -0,0 +1,46 @@ +TED(1) User Commands TED(1) + +NAME + ted - tiny text editor + +SYNOPSIS + ted - [FILE] + +DESCRIPTION + Edits the file named FILE. ted is a subset of the ed text editor It + uses single letter commands that may be prefixed by a line number or a + range of line numbers. The "." character represents the current line, + and the "$" character is the last line number. A range is specifed by + two line numbers seperated by a comma, such as "1,10" or ".,$". + + The commands are: + q - quit + w - write edit buffer to FILE + d - delete the current line + i - insert lines after the current line + a - append lines before the current line + p - print lines + n - print lines with line number + j - join the current line with the next line + # - move to line # + . - current line + $ - move to last line + - - move down one line + + - move up one line + h - print help information + +LIMITATIONS + A file name must be specified when starting ted or it will write a + blank file name when using the "w" command. + +AUTHOR + Dave Hein + +COPYRIGHT + Copyright (c) 2011, 2012, Dave Hein + MIT License (See license.txt in the root directory) + This is free software: you are free to change and redistribute it. + There is no warranty, to the extent permitted by law. + + +SPINIX utility March 2012 TED(1) diff --git a/manpages/test.txt b/manpages/test.txt new file mode 100644 index 0000000..0d56c81 --- /dev/null +++ b/manpages/test.txt @@ -0,0 +1,40 @@ +TEST(1) User Commands TEST(1) + +NAME + test - return the result of a logical expression + +SYNOPSIS + test [parm1] operator parm2 + +DESCRIPTION + Test will evaluate the expression given by the command-line arguments, + and return 0 if the result is true, or 1 if the result is false. The + operators are as follows: + + -e parm2 - Named file exists + -f parm2 - Is a regulare file + -d parm2 - Is a directory + parm1 = parm2 - Determine if the two strings are equal + parm1 != parm2 - Determine if the two strings are not equal + parm1 < parm2 - Determine if the parm1 is alphabetically before parm2 + parm1 > parm2 - Determine if the parm1 is alphabetically after parm2 + -z parm2 - Test if parm2 is null + -n parm2 - Test if parm2 is not null + parm1 -eq parm2 - parm1 is numerically equal to parm2 + parm1 -ne parm2 - parm1 is numerically not equal to parm2 + parm1 -gt parm2 - parm1 is numerically greater than parm2 + parm1 -ge parm2 - parm1 is numerically greater than or equal to parm2 + parm1 -lt parm2 - parm1 is numerically less than parm2 + parm1 -le parm2 - parm1 is numerically less than or equal to parm2 + +AUTHOR + Dave Hein + +COPYRIGHT + Copyright (c) 2013 Dave Hein + MIT License (See license.txt in the root directory) + This is free software: you are free to change and redistribute it. + There is no warranty, to the extent permitted by law. + + +SPINIX utility October 2013 TEST(1) diff --git a/manpages/timezone.txt b/manpages/timezone.txt new file mode 100644 index 0000000..e4e88f7 --- /dev/null +++ b/manpages/timezone.txt @@ -0,0 +1,27 @@ +TIMEZONE(1) User Commands TIMEZONE(1) + +NAME + timezone - display or set the system time zone + +SYNOPSIS + timezone [-s time_zone] + +DESCRIPTION + If no parameters are specified the value of the system time zone is + printed. If the -s option is specified the new value for the system + time zone must be specified. The time zone is represented as an offset + for GMT. As an example, the time zone offset would be -6 for central + time in the U.S. During daylight savings, the offset would be -5 for + the central time zone. + +AUTHOR + Dave Hein + +COPYRIGHT + Copyright (c) 2013, Dave Hein + MIT License (See license.txt in the root directory) + This is free software: you are free to change and redistribute it. + There is no warranty, to the extent permitted by law. + + +SPINIX utility November 2013 TIMEZONE(1) diff --git a/manpages/touch.txt b/manpages/touch.txt new file mode 100644 index 0000000..16bcc78 --- /dev/null +++ b/manpages/touch.txt @@ -0,0 +1,23 @@ +TOUCH(1) User Commands TOUCH(1) + +NAME + touch - update the timestamp for one or more files + +SYNOPSIS + touch FILE... + +DESCRIPTION + touch updates the modified timestamp for all the files in the file + list. The file list can include the wildcard characters * and ?. + +AUTHOR + Dave Hein + +COPYRIGHT + Copyright (c) 2013, Dave Hein + MIT License (See license.txt in the root directory) + This is free software: you are free to change and redistribute it. + There is no warranty, to the extent permitted by law. + + +SPINIX utility November 2013 TOUCH(1) diff --git a/manpages/unalias.txt b/manpages/unalias.txt new file mode 100644 index 0000000..21aac6e --- /dev/null +++ b/manpages/unalias.txt @@ -0,0 +1,23 @@ +UNALIAS(1) User Commands UNALIAS(1) + +NAME + unalias - remove an alias + +SYNOPSIS + unalias name [name ...] + +DESCRIPTION + The unalias command will remove the aliases that are specified by the + command-line parameters. + +AUTHOR + Dave Hein + +COPYRIGHT + Copyright (c) 2013 Dave Hein + MIT License (See license.txt in the root directory) + This is free software: you are free to change and redistribute it. + There is no warranty, to the extent permitted by law. + + +SPINIX utility October 2013 UNALIAS(1) diff --git a/manpages/unix2dos.txt b/manpages/unix2dos.txt new file mode 100644 index 0000000..6426ccf --- /dev/null +++ b/manpages/unix2dos.txt @@ -0,0 +1,26 @@ +UNIX2DOS(1) User Commands UNIX2DOS(1) + +NAME + unix2dos - convert from from unix to dos format + +SYNOPSIS + dos2unix [FILE] + +DESCRIPTION + The unix2dos utility converts a text file from the Unix format to the + DOS format. A line in a DOS text file is terminated by the carriage- + return and line-feed characters. A Unix file is terminated only by + the line-feed character. + + +AUTHOR + Dave Hein + +COPYRIGHT + Copyright (c) 2011, 2012, Dave Hein + MIT License (See license.txt in the root directory) + This is free software: you are free to change and redistribute it. + There is no warranty, to the extent permitted by law. + + +SPINIX utility March 2012 UNIX2DOS(1) diff --git a/manpages/unset.txt b/manpages/unset.txt new file mode 100644 index 0000000..5c31683 --- /dev/null +++ b/manpages/unset.txt @@ -0,0 +1,24 @@ +UNSET(1) User Commands UNSET(1) + +NAME + unset - remove one or more environment variables + +SYNOPSIS + unset NAME... + +DESCRIPTION + unset will remove all of the environment variables given by the name + list. + + +AUTHOR + Dave Hein + +COPYRIGHT + Copyright (c) 2013, Dave Hein + MIT License (See license.txt in the root directory) + This is free software: you are free to change and redistribute it. + There is no warranty, to the extent permitted by law. + + +SPINIX utility March 2013 UNSET(1) diff --git a/manpages/vi.txt b/manpages/vi.txt new file mode 100644 index 0000000..77017d0 --- /dev/null +++ b/manpages/vi.txt @@ -0,0 +1,357 @@ +VI(1) User Commands VI(1) + +NAME + vi - visual text editor + +SYNOPSIS + vi [file] + +DESCRIPTION + vi is a visual-oriented text editor. It is used to create, display, + modify and otherwise manipulate text files. This version of vi + implements most of the commands in the standard vi utility. It does + not allow wildcard or escape characters in the search or replacement + strings, and it cannot execute shell commands. It also does not + implement the undo command. + + vi can also run a in line-oriented mode, which is know as the ex mode. + The ex mode is entered by typing the 'Q' command. The ex mode can be + terminated, and visual-oriented editing can be resumed by typing the + 'V' command. The 'V' command has been extended to include an optional + parameter that defines the number of lines used in the visual mode. + The ex mode can also be invoked for one command by typing a ':', '/", + or '?' character. + + If invoked with a file argument, a copy of the file is read into the + editor's buffer. Changes are made to this copy and not directly to + file itself. Changes must be written back to the file before exiting + vi. + + In both the vi and ex modes, editing is done in two distinct modes: + command and input. When first invoked, vi is in command mode. In this + mode commands are used to move the cursor, scroll up or down in the + file, or delete or copy text. In the input mode, lines of text can be + inserted before or after the current line, or even within the line + itself. + + The vi commands are summarized below: + + Q - Quit from vi mode, and go into ex mode. + Y - Yank "count" lines to the copy buffer. Default is 1 line. + p - Push the lines in the copy buffer to the edit buffer. + : - Exit to the ex mode for one command. + / - Search forward for the follwing text string. + ? - Search backward for the follwing text string. + n - Move to the next line matching the last search string. + N - Move to the previous line matching the last search string. + CR - Move down one line. Same as "j". + j - Move down one line. + k - Move up one line. + h - Move left one space. + l - Move right one space. + ^F - Move forward one screen. + ^B - Move back one screen. + ^D - Move down one-half screen. + ^U - Move up one-half screen. + G - Go to the line given by "count". The default is the last line. + J - Join "count" lines to current line. The default is one line. + dd - Delete "count" lines. The default is one line. + x - Delete "count" characters. The default is one. + r - Replace a character. + O - Open up a new line above the current line, and go into insert + mode. + A - Append characters after the end of the current line. + I - Insert characters at the beginning of the current line. + i - Insert characters before the current column. + a - Append characters after the current column. + $ - Move to the last character in the line. + 0 - Move to the first character in the line. + 1-9 - Set the count to a decimal number, including zero characters. + ESC - Escape from the insert mode, or ignore previous command characters. + + In the ex mode, commands are executed on single lines, or a range of + lines. All ex commands are entered as a complete line of text, + terminated by the enter key. This version of ex only allows one + command per line. + + As with the visual mode, editing is done in either the command or input + mode. When invoked, ex is in command mode. A typical ex command might + look like: + + ,s/old/new/g + + which replaces all occurrences of the string old with new. + + When an input command, such as 'a' (append), 'i' (insert) or 'c' + (change), is given, ex enters input mode. In this mode, no commands are + available; instead, the standard input is written directly to the editor + buffer. Lines consist of text up to and including a newline character. + The input mode is terminated by entering a single period (.) on a line. + + All ex commands operate on whole lines or ranges of lines; e.g., the 'd' + command deletes lines; the 'm' command moves lines, and so on. It is + possible to modify only a portion of a line by means of replacement, as + in the example above. However even here, the 's' command is applied to + whole lines at a time. + + In general, ex commands consist of zero or more line addresses, followed + by a single character command and possibly additional parameters; i.e., + commands have the structure: + + [address [,address]]command[parameters] + + The address(es) indicate the line or range of lines to be affected by + the command. If fewer addresses are given than the command accepts, + then default addresses are supplied. + + + LINE ADDRESSING + An address represents the number of a line in the buffer. ex maintains + a current address which is typically supplied to commands as the default + address when none is specified. When a file is first read, the + current address is set to the last line of the file. In general, the + current address is set to the last line affected by a command. + + A line address is constructed from one of the bases in the list below, + optionally followed by a numeric offset. The offset may include any + combination of digits, operators (i.e. + and -). Addresses are read + from left to right, and their values are computed relative to the + current address. + + One exception to the rule that addresses represent line numbers is the + address 0 (zero). This means "before the first line," and is legal + wherever it makes sense. + + An address range is two addresses separated either by a comma or + semicolon. The value of the first address in a range cannot exceed the + value of the second. If only one address is given in a range, then the + second address is set to the given address. If only one address is + expected, then the last address is used. + + Each address in a comma-delimited range is interpreted relative to the + current address. In a semicolon-delimited range, the first address is + used to set the current address, and the second address is + interpreted relative to the first. + + + The following address symbols are recognized. + + + . The current line (address) in the buffer. + + + $ The last line in the buffer. + + + n The nth, line in the buffer where n is a number in the range + [0,$]. + + n,m Lines n through m, where n is less than or equal to m. + + - The previous line. This is equivalent to -1. + + + -n The nth previous line, where n is a non-negative number. + + + + The next line. This is equivalent to +1. + + + +n The nth next line, where n is a non-negative number. + + + % The first through last lines in the buffer. This is equivalent + to the address range 1,$. + + + ; The current through last lines in the buffer. This is + equivalent to the address range .,$. + + + /re/ The next line containing the regular expression re. The search + wraps to the beginning of the buffer and continues down to the + current line, if necessary. // repeats the last search. + + + ?re? The previous line containing the regular expression re. The + search wraps to the end of the buffer and continues up to the + current line, if necessary. ?? repeats the last search. + + + + REGULAR EXPRESSIONS + Regular expressions are patterns used in selecting text. For example, + the ex command + + g/string/ + + prints all lines containing string. Regular expressions are also used + by the 's' command for selecting old text to be replaced with new. This + version of ex does not use any wildcard or escape characters. + + COMMANDS + All ex commands are single characters, though some require additional + parameters. ex commands must fit in a single line, and cannot be + continued to another line. + + In general, only one command is allowed per line. The only exceptions + are the 'g' and 'v' commands, which can be followed by an 's', 'p', 'l' + or 'n' command. + + ex recognizes the following commands. The commands are shown together + with the default address or address range supplied if none is specified + (in parenthesis). + + (.)a Appends text to the buffer after the addressed line, which may + be the address 0 (zero). Text is entered in input mode. The + current address is set to last line entered. + + (.,.)c Changes lines in the buffer. The addressed lines are deleted + from the buffer, and text is appended in their place. Text is + entered in input mode. The current address is set to last line + entered. + + (.,.)d Deletes the addressed lines from the buffer. If there is a line + after the deleted range, then the current address is set to this + line. Otherwise the current address is set to the line before + the deleted range. + + e file Edits file, and sets the default filename. If file is not + specified, then the default filename is used. Any lines in the + buffer are deleted before the new file is read. The current + address is set to the last line read. + + e! file Edits file unconditionally. This is similar to the e command, + except that unwritten changes are discarded without warning. + The current address is set to the last line read. + + (1,$)g/re/command + Applies command to each of the addressed lines matching a + regular expression re. The current address is set to the line + currently matched before command is executed. At the end of the + 'g' command, the current address is set to the last line + affected by command. The allowable commands are 's', 'p', 'l' + and 'n'. The 'p' command is used if command is not specified. + + (.)i Inserts text in the buffer before the current line. Text is + entered in input mode. The current address is set to the last + line entered. + + (.,.+1)j + Joins the addressed lines. The addressed lines are deleted from + the buffer and replaced by a single line containing their joined + text. The current address is set to the resultant line. + + + (.,.)l Prints the addressed lines unambiguously. The current address + is set to the last line printed. + + (.,.)m(.) + Moves lines in the buffer. The addressed lines are moved to + after the right-hand destination address, which may be the + address 0 (zero). The current address is set to the new address + of the last line moved. + + (.,.)# Prints the addressed lines along with their line numbers. The + current address is set to the last line printed. + + (.,.)p Prints the addressed lines. The current address is set to the + last line printed. + + q Quits the editor. + + q! Quits the editor unconditionally. This is similar to the q + command, except that unwritten changes are discarded without + warning. + + ($)r file + Reads file to after the addressed line. If file is not + specified, then the default filename is used. If there was no + default filename prior to the command, then the default filename + is set to file. Otherwise, the default filename is unchanged. + The current address is set to the last line read. + + (.,.)s/re/replacement/ + (.,.)s/re/replacement/g + Replaces text in the addressed lines matching a regular + expression re with replacement. By default, only the first + match in each line is replaced. If the 'g' (global) suffix is + given, then every match to be replaced. It is an error if no + substitutions are performed on any of the addressed lines. The + current address is set to the last line affected. + + (.,.)t(.) + Copies (i.e., transfers) the addressed lines to after the right- + hand destination address, which may be the address 0 (zero). + The current address is set to the last line copied. + + Tn Change to the terminal mode specified by "n", or toggle the + terminal mode if "n" is not specified. A value of 0 species the + dumb terminal mode, and 1 specifies the PST terminal. + + (1,$)v/re/command-list + Applies command-list to each of the addressed lines not matching + a regular expression re. This is similar to the 'g' command. + + + Vn Enter the visualization mode, and set the number of display lines + to 'n' if specified. + + (1,$)w file + Writes the addressed lines to file. Any previous contents of + file is lost without warning. If there is no default filename, + then the default filename is set to file, otherwise it is + unchanged. If no filename is specified, then the default + filename is used. The current address is unchanged. + + (.)P Copies (puts) the contents of the cut buffer to after the + addressed line. The current address is set to the last line + copied. + + (1,$)x file + Writes the addressed lines to file and exits. Any previous + contents of file is lost without warning. If no filename is + specified, then the default filename is used. + + + (.,.)y Copies (yanks) the addressed lines to the cut buffer. The cut + buffer is overwritten by subsequent 'y', 'j', 'd', or 'c' + commands. The current address is unchanged. + + (.+1)zn Scrolls n lines at a time starting at addressed line. If n is + not specified, then the current window size is used. The + current address is set to the last line printed. + + ($)= Prints the line number of the addressed line. + + (.+1)newline + Prints the addressed line, and sets the current address to that + line. + + +OPTIONS + file Specifies the name of a file to read. The default filename is + set to the specified file. + +SEE ALSO + B. W. Kernighan and P. J. Plauger, Software Tools in Pascal , Addison- + Wesley, 1981. + +LIMITATIONS + vi is limited to 200 characters per line. If an input line is greater + than 200 characters it will be broken up into multiple lines. The input + file must terminate lines with a newline (LF) character. Files will be + written using the UNIX LF line termination. The size of the file is + limited by the amount of buffer memory available to vi. + +AUTHOR + Dave Hein + +COPYRIGHT + Copyright (c) 2011, 2012, Dave Hein + MIT License (See license.txt in the root directory) + This is free software: you are free to change and redistribute it. + There is no warranty, to the extent permitted by law. + + +SPINIX utility March 2012 VI(1) diff --git a/manpages/wc.txt b/manpages/wc.txt new file mode 100644 index 0000000..f283746 --- /dev/null +++ b/manpages/wc.txt @@ -0,0 +1,24 @@ +WC(1) User Commands WC(1) + +NAME + wc - print counts of newlines, words and bytes + +SYNOPSIS + wc [FILE]... + +DESCRIPTION + Print the counts of newlines, words and bytes for each file that is + specified. If more than one file is specified also print the totals + for all the files. + +AUTHOR + Dave Hein + +COPYRIGHT + Copyright (c) 2013, Dave Hein + MIT License (See license.txt in the root directory) + This is free software: you are free to change and redistribute it. + There is no warranty, to the extent permitted by law. + + +SPINIX utility August 2013 WC(1) diff --git a/pfth/_startup.fth b/pfth/_startup.fth new file mode 100644 index 0000000..f9692a8 --- /dev/null +++ b/pfth/_startup.fth @@ -0,0 +1,13 @@ +\ Spinix startup file +cd /forth +include bye.fth +include see.fth +include linux.fth +include procarg.fth +hostcwd zcount cd-path +1 process-arg +2 process-arg +3 process-arg +4 process-arg +5 process-arg +6 process-arg diff --git a/pfth/bufser.fth b/pfth/bufser.fth new file mode 100644 index 0000000..762ba13 --- /dev/null +++ b/pfth/bufser.fth @@ -0,0 +1,89 @@ +\ Define data space that will be used by the serial cog +create bufserstack 40 allot \ Data stack space +create bufserreturn 40 allot \ Return stack space +create bufserbuffer 32 allot \ Serial buffer +create bufserrdindex 0 , \ Read index +create bufserwrindex 0 , \ Write index + +\ This word runs in a separate cog and writes input characters into the buffer +: bufsercog + bufserbuffer + begin + getchar over bufserwrindex @ + c! + bufserwrindex dup @ 1 + 31 and swap ! + again ; + +\ This word replaces the old KEY word with one that reads from the buffer +: bufkey + bufserrdindex @ + begin + dup + bufserwrindex @ + - + until + bufserbuffer + c@ + bufserrdindex dup @ 1 + 31 and swap ! + ; + +\ This is the cog configuration structure used by the serial cog +create bufserconfig \ Forth cog config structure + ' bufsercog >body , \ Get execution token for TOGGLE + bufserstack , \ Initial value of stack ptr + bufserstack , \ Empty value for stack ptr + bufserreturn , \ Initial value of return ptr + bufserreturn , \ Empty value for return ptr + +\ This word starts a cog running the BUFSER word +: startbufsercog forth @ bufserconfig cognew ; + +\ This word starts the serial cog and sets key to the new one +: bufser startbufsercog ['] bufkey is key ; + +\ Buffer the serial input into memory until an ESC is received +: bufferit here 256 + dup + ." Buffering serial input to memory" cr + ." Enter to exit" cr + begin + key dup 27 <> + while + over c! 1+ + repeat + drop over - + \ over infile cog! +; + +\ Determine the length of the current line +: linelen ( addr count -- addr len ) + >r dup ( addr ptr ) ( left ) + begin + r@ 0 <= + if over - r> drop exit then + dup c@ dup 10 = swap 13 = or + if over - r> drop exit then + 1+ r> 1- >r + again +; + +\ Variables to store the buffer address and count +variable evalbufaddr +variable evalbufcount + +\ Evaluate a buffer containing multiple lines +: evalbuf ( addr count ) + evalbufcount ! + evalbufaddr ! + begin + evalbufcount @ 0 > + while + evalbufaddr @ evalbufcount @ + linelen + dup 1+ dup + evalbufaddr @ + evalbufaddr ! + evalbufcount @ swap - evalbufcount ! + over over type cr + evaluate + repeat +; + +\ Redefine switch to use buffered serial +\ : switch bufser ; diff --git a/pfth/bye.fth b/pfth/bye.fth new file mode 100644 index 0000000..9308052 --- /dev/null +++ b/pfth/bye.fth @@ -0,0 +1,39 @@ +hex + +create run_prog + a0bc65f0 , 08bc6e32 , 80fc6404 , 08bc7032 , 80fc6404 , 08bc6032 , 80fc6404 , + 08bc6232 , 80fc63ff , 28fc6209 , 08fc6600 , 00fc6804 , 083c6029 , 083c5c2a , + 083c582b , 08bc642b , 863c642c , 5c68000f , 80fc6001 , 80fc5d00 , 80fc5d00 , + e4fc620c , 087c6600 , 007c6804 , 04fc6a08 , 04fc6c0a , a0bc6236 , 84bc6235 , + 28fc6202 , 083c5a35 , 80fc6a04 , e4fc621d , 083c5a36 , 80fc6c04 , 083c6e36 , + 80fc6c04 , 083c7036 , 0cfc6401 , 60fc6407 , 68bc5e32 , 0c7c5e02 , 00007fd8 , + 00007fdc , 00007fd4 , 00000072 , 00000000 , 00000000 , 0007c010 , + +create arg_list 0 , 0 , 7fa8 @ , 7fc4 @ , + +: lockset ( locknum ... ) 0cfc0006 cogx1 drop ; +: lockclr ( locknum ... ) 0cfc0007 cogx1 drop ; + +: bye + + \ Close files + 7b close-file drop + 7c close-file drop + + \ Stop all the cogs except this one and the SD SPI cog + SPI_engine_cog @ 1 - + 8 0 do + dup i <> i cogid <> and + if i cogstop then + loop drop + + \ Clear and return all the locks + 8 0 do i lockclr i lockret loop + + \ Start run_prog in this cog + run_prog arg_list cogid coginit +; + +decimal + + diff --git a/pfth/cd.fth b/pfth/cd.fth new file mode 100644 index 0000000..a4f109a --- /dev/null +++ b/pfth/cd.fth @@ -0,0 +1,61 @@ +: slash-count ( addr count -- addr num ) + over swap ( addr0 addr count ) + begin + over c@ [char] / <> + over 0 > and + while + 1 /string + repeat + drop over - +; + +\ Change directory +: cd-path + \ Check if count <= 0 + dup 0 <= + if + spi_rootdir @ + spi_currdir ! + 2drop exit + then + + \ Save current directory on return stack + spi_currdir @ >r + + \ Check if absolute path or relative path + over c@ [char] / = + if + spi_rootdir @ + spi_currdir ! + 1 /string + then + + \ Loop on directory list + begin + dup 0 > + while + 2dup slash-count + dup 0= + if + ." Unexpected /" cr + r> spi_currdir ! + 2drop 2drop exit + then + dup 1+ >r + 0 open-file + if + drop + ." Could not open file" cr + r> drop + r> spi_currdir ! + 2drop exit + then + file_sector0 @ + spi_currdir ! + close-file drop + r> /string + repeat + r> drop 2drop +; + +: cd bl word count cd-path ; diff --git a/pfth/changes.txt b/pfth/changes.txt new file mode 100644 index 0000000..6e75605 --- /dev/null +++ b/pfth/changes.txt @@ -0,0 +1,84 @@ + Changes in pfth 1.02 + +1. Added ospfth.spin, which runs under Spinix + +2. Added support for two simultaneous open files + +3. Added bye and reboot words + +4. Added sub-directory support + +5. Added CD word to change the working directory + +6. Modified the s" word to work in the interpretation mode + +7. Added a kernel word to make constants and values more efficient + + + Changes in pfth 1.00 + +1. Fixed the FOR and NEXT words + +2. Fixed a bug in UNLOOP + +3. Added create-file, file-write, file-line, file-flush, file-close and + delete-file + +4. Added linux.fth that implements simple ls, rm, cp and cat commands + +5. Added sdutils.fth that contains some file utilities + +6. Added TED -- a tiny text editor + +7. Changed SAVE to EESAVE in i2c.fth + + + Changes in pfth 0.89 + +1. Changed the dictionary word structure to use 16-bit pointers instead of 32 + bits. + +2. Changed the execution list in compiled words to use 16-bit execution tokens + instead of 32 bits. + +3. Changed the positions of the code pointer and DOES pointer to make the inner + loop more efficient. It now runs about 25% faster. + +4. Changed SEE back to printing "_lit" for numbers. + +5. Implemented a _loop kernel word to make DO-LOOP faster. + + + Changes in pfth 0.83 + +1. Changed SEE in see.fth so that it doesn't print "_lit" for numbers + +2. Added bufser.fth that contains a buffered serial driver + +3. Added a BUFFERIT word in bufser.fth that reads the serial input into memory + until it encounters an "ESC" character. + +4. Added an EVALBUF word in bufser.fth that will evaluate multiple lines in a + memory buffer + +5. Updated the readme.txt file with text contributed by Loopy + +6. Updated the readme.txt file with a description of the dictionary word format + +7. Added a SAVE word to i2c.fth that will write the current hub RAM image to + EEPROM. Note: The small Spin program at the end of memory must have been + preserved for this to boot properly. + +8. Changed the CR word so that it emits both a carriage return and a line feed. + +9. Added CASE, ENDCASE, OF and ENDOF to init.fth + +10. Improved WAITCNT and MS in propwords.fth. Removed INA! + +11. Fixed LIST-FILES in sd.fth to skip over deleted files in the directory + +12. Changed the execution token so that it points to the code pointer instead + of the beginning of the word in the dictionary + +13. Made KEY a deferred word so that it can be re-assigned to the serial input + after reading the FILEs in memory diff --git a/pfth/chess.fth b/pfth/chess.fth new file mode 100644 index 0000000..ce2d922 --- /dev/null +++ b/pfth/chess.fth @@ -0,0 +1,641 @@ +\ This program was written by Lennart Benschop and converted to ANS Forth by +\ by Jeff Fox. It was further modified by Dave Hein to run under pfth. The +\ orignal source is available at http://www.ultatechnology.com/chess.html. + +HEX + +: scroll cr ; +: cls page ; +: key? 1 ; +: off false swap ! ; +: >defer 2 + w@ 4 - ; + +3 constant maxlevel +create bp0 +maxlevel 1 + c0 * allot + +variable bpv + +: bp bpv @ ; +: b@ bpv @ + c@ ; +: b! bpv @ + c! ; + +: boardvar create , + does> c@ bpv @ + ; + 0c boardvar start + 0d boardvar castlew + 0e boardvar castleb + 0f boardvar ep + 1c boardvar starting + 1d boardvar piece + 1e boardvar best + 1f boardvar farther? + 2c boardvar wlcastle? + 2d boardvar blcastle? + 2e boardvar check + 2f boardvar pawnmove + 3c boardvar kingw + 3d boardvar kingb + 3e boardvar inpassing + 3f boardvar advance + 4c boardvar valuew + 5c boardvar alfa + 6c boardvar beta + 7c boardvar (eval) + 8c boardvar highest + 9c boardvar cutoff + ac boardvar valueb + bc boardvar played + +variable level + +variable lastcnt + +: timeit cnt@ dup lastcnt @ - 80 / space . lastcnt ! ; + +: +level + \ ." +LEVEL" timeit cr + bp dup c0 + c0 cmove + c0 bpv +! 1 level +! ; + +: -level + \ ." -LEVEL" timeit cr + -c0 bpv +! -1 level +! ; + + +create symbols + + CHAR . , CHAR p , CHAR k , CHAR b , + CHAR r , CHAR q , CHAR K , + +create values + 0 , 40 , c0 , c0 , 140 , 240 , 3000 , + +: .board + \ ." .BOARD" timeit cr + cls + 0 0 at-xy 20 spaces + cr 2 spaces + [CHAR] H 1 + [CHAR] A do i emit 2 spaces loop + bp 20 + 8 0 do + cr 20 spaces + cr [CHAR] 8 i - emit + 0a 2 do space + dup i + c@ dup + 07 and cells symbols + 1 type + dup 80 and if ." W" drop else + if ." B" else ." ." then + then + loop + 10 + + loop cr drop ; + +: .pos + \ ." .POS" timeit cr + 10 /mod + swap 2 - [CHAR] A + emit + [CHAR] 8 2 + swap - emit ; + +\ constants that indicate the directions on the board +-11 constant nw -0f constant no + 0f constant zw 11 constant zo +-10 constant n 10 constant z + -1 constant w 1 constant o + +create spring +-12 , -21 , -1f , -0e , 12 , 21 , 1f , 0e , + +defer tmove + +defer attacktest + +: mine? + \ ." MINE? " depth . timeit cr + b@ dup 0= 0= swap 80 and start c@ = and ; + +variable movits + +: moveit + \ ." MOVEIT" timeit cr + starting c@ best c! 1 farther? c! + begin + best c@ over + dup best c! + dup mine? over b@ 87 = or 0= + farther? c@ and while + tmove + b@ 0= farther? c! + repeat + drop drop + 1 movits +! ; + +: Bishop + \ ." BISHOP" timeit cr + no nw zo zw moveit moveit moveit moveit ; + +: Rook + \ ." ROOK" timeit cr + n o z w moveit moveit moveit moveit ; + +: Queen + \ ." QUEEN" timeit cr + n o z w no nw zo zw 8 0 do moveit loop ; + +: Knight + \ ." KNIGHT" timeit cr + 8 0 do + i cells spring + @ + starting c@ + dup best c! + dup mine? swap b@ 87 = or 0= + if tmove then + loop ; + +: ?castle + \ ." ?CASTLE" timeit cr + start c@ 80 = if castlew else castleb then c@ check c@ 0= and ; + +: ?lcastle + \ ." ?LCASTLE" timeit cr + start c@ 80 = if wlcastle? else blcastle? then c@ check c@ 0= and ; + +: king + \ ." KING" timeit cr + n o z w no nw zo zw 8 0 do + starting c@ + dup best c! + dup mine? swap b@ 87 = or 0= + if tmove then + loop + ?castle if 28 start c@ if 70 + then + dup bp + 1- @ 0= + if + dup 1- attacktest 0= + if + best c! tmove + else drop then + else drop then + then + ?lcastle if 24 start c@ if 70 + then + dup bp + @ over bp + 1- @ or 0= + if + dup 1 + attacktest 0= + if + best c! tmove + else drop then + else drop then + then ; + +: Pawnrow + \ ." PAWNROW" timeit cr + start c@ if negate then ; + +: Pawnz + \ ." PAWNZ" timeit cr + dup best c! + f0 and start c@ if 20 else 90 then = + if 6 2 do i advance c! tmove loop + else tmove then + 0 pawnmove c! 0 inpassing c! 0 advance c! ; + +: Pawn + \ ." PAWN" timeit cr + starting c@ z Pawnrow + + dup b@ if + drop + else + dup Pawnz + z Pawnrow + dup b@ if + drop + else + starting c@ f0 and + start c@ if 80 else 30 then = + if starting c@ 0f and pawnmove c! + Pawnz + else drop + then + then + then + zw zo 2 0 do + Pawnrow starting c@ + + dup f0 and start c@ if 40 else 70 then = + over 0f and ep c@ = and + if 1 inpassing c! + dup Pawnz + then + dup b@ dup 0= 2 pick mine? or + swap 87 = or + if drop else Pawnz then + loop ; + +create pieces + + ' noop , ' Pawn , ' Knight , ' Bishop , ' Rook , ' Queen , ' king , + +: piecemove + \ ." PIECEMOVE" timeit cr +\ using above jump table for each type of piece - jump table uses , (CELLS) + piece c@ cells pieces + @ execute ; + +: ?piecemove + \ ." ?PIECEMOVE" timeit cr + starting c@ dup mine? if + b@ 07 and piece c! + 0 pawnmove c! 0 inpassing c! 0 advance c! + piecemove + else drop then ; + +: allmoves + \ ." ALLMOVES" timeit cr + [char] . emit + start c@ 0= if + 22 starting c! + 8 0 do + 8 0 do + ?piecemove starting c@ 1 + starting c! + loop + starting c@ 8 + starting c! + loop + else + 92 starting c! + 8 0 do + 8 0 do + ?piecemove starting c@ 1 + starting c! + loop + starting c@ 18 - starting c! + loop + then ; + +variable attack + +: ?attack + \ ." ?ATTACK" timeit cr + best c@ dup mine? 0= + swap b@ 07 and piece c@ = and + attack @ or attack ! ; + +: attacked? + \ ." ATTACKED?" timeit cr + attack off 0 7 1 do + i piece c! + piecemove + attack @ if drop 1 leave then + loop ; + +variable starting' +variable best' +variable start' +variable tmove' + +: settest + starting c@ starting' c! + best c@ best' c! + start c@ start' c! + ['] tmove >defer tmove' ! + ['] ?attack is tmove ; + +: po@ + starting' c@ starting c! + best' c@ best c! + start' c@ start c! + tmove' @ is tmove ; + +: changecolor + start c@ 80 xor start c! ; + +variable endf +variable playlevel +variable #legal +variable selected +variable compcolor +variable move# + +create bp1 c0 allot + +: endgame? + start c@ if valueb else valuew then @ c1 < ; + +: evalboard + valueb @ valuew @ - start c@ if negate then + 55 mine? 1 and + 56 mine? 1 and + 65 mine? 1 and + 66 mine? 1 and + + changecolor 55 mine? + 56 mine? + 65 mine? + 66 mine? + changecolor + + endgame? if + start c@ if kingb else kingw then c@ + dup f0 and dup 20 = swap 90 = or 7 and + swap 0f and dup 2 = swap 9 = or 7 and + + + then ; + +: ?check + settest + start c@ if kingw else kingb then c@ + starting c! attacked? check c! + po@ ; + +: (attacktest) + ['] tmove >defer ['] ?attack <> if + settest + starting c! + attacked? + po@ + else drop true + then ; + +' (attacktest) is attacktest + +variable seed + +: rnd + seed @ 743 * 43 + dup seed ! ; +\ 1 ; + +: domove + best c@ b@ 7 and cells values + @ negate start c@ + if valueb else valuew then +! + starting c@ b@ best c@ b! + 0 starting c@ b! + advance c@ if + advance c@ dup cells values + @ 40 - start c@ + if valueb else valueb then +! + start c@ or best c@ b! + then + piece c@ 4 = if + starting c@ 0f and 2 = + if + 0 start c@ if wlcastle? else blcastle? then c! + then + starting c@ 0f and 9 = + if + 0 start c@ if castlew else castleb then c! + then + then + piece c@ 6 = if + 0 0 start c@ if castlew else castleb then dup >r c! + r> 1f + c! + best c@ starting c@ - 2 = + if + 4 start c@ or best c@ 1- b! + 0 best c@ 1 + b! + then + best c@ starting c@ - -2 = + if + 4 start c@ or best c@ 1 + b! + 0 best c@ 2 - b! + then + best c@ start c@ if kingw else kingb then c! + then + inpassing c@ if + 0 best c@ n Pawnrow + b! + -40 start c@ if valueb else valuew then +! + then + pawnmove c@ ep c! ; + +: deeper + cutoff @ + invert if + +level + domove + ?check check c@ if -level exit then + -1 played c0 - ! + level @ playlevel @ = if + evalboard + (eval) c0 - ! + else + alfa @ highest ! + alfa @ negate beta @ negate alfa ! beta ! + changecolor + 0 played ! + allmoves + played @ 0= if + ?check check c@ if -2000 highest ! else 0 highest ! then + then + highest @ negate + (eval) c0 - ! + then + -level + (eval) @ highest @ max + highest ! + highest @ beta @ > if TRUE cutoff ! then + + + then ; + +: analyse + +level + domove + ?check check c@ 0= if + 1 #legal +! + changecolor + ['] tmove >defer + ['] deeper is tmove + 0 played ! + allmoves + is tmove + played @ 0= if + ?check check c@ if -2000 highest ! else 0 highest ! then + then + highest @ beta c0 - @ = if + rnd 2000 > if #legal @ selected ! then + then + highest @ beta c0 - @ < if + #legal @ selected ! + highest @ beta c0 - ! + then + then + -level ; + +: select + +level + domove + ?check check c@ 0= if + 1 #legal +! + #legal @ selected @ = if + bp bp1 c0 cmove + starting c@ .pos ." -" best c@ .pos space + then + then + -level ; + +: against + +level + domove + ?check check c@ 0= if + 1 #legal +! + then + -level ; + +: compmove + .board + ['] analyse is tmove + 0 #legal ! + -4000 alfa ! 4000 beta ! + +\ 0 18 at-xy cr + scroll + + \ 28 spaces + start c@ if 1 move# +! move# @ 3 .r space else 4 spaces then + ?check check c@ if ." Check" then + 1 selected ! + allmoves + #legal @ 0= if + check c@ if + ." mate" + else + ." Pat" + then + TRUE endf ! + else + ['] select is tmove + 0 #legal ! + allmoves + bp1 bp0 c0 cmove + changecolor + ['] against is tmove + 0 #legal ! + allmoves + ?check check c@ if ." Check" then + #legal @ 0= if + check c@ if + ." mate" + else + ." Pat" + then + TRUE endf ! + then + then + .board ; + +variable startingm +variable bestm +variable personmove + +: legal + \ ." LEGAL" timeit cr + startingm @ starting c@ = + bestm @ best c@ = and + personmove @ advance c@ = and + if + \ ." LEGAL 1" timeit cr + +level + domove + ?check check c@ 0= if + \ ." LEGAL 2" timeit cr + 1 #legal ! + bp bp1 c0 cmove + then + -level + \ ." LEGAL 3" timeit cr + then ; + +create inputbuf 6 allot + +: inpos + dup inputbuf + c@ [CHAR] A - + dup 8 u< + rot inputbuf + 1 + c@ [CHAR] 1 - + dup 8 u< rot and + swap 7 swap - 10 * rot + 22 + ; + +: promote + 0 6 2 do over symbols i cells + c@ = if drop i then loop ; + +: person + begin + .board + +\ 0 18 at-xy timeit cr + scroll + + \ 28 spaces + start c@ if 1 move# +! move# @ 3 .r else 3 spaces then + + inputbuf 5 expect cr + + \ [char] X emit inputbuf 5 type [char] X emit + + inputbuf c@ [CHAR] Q = if quit then + 0 inpos startingm ! + 2 inputbuf + c@ [CHAR] - = and + 3 inpos bestm ! + and + bestm @ f0 and start c@ if 20 else 90 then = + startingm b@ 07 and 1 = and + if + ." What piece? " 0 0 begin drop drop key promote dup until + personmove ! emit + else + 0 personmove ! + then + if + \ ." Trace 1" timeit cr + ['] legal is tmove + 0 #legal ! + startingm c@ starting c! ?piecemove + #legal @ + else + \ ." Trace 2" timeit cr + 0 + then + \ ." Trace 3" timeit cr + dup 0= start c@ and if -1 move# +! then + until + \ ." Trace 4" timeit cr + bp1 bp0 c0 cmove + changecolor + + cr + + \ ." Trace 5" timeit cr + .board ; + +: setmove + compcolor @ 0< start c@ 80 = = if compmove else person then ; + +variable manVsMachine + +: askcolor + manVSmachine @ + if ." Do you want White Y/N" + key dup [CHAR] Y = swap [CHAR] y = or + if 1 else -1 then compcolor ! + then ; + +: asklevel + cr ." Level? 2-" + maxlevel . key [CHAR] 0 - 2 max maxlevel min playlevel ! + cls ; + +: init + 0 level ! bp0 bpv ! + bp c0 87 fill + 4 2 3 6 5 3 2 4 8 0 do bp 22 + i + c! loop + bp 32 + 8 01 fill + bp 42 + 8 00 fill bp 52 + 8 00 fill + bp 62 + 8 00 fill bp 72 + 8 00 fill + bp 82 + 8 81 fill + 84 82 83 86 85 83 82 84 8 0 do bp 92 + i + c! loop + 1 castlew c! 1 castleb c! 0 ep c! 1 wlcastle? c! 1 blcastle? c! 0 advance c! + 80 start c! 96 kingw c! 26 kingb c! + askcolor cr asklevel + 0 move# ! 0 endf ! + 0 check c! 9c0 valuew ! 9c0 valueb ! ; + +: play + begin setmove endf @ until ; + +: games + begin init play again ; + +: autoplay + begin setmove compcolor @ negate compcolor ! key? if quit then endf @ until ; + +: auto + init -1 compcolor ! autoplay ; + +: chess + cls + ." ANS Forth Chess" cr + ." Do you want to play against the computer? Y/N" cr + begin rnd drop key? until key + dup [CHAR] Y = swap [CHAR] y = or dup manVsMachine ! + if games else auto then ; + +decimal diff --git a/pfth/chess.spin b/pfth/chess.spin new file mode 100644 index 0000000..c33160a --- /dev/null +++ b/pfth/chess.spin @@ -0,0 +1,1536 @@ +{ +############################################################################ +# PFTH - This program implements a Forth interpreter. +# +# Copyright (c) 2012, 2013 Dave Hein +# MIT Licensed +############################################################################ +} +con + _clkmode = xtal1+pll16x + _clkfreq = 80_000_000 + + ' SD Pin Definitions + 'DO = 10 + 'CLK = 11 + 'DI = 9 + 'CS = 25 + + Q = 16 ' Object Offset + + FLAG_IMMEDIATE = 1 + FLAG_CORE = $10 | $80 + FLAG_LIT = $12 | $80 + FLAG_VAR = $20 | $80 + FLAG_DEF = $00 | $80 + FLAG_JMP = $0A | $80 + FLAG_SEMI = FLAG_CORE | FLAG_IMMEDIATE + +obj + 'spi : "mount" + +'******************************************************************************* +' This Spin code waits three seconds and then starts the Forth cog +'******************************************************************************* +pub main(argc, argv) + waitcnt(clkfreq+cnt) + 'spi.mount_explicit(@spi_vars, DO, CLK, DI, CS) + coginit(cogid, @forth, @pfthconfig) + +dat +pfthconfig long @xboot_1+Q ' Initial word to execute + long @stack+Q+16 ' Starting stack pointer + long @stack+Q+16 ' Empty stack pointer value + long @retstk+Q ' Starting return pointer + long @retstk+Q ' Empty return pointer value +stack long 0[100] ' Data stack +retstk long 0[100] ' Return stack + +'******************************************************************************* +' pfth cog code +'******************************************************************************* + org 0 +forth +parm mov parm, par +parm1 rdlong pc, parm +parm2 add parm, #4 +parm3 rdlong stackptr, parm +parm4 add parm, #4 +temp rdlong stackptr0, parm +temp1 add parm, #4 +temp2 rdlong returnptr, parm +temp3 add parm, #4 +temp4 rdlong returnptr0, parm + jmp #innerloop ' Begin execution + +'******************************************************************************* +' Execute the words contained in the body of a word +' Changes parm, temp1 +'******************************************************************************* +execlistfunc add parm, #4 ' Get body from XT +innerloopcall wrlong pc, returnptr ' Push PC to return stack + add returnptr, #4 + mov pc, parm ' Set new value for PC + +'******************************************************************************* +' Get an execution token from the location pointed to by the program counter +' Increment the program counter, fetch the code pointer and jump to it +' Changes parm, temp1, pc +'******************************************************************************* +innerloop rdword parm, pc wz + if_z jmp #exitfunc + add pc, #2 + rdword temp1, parm + jmp temp1 + +pc long @xboot_1+Q ' Program Counter + +'******************************************************************************* +' Stop executing the current word, and return to the calling word +' No Changes +'******************************************************************************* +exitfunc sub returnptr, #4 + rdlong pc, returnptr + jmp #innerloop + +'******************************************************************************* +' Abort or quit execution, and return to the interpreter +' No Changes +'******************************************************************************* +abortfunc mov stackptr, stackptr0 +quitfunc mov returnptr, returnptr0 + add returnptr, #4 ' Use second entry return stack + rdlong pc, returnptr + jmp #innerloop + +'******************************************************************************* +' Push the value contained in the word's body onto the stack +' No changes +'******************************************************************************* +confunc add parm, #4 + rdlong parm1, parm + jmp #push_jmp + +'******************************************************************************* +' Push the address of the word's body onto the stack +' Execute the words pointed to by the does pointer, if non-zero +' No changes +'******************************************************************************* +varfunc mov parm1, parm + add parm1, #4 + call #push + +'******************************************************************************* +' Execute the words pointed to by the does pointer, if non-zero +' No changes +'******************************************************************************* +deferfunc add parm, #2 ' DOES> pointer + rdword parm, parm wz + if_z jmp #innerloop ' Done with varfunc + jmp #innerloopcall ' Execute DOES> code + +'******************************************************************************* +' Execute the word on the stack +' Changes parm, temp1 +'******************************************************************************* +executefunc sub stackptr, #4 + rdlong parm, stackptr + rdlong temp1, parm + jmp temp1 ' Execute code + +'******************************************************************************* +' Execute the PASM instruction on the TOS using the next value on the stack as +' the destination register data. Return the result on the stack. +' Changes parm1, parm2 +'******************************************************************************* +cogx1func call #pop2 + mov cogx1instr, parm2 + movd cogx1instr, #parm1 + nop +cogx1instr nop + jmp #push_jmp + +'******************************************************************************* +' Duplicate the top of stack +' Changes parm1 +'******************************************************************************* +dupfunc call #pop + call #push + jmp #push_jmp + +'******************************************************************************* +' Swap the top two items on the stack +' Changes parm1, parm2 +'******************************************************************************* +swapfunc call #pop2 + wrlong parm2, stackptr + add stackptr, #4 + jmp #push_jmp + +'******************************************************************************* +' Get the next word from the input buffer using the delimiter from the stack +' Changes parm, parm1, parm2, temp1, temp2 +'******************************************************************************* +wordfunc sub stackptr, #4 + rdlong parm, stackptr + call #word_del + mov temp1, #1 + shl temp1, #15 + sub temp1, parm2 + sub temp1, #1 + wrlong temp1, stackptr + add stackptr, #4 + wrbyte parm2, temp1 + cmps parm2, #0 wc, wz + if_c_or_z jmp #innerloop +:loop add temp1, #1 + rdbyte temp2, parm1 + add parm1, #1 + wrbyte temp2, temp1 + djnz parm2, #:loop + jmp #innerloop + +'******************************************************************************* +' Find the word specfied on the stack in the dictionary +' Changes parm1, parm2, temp4 +'******************************************************************************* +findfunc call #pop + mov temp4, parm1 + add parm1, #1 + rdbyte parm2, temp4 + call #findword + mov parm1, parm wz + if_z jmp #findfunc1 + call #link2xt + call #push + add parm, #2 ' Point to flag byte + rdbyte parm1, parm + and parm1, #1 ' Check immediate bit + shl parm1, #1 + sub parm1, #1 ' Return 1 if set, -1 if not + call #push + jmp #innerloop +findfunc1 mov parm1, temp4 + call #push + mov parm1, #0 + call #push + jmp #innerloop + +'******************************************************************************* +' Send the character from the stack to the output port +' Changes parm +'******************************************************************************* +emitfunc call #pop + mov parm, parm1 + call #putch + jmp #innerloop + +'******************************************************************************* +' Get a character from the input port and put it on the stack +' Changes parm, parm1 +'******************************************************************************* +getcharfunc call #getch + mov parm1, parm + jmp #push_jmp + +'******************************************************************************* +' Get a character from the files stored in memory and put it on the stack +' Changes parm1 +'******************************************************************************* +getfcharfunc rdbyte parm1, infileptr + add infileptr, #1 + jmp #push_jmp + +'******************************************************************************* +' Get an address and value from the stack, and store the value at the address +' No changes +'******************************************************************************* +storefunc call #pop2 + wrlong parm1, parm2 + jmp #innerloop + +'******************************************************************************* +' Fetch a value from the address specified on the stack, and put it on the stack +' Changes parm1 +'******************************************************************************* +fetchfunc call #pop + rdlong parm1, parm1 + jmp #push_jmp + +'******************************************************************************* +' Get an address and word from the stack, and store the word at the address +' No changes +'******************************************************************************* +wstorefunc call #pop2 + wrword parm1, parm2 + jmp #innerloop + +'******************************************************************************* +' Fetch a word from the address specified on the stack, and put it on the stack +' Changes parm1 +'******************************************************************************* +wfetchfunc call #pop + rdword parm1, parm1 + jmp #push_jmp + +'******************************************************************************* +' Get an address and byte from the stack, and store the byte at the address +' No changes +'******************************************************************************* +cstorefunc call #pop2 + wrbyte parm1, parm2 + jmp #innerloop + +'******************************************************************************* +' Fetch a byte from the address specified on the stack, and put it on the stack +' Changes parm1 +'******************************************************************************* +cfetchfunc call #pop + rdbyte parm1, parm1 + jmp #push_jmp + +'******************************************************************************* +' Add two values from the stack, and write the result back to the stack +' Changes parm1, parm2 +'******************************************************************************* +plusfunc call #pop2 + add parm1, parm2 + jmp #push_jmp + +'******************************************************************************* +' Subtract two values from the stack, and write the result back to the stack +' Changes parm1, parm2 +'******************************************************************************* +minusfunc call #pop2 + sub parm1, parm2 + jmp #push_jmp + +'******************************************************************************* +' Multiply two values from the stack, and write the result back to the stack +' Changes parm1, parm2 +'******************************************************************************* +multfunc call #pop2 + call #multiply + jmp #push_jmp + +'******************************************************************************* +' Divide two values from the stack, and write the result back to the stack +' Changes parm1, parm2 +'******************************************************************************* +dividefunc call #pop2 + call #divide + mov parm1, parm2 + test parm3, #1 wc + if_c neg parm1, parm1 + jmp #push_jmp + +'******************************************************************************* +' Compute the modulus from two values from the stack, and write the result back +' to the stack +' Changes parm1, parm2 +'******************************************************************************* +modfunc call #pop2 + call #divide + test parm3, #2 wc + if_c neg parm1, parm1 + jmp #push_jmp + +'******************************************************************************* +' Compare two values from the stack to determine if the second one is less than +' the first one, and write the result back to the stack +' Changes parm1, parm2 +'******************************************************************************* +lessfunc call #pop2 + cmps parm1, parm2 wc + if_c neg parm1, #1 + if_nc mov parm1, #0 + jmp #push_jmp + +'******************************************************************************* +' Compare two values from the stack to determine if they are equal, and write +' the result back to the stack +' Changes parm1, parm2 +'******************************************************************************* +equalfunc call #pop2 + cmp parm1, parm2 wz + if_z neg parm1, #1 + if_nz mov parm1, #0 + jmp #push_jmp + +'******************************************************************************* +' Compare two values from the stack to determine if the second one is greater +' than the first one, and write the result back to the stack +' Changes parm1, parm2 +'******************************************************************************* +greaterfunc call #pop2 + cmps parm1, parm2 wc, wz + if_nz_and_nc neg parm1, #1 + if_z_or_c mov parm1, #0 + jmp #push_jmp + +'******************************************************************************* +' Compute the logical AND of two values from the stack, and write the result +' back to the stack +' Changes parm1, parm2 +'******************************************************************************* +andfunc call #pop2 + and parm1, parm2 + jmp #push_jmp + +'******************************************************************************* +' Compute the logical OR of two values from the stack, and write the result +' back to the stack +' Changes parm1, parm2 +'******************************************************************************* +orfunc call #pop2 + or parm1, parm2 + jmp #push_jmp + +'******************************************************************************* +' Compute the logical XOR of two values from the stack, and write the result +' back to the stack +' Changes parm1, parm2 +'******************************************************************************* +xorfunc call #pop2 + xor parm1, parm2 + jmp #push_jmp + +'******************************************************************************* +' Right-shift the second value on the stack by the number of bits specified by +' the first value on the stack, and write the result to the stack +' Changes parm1, parm2 +'******************************************************************************* +rshiftfunc call #pop2 + shr parm1, parm2 + jmp #push_jmp + +'******************************************************************************* +' Left-shift the second value on the stack by the number of bits specified by +' the first value on the stack, and write the result to the stack +' Changes parm1, parm2 +'******************************************************************************* +lshiftfunc call #pop2 + shl parm1, parm2 + jmp #push_jmp + +'******************************************************************************* +' Push the stack depth to the stack +' Changes parm1 +'******************************************************************************* +depthfunc mov parm1, stackptr + sub parm1, stackptr0 + sar parm1, #2 + jmp #push_jmp + +'******************************************************************************* +' Drop the top value from the stack +' No changes +'******************************************************************************* +dropfunc sub stackptr, #4 + jmp #innerloop + +'******************************************************************************* +' Use the value on top of the stack as an index to another value in the stack, +' and write its value to the stack +' No changes +'******************************************************************************* +pickfunc call #pop + call #indexstack + jmp #push_jmp + +'******************************************************************************* +' Use the value on top of the stack as and index to remove another value from +' the stack, and place it at the top of the stack. +' Changes temp1, temp2, temp3, temp4 +'******************************************************************************* +rollfunc call #pop + cmp parm1, #0 wc, wz + if_c_or_z jmp #innerloop + mov temp3, parm1 + call #indexstack + mov temp2, temp1 +:loop add temp2, #4 + rdlong temp4, temp2 + wrlong temp4, temp1 + add temp1, #4 + djnz temp3, #:loop + wrlong parm1, temp1 + jmp #innerloop + +'******************************************************************************* +' Pop the value from the top of the stack, and push it onto the return stack. +' No changes +'******************************************************************************* +torfunc call #pop + wrlong parm1, returnptr + add returnptr, #4 + jmp #innerloop + +'******************************************************************************* +' Pop the value from the top of the return stack and push it to the stack. +' Changes parm1 +'******************************************************************************* +fromrfunc sub returnptr, #4 + rdlong parm1, returnptr + jmp #push_jmp + +'******************************************************************************* +' Push the value on the stack pointed to by the PC and increment the PC +' Changes parm1 +'******************************************************************************* +_litfunc rdword parm1, pc + add pc, #2 + jmp #push_jmp + +'******************************************************************************* +' Convert the string described by the address and length on the top of the +' stack to a hex number, and push it to the stack +' Changes parm1 +'******************************************************************************* +_gethexfunc call #pop2 + call #gethex + mov parm1, parm + jmp #push_jmp + +'******************************************************************************* +' Create a variable, and add it to the dictionary +' Changes parm3 +'******************************************************************************* +createfunc mov parm3, #varfunc + mov parm4, #FLAG_VAR + call #create + jmp #innerloop + +'******************************************************************************* +' Create an executable word, and add it to the dictionary. Set the compile +' state to -1 +' Changes parm3, temp1 +'******************************************************************************* +colonfunc mov parm3, #execlistfunc + mov parm4, #FLAG_DEF + call #create + if_z jmp #innerloop + neg temp1, #1 + wrlong temp1, a_state + jmp #innerloop + +'******************************************************************************* +' Compile a zero into memory indicating the end of an executable word, and set +' the compile flag to zero +' Changes temp1, temp2 +'******************************************************************************* +semicolonfunc mov temp1, #0 + wrlong temp1, a_state + rdlong temp2, a_dp + wrword temp1, temp2 + add temp2, #2 + wrlong temp2, a_dp + jmp #innerloop + +'******************************************************************************* +' Fetch a value from the specified cog address, and put it on the stack +' the compile flag to zero +' Changes parm1 +'******************************************************************************* +cogfetchfunc call #pop + movs cogfetch1, parm1 + nop +cogfetch1 mov parm1, 0-0 + jmp #push_jmp + +'******************************************************************************* +' Get a cog address and value from the stack, and store the value at the address +' the compile flag to zero +' Changes parm1, parm2 +'******************************************************************************* +cogstorefunc call #pop2 + movd cogstore1, parm2 + nop +cogstore1 mov 0-0, parm1 + jmp #innerloop + + +'******************************************************************************* +' Print out an 8-digit hex number to the output port. +' Changes parm +'******************************************************************************* +dotxfunc mov parm, #"$" + call #putch + call #pop + call #printhex + mov parm, #" " + call #putch + jmp #innerloop + +'******************************************************************************* +' If top of stack is zero, jump to address contained in location at current PC. +' Otherwise, increment the PC +' Changes parm1 +'******************************************************************************* +_jzfunc call #pop + if_z rdword pc, pc + if_nz add pc, #2 + jmp #innerloop + +'******************************************************************************* +' Copy bytes from the source to the destination +' Changes parm1 +'******************************************************************************* +cmovefunc sub stackptr, #4 + rdlong parm3, stackptr + call #pop2 + cmps parm3, #0 wz, wc + if_c_or_z jmp #innerloop +:loop rdbyte temp1, parm1 + add parm1, #1 + wrbyte temp1, parm2 + add parm2, #1 + djnz parm3, #:loop + jmp #innerloop + +'******************************************************************************* +' Perform the increment and compare for the loop word +' Changes parm1, parm2, parm3 +'******************************************************************************* +_loopfunc call #pop ' Get increment + sub returnptr, #8 + rdlong parm3, returnptr ' Get upper limit + add returnptr, #4 + rdlong parm2, returnptr ' Get index + add parm1, parm2 ' index + increment + wrlong parm1, returnptr ' Push index back + add returnptr, #4 + cmps parm1, parm3 wc + if_nc neg parm1, #1 + if_c mov parm1, #0 + jmp #push_jmp + +'******************************************************************************* +' The following code implements the basic functions used by the kernel words +'******************************************************************************* + +'******************************************************************************* +' Create a word entry in the dictionary +' Changes parm, parm1, parm2, temp1, temp2 +'******************************************************************************* +create mov parm, #" " + call #word_del + if_z jmp create_ret + rdlong temp1, a_dp ' Align DP + add temp1, #3 + and temp1, minus4 + rdlong temp2, a_last + wrword temp2, temp1 ' Write the link pointer + wrlong temp1, a_last ' Update LAST + add temp1, #2 + + wrbyte parm4, temp1 ' Write the flag + add temp1, #1 + wrbyte parm2, temp1 ' Write the length + add temp1, #1 + cmps parm2, #0 wc, wz + if_c_or_z jmp #create_done +:loop rdbyte temp2, parm1 ' Copy the name + add parm1, #1 + wrbyte temp2, temp1 + add temp1, #1 wz + djnz parm2, #:loop + +create_done mov temp2, #0 ' Pad with 0's to align +:loop1 test temp1, #3 wz + if_z jmp #create_aligned + wrbyte temp2, temp1 + add temp1, #1 + jmp #:loop1 + +create_aligned wrword parm3, temp1 ' Write the code pointer + add temp1, #2 + wrword temp2, temp1 ' Write the DOES> pointer + add temp1, #2 wz ' Clear zero flag + + wrlong temp1, a_dp +create_ret ret + +'******************************************************************************* +' Get one character from the input port. +' Input none +' Changes parm, temp, temp1, temp2 +' Output parm +'******************************************************************************* +getch mov parm, ina + and parm, inbit wz + if_nz jmp #getch + mov temp2, cnt + mov temp, bitcycles + shr temp, #1 + add temp2, temp + mov temp1, #10 +:loop waitcnt temp2, bitcycles + mov temp, ina + and temp, inbit + ror parm, #1 + or parm, temp + djnz temp1, #:loop + ror parm, #31 - 8 + and parm, #255 +getch_ret ret + +inbit long $80000000 +bitcycles long 80_000_000 / 115_200 + +'******************************************************************************* +' Send one character to the output port. +' Input parm +' Changes parm, temp1, temp2 +' Output none +'******************************************************************************* +putch rdlong temp1, a_verbose wz + if_z jmp putch_ret + or parm, #$100 + shl parm, #1 + rol parm, #30 + mov temp1, #10 + mov temp2, bitcycles + add temp2, cnt +:loop mov outa, parm + ror parm, #1 + waitcnt temp2, bitcycles + djnz temp1, #:loop +putch_ret ret + +outbit long $40000000 + +'******************************************************************************* +' Skip the specified character in the input buffer +' Input parm +' Changes temp, temp1 +' Output none +'******************************************************************************* +skipchar cmps temp1, temp2 wc + if_nc jmp skipchar_ret + rdlong temp, a_tib + add temp, temp1 + rdbyte temp, temp + cmp temp, parm wz + if_nz jmp skipchar_ret + add temp1, #1 + jmp #skipchar +skipchar_ret ret + +'******************************************************************************* +' Find the next occurance of the specified character in the input buffer +' Input parm +' Changes temp, temp1 +' Output none +'******************************************************************************* +findchar cmps temp1, temp2 wc + if_nc jmp findchar_ret + rdlong temp, a_tib + add temp, temp1 + rdbyte temp, temp + cmp temp, parm wz + if_z jmp findchar_ret + add temp1, #1 + jmp #findchar +findchar_ret ret + +'******************************************************************************* +' Find the next word in the input buffer delimited by the specified character +' Input parm +' Changes parm1, parm2, temp1, temp2 +' Output none +'******************************************************************************* +word_del + rdlong temp1, a_inputidx + rdlong temp2, a_inputlen + call #skipchar + mov parm1, temp1 + call #findchar + mov parm2, temp1 + sub parm2, parm1 wz + rdlong temp, a_tib + add parm1, temp + cmps temp1, temp2 wc + if_c add temp1, #1 + wrlong temp1, a_inputidx +word_del_ret ret + +'******************************************************************************* +' Find the specified word in the dictionary +' Input parm1, parm2 +' Changes parm, parm3, parm4 +' Output parm +'******************************************************************************* +findword rdlong parm, a_last wz + if_z jmp findword_ret +:loop mov parm3, parm + add parm3, #3 + rdbyte parm4, parm3 + add parm3, #1 + call #compare + if_z jmp findword_ret + rdword parm, parm wz + if_nz jmp #:loop +findword_ret ret + +'******************************************************************************* +' Do a case insensitive comparison of two character strings +' Input parm1, parm2, parm3, parm4 +' Changes parm3, parm4, temp, temp1, temp2 +' Outut Z +'******************************************************************************* +compare cmps parm2, #1 wc, wz + if_c jmp compare_ret + cmp parm2, parm4 wz + if_nz jmp compare_ret + mov temp, parm1 +:loop rdbyte temp1, temp + call #toupper + mov temp2, temp1 + rdbyte temp1, parm3 + call #toupper + cmp temp1, temp2 wz + if_nz jmp compare_ret + add temp, #1 + add parm3, #1 + djnz parm4, #:loop +compare_ret ret + +'******************************************************************************* +' Convert a character to uppercase +' Input temp1 +' Changes temp1 +' Ouput temp1 +'******************************************************************************* +toupper cmp temp1, #"a" wc + if_c jmp toupper_ret + cmp temp1, #"z" wc, wz + if_nc_and_nz jmp toupper_ret + sub temp1, #"a" - "A" +toupper_ret ret + + +'******************************************************************************* +' Print an 8-digit hex value to the output port +' Input parm1 +' Changes parm, parm1, parm2 +' Output none +'******************************************************************************* +printhex mov parm2, #8 +:loop rol parm1, #4 + mov parm, #15 + and parm, parm1 + add parm, a_hexstr + rdbyte parm, parm + call #putch + djnz parm2, #:loop +printhex_ret ret + + +'******************************************************************************* +' Convert a string to a hex number +' Input parm1, parm2 +' Changes parm, temp, temp1, temp2 +' Output parm +'******************************************************************************* +gethex mov parm, #0 + cmps parm2, #0 wc, wz + if_c_or_z jmp gethex_ret + mov temp1, parm1 + mov temp2, parm2 +:loop rdbyte temp, temp1 + add temp1, #1 + sub temp, #"0" + cmps temp, #10 wc + if_nc sub temp, #"a"-"0"-10 + shl parm, #4 + add parm, temp + djnz temp2, #:loop +gethex_ret ret + +'******************************************************************************* +' Push a value onto the data stack +' Input parm1 +' No changes +' Output none +'******************************************************************************* +push wrlong parm1, stackptr + add stackptr, #4 +push_ret ret + +'******************************************************************************* +' Push a value onto the data stack and jump to the innerloop +' Input parm1 +' No changes +' Output none +'******************************************************************************* +push_jmp wrlong parm1, stackptr + add stackptr, #4 + jmp #innerloop + +'******************************************************************************* +' Pop two values off of the data stack +' Input none +' Changes parm1, parm2 +' Output parm1, parm2 +'******************************************************************************* +pop2 sub stackptr, #4 + rdlong parm2, stackptr + +'******************************************************************************* +' Pop one value off of the data stack +' Input none +' Changes parm1 +' Ouput parm1 +'******************************************************************************* +pop sub stackptr, #4 + rdlong parm1, stackptr wz +pop_ret +pop2_ret ret + +'******************************************************************************* +' Read a value on the stack based on an index number +' Changes parm1, temp1 +'******************************************************************************* +indexstack neg temp1, parm1 + shl temp1, #2 + sub temp1, #4 + add temp1, stackptr + rdlong parm1, temp1 +indexstack_ret ret + +'******************************************************************************* +' Compute the XT from the address of the link +' Input: parm1 +' Output: parm1 +' Changes: temp1 +'******************************************************************************* +link2xt mov temp1, parm1 + add temp1, #3 + rdbyte parm1, temp1 ' Get name length + add parm1, temp1 + add parm1, #4 + and parm1, minus4 ' Align +link2xt_ret ret + +'******************************************************************************* +' Multiply two 32-bit numbers +' Changes parm2, temp1, temp2 +'******************************************************************************* +multiply mov temp1, #0 + mov temp2, #32 + shr parm1, #1 wc +mmul if_c add temp1, parm2 wc + rcr temp1, #1 wc + rcr parm1, #1 wc + djnz temp2, #mmul +multiply_ret ret + +'******************************************************************************* +' Divide two 32-bit numbers producing a quotient and a remainder +' Changes parm1, parm2, parm3, temp1, temp2 +'******************************************************************************* +divide mov temp2, #32 + mov temp1, #0 + abs parm1, parm1 wc + muxc parm3, #%11 + abs parm2, parm2 wc,wz + if_c xor parm3, #%01 +' if_nz jmp #mdiv +' mov parm1, #0 +' jmp divide_ret +mdiv shr parm2, #1 wc,wz + rcr temp1, #1 + if_nz djnz temp2, #mdiv +mdiv2 cmpsub parm1, temp1 wc + rcl parm2, #1 + shr temp1, #1 + djnz temp2, #mdiv2 +divide_ret ret + +'******************************************************************************* +' These are working registers. The parm registers are generally used to pass +' parameters from one routine to another, and the temp registers are used as +' temporary storage within a routine. +'******************************************************************************* + +'******************************************************************************* +' Addresses of variables in the dictionary, and the hex table +'******************************************************************************* +a_hexstr long @hexstr+Q +a_last long @last+Q +a_state long @state+Q +a_dp long @dp+Q +a_tib long @tib+Q +a_verbose long @verbose+Q +a_inputidx long @greaterin+Q +a_inputlen long @poundtib+Q + +'******************************************************************************* +' The data and return stack pointers, and their base addresses +'******************************************************************************* +stackptr long 0 +stackptr0 long 0 +returnptr long 0 +returnptr0 long 0 + +'******************************************************************************* +' The input file pointer used during initialization +'******************************************************************************* +infileptr long @infile+Q + +'******************************************************************************* +' Constants +'******************************************************************************* +minus4 long -4 + + fit $1f0 + +'******************************************************************************* +' Input buffer and hex table +'******************************************************************************* +hexstr byte "0123456789abcdef" +inputbuf byte 0[200] + +'******************************************************************************* +' This is the beginning of the dictionary. The kernel words are specified below +'******************************************************************************* +exit_L word 0 + byte FLAG_CORE, 4, "exit" + long +exit_X word exitfunc, 0 + +quit_L word @exit_L+Q + byte FLAG_CORE, 4, "quit" + long +quit_X word quitfunc, 0 + +abort_L word @quit_L+Q + byte FLAG_CORE, 5, "abort" + long +abort_X word abortfunc, 0 + +execute_L word @abort_L+Q + byte FLAG_CORE, 7, "execute" + long +execute_X word executefunc, 0 + +word_L word @execute_L+Q + byte FLAG_CORE, 4, "word" + long +word_X word wordfunc, 0 + +find_L word @word_L+Q + byte FLAG_CORE, 4, "find" + long +find_X word findfunc, 0 + +getchar_L word @find_L+Q + byte FLAG_CORE, 7, "getchar" + long +getchar_X word getcharfunc, 0 + +getfchar_L word @getchar_L+Q + byte FLAG_CORE, 8, "getfchar" + long +getfchar_X word getfcharfunc, 0 + +key_L word @getfchar_L+Q + byte FLAG_CORE, 3, "key" + long +key_X word deferfunc, @key_B+Q +key_B word @getfchar_X+Q, 0 + +create_L word @key_L+Q + byte FLAG_CORE, 6, "create" + long +create_X word createfunc, 0 + +_lit_L word @create_L+Q + byte FLAG_LIT, 4, "_lit" + long +_lit_X word _litfunc, 0 + +_gethex_L word @_lit_L+Q + byte FLAG_CORE, 7, "_gethex" + long +_gethex_X word _gethexfunc, 0 + +emit_L word @_gethex_L+Q + byte FLAG_CORE, 4, "emit" + long +emit_X word emitfunc, 0 + +store_L word @emit_L+Q + byte FLAG_CORE, 1, "!" + long +store_X word storefunc, 0 + +fetch_L word @store_L+Q + byte FLAG_CORE, 1, "@" + long +fetch_X word fetchfunc, 0 + +wstore_L word @fetch_L+Q + byte FLAG_CORE, 2, "w!" + long +wstore_X word wstorefunc, 0 + +wfetch_L word @wstore_L+Q + byte FLAG_CORE, 2, "w@" + long +wfetch_X word wfetchfunc, 0 + +cstore_L word @wfetch_L+Q + byte FLAG_CORE, 2, "c!" + long +cstore_X word cstorefunc, 0 + +cfetch_L word @cstore_L+Q + byte FLAG_CORE, 2, "c@" + long +cfetch_X word cfetchfunc, 0 + +plus_L word @cfetch_L+Q + byte FLAG_CORE, 1, "+" + long +plus_X word plusfunc, 0 + +minus_L word @plus_L+Q + byte FLAG_CORE, 1, "-" + long +minus_X word minusfunc, 0 + +multiply_L word @minus_L+Q + byte FLAG_CORE, 1, "*" + long +multiply_X word multfunc, 0 + +divide_L word @multiply_L+Q + byte FLAG_CORE, 1, "/" + long +divide_X word dividefunc, 0 + +mod_L word @divide_L+Q + byte FLAG_CORE, 3, "mod" + long +mod_X word modfunc, 0 + +and_L word @mod_L+Q + byte FLAG_CORE, 3, "and" + long +and_X word andfunc, 0 + +or_L word @and_L+Q + byte FLAG_CORE, 2, "or" + long +or_X word orfunc, 0 + +xor_L word @or_L+Q + byte FLAG_CORE, 3, "xor" + long +xor_X word xorfunc, 0 + +less_L word @xor_L+Q + byte FLAG_CORE, 1, "<" + long +less_X word lessfunc, 0 + +equal_L word @less_L+Q + byte FLAG_CORE, 1, "=" + long +equal_X word equalfunc, 0 + +greater_L word @equal_L+Q + byte FLAG_CORE, 1, ">" + long +greater_X word greaterfunc, 0 + +rshift_L word @greater_L+Q + byte FLAG_CORE, 6, "rshift" + long +rshift_X word rshiftfunc, 0 + +lshift_L word @rshift_L+Q + byte FLAG_CORE, 6, "lshift" + long +lshift_X word lshiftfunc, 0 + +depth_L word @lshift_L+Q + byte FLAG_CORE, 5, "depth" + long +depth_X word depthfunc, 0 + +tib_L word @depth_L+Q + byte FLAG_VAR, 3, "tib" + long +tib_X word varfunc, @tib+Q+4 +tib long @inputbuf+Q + word @fetch_X+Q, 0 + long + +poundtib_L word @tib_L+Q + byte FLAG_VAR, 4, "#tib" + long +poundtib_X word varfunc, 0 +poundtib long 0 + +greaterin_L word @poundtib_L+Q + byte FLAG_VAR, 3, ">in" + long +greaterin_X word varfunc, 0 +greaterin long 0 + +dp_L word @greaterin_L+Q + byte FLAG_VAR, 2, "dp" + long +dp_X word varfunc, 0 +dp long @_here+Q + +last_L word @dp_L+Q + byte FLAG_VAR, 4, "last" + long +last_X word varfunc, 0 +last long @_last+Q + +state_L word @last_L+Q + byte FLAG_VAR, 5, "state" + long +state_X word varfunc, 0 +state long 0 + +base_L word @state_L+Q + byte FLAG_VAR, 4, "base" + long +base_X word varfunc, 0 +base long 16 + +verbose_L word @base_L+Q + byte FLAG_VAR, 7, "verbose" + long +verbose_X word varfunc, 0 +verbose long 0 + +forth_L word @verbose_L+Q + byte FLAG_VAR, 5, "forth" + long +forth_X word varfunc, 0 + long @forth+Q + +drop_L word @forth_L+Q + byte FLAG_CORE, 4, "drop" + long +drop_X word dropfunc, 0 + +dup_L word @drop_L+Q + byte FLAG_CORE, 3, "dup" + long +dup_X word dupfunc, 0 + +swap_L word @dup_L+Q + byte FLAG_CORE, 4, "swap" + long +swap_X word swapfunc, 0 + +pick_L word @swap_L+Q + byte FLAG_CORE, 4, "pick" + long +pick_X word pickfunc, 0 + +roll_L word @pick_L+Q + byte FLAG_CORE, 4, "roll" + long +roll_X word rollfunc, 0 + +tor_L word @roll_L+Q + byte FLAG_CORE, 2, ">r" + long +tor_X word torfunc, 0 + +fromr_L word @tor_L+Q + byte FLAG_CORE, 2, "r>" + long +fromr_X word fromrfunc, 0 + +colon_L word @fromr_L+Q + byte FLAG_CORE, 1, ":" + long +colon_X word colonfunc, 0 + +semicolon_L word @colon_L+Q + byte FLAG_SEMI, 1, ";" + long +semicolon_X word semicolonfunc, 0 + +cogfetch_L word @semicolon_L+Q + byte FLAG_CORE, 4, "cog@" + long +cogfetch_X word cogfetchfunc, 0 + +cogstore_L word @cogfetch_L+Q + byte FLAG_CORE, 4, "cog!" + long +cogstore_X word cogstorefunc, 0 + +cogx1_L word @cogstore_L+Q + byte FLAG_CORE, 5, "cogx1" + long +cogx1_X word cogx1func, 0 + +_jz_L word @cogx1_L+Q + byte FLAG_JMP, 3, "_jz" + long +_jz_X word _jzfunc, 0 + +cmove_L word @_jz_L+Q + byte FLAG_CORE, 5, "cmove" + long +cmove_X word cmovefunc, 0 + +dotx_L word @cmove_L+Q + byte FLAG_CORE, 2, ".x" + long +dotx_X word dotxfunc, 0 + +'******************************************************************************* +' SPI/SD Variables +'******************************************************************************* +spi_vars_L word @dotx_L+Q + byte FLAG_VAR, 8, "spi_vars" + long +spi_vars_X word varfunc, 0 +spi_vars long 0 ' SPI_engine_cog + long 0 ' SPI_command + long 0 ' SPI_block_index + long 0 ' SPI_buffer_address + long 0 ' SD_rootdir + long 0 ' SD_filesystem + long 0 ' SD_clustershift + long 0 ' SD_dataregion + long 0 ' SD_fat1 + long 0 ' SD_sectorsperfat + long 0 ' SD_currdir + +argc_L word @spi_vars_L+Q + byte FLAG_VAR, 4, "argc" + long +argc_X word confunc, 0 +argc_B long 0 + +argv_L word @argc_L+Q + byte FLAG_VAR, 4, "argv" + long +argv_X word confunc, 0 +argv_B long 0 + +hostcwd_L word @argv_L+Q + byte FLAG_VAR, 7, "hostcwd" + long +hostcwd_X word confunc, 0 +hostcwd_B long 0 + +'******************************************************************************* +' A small number of compiled words follow below. These are used by the boot +' interpreter. +'******************************************************************************* + ' : here dp @ ; +here_L word @hostcwd_L+Q + byte FLAG_DEF, 4, "here" + long +here_X word execlistfunc, 0 + word @dp_X+Q, @fetch_X+Q, 0 + long + + ' : allot dp @ + dp ! ; +allot_L word @here_L+Q + byte FLAG_DEF, 5, "allot" + long +allot_X word execlistfunc, 0 + word @dp_X+Q, @fetch_X+Q, @plus_X+Q, @dp_X+Q, @store_X+Q, 0 + long + + ' : , here ! 4 allot ; +comma_L word @allot_L+Q + byte FLAG_DEF, 1, "," + long +comma_X word execlistfunc, 0 + word @here_X+Q, @store_X+Q, @_lit_X+Q, 4, @allot_X+Q, 0 + long + + ' : _jmp r> @ >r ; +_jmp_L word @comma_L+Q + byte FLAG_JMP, 4, "_jmp" + long +_jmp_X word execlistfunc, 0 + word @fromr_X+Q, @wfetch_X+Q, @tor_X+Q, 0 + long + + ' : count 0 pick 1 + 1 roll c@ ; +count_L word @_jmp_L+Q + byte FLAG_DEF, 5, "count" + long +count_X word execlistfunc, 0 + word @_lit_X+Q, 0, @pick_X+Q, @_lit_X+Q, 1, @plus_X+Q, @_lit_X+Q, 1, @roll_X+Q, @cfetch_X+Q, 0 + long + + ' : accept ( addr size -- num ) \ Accept a string from the input source +accept_L word @count_L+Q + byte FLAG_DEF, 6, "accept" + long +accept_X word execlistfunc, 0 + ' >r dup + word @tor_X+Q, @dup_X+Q + ' r> dup 1 < _jz _accept4 +accept_1 word @fromr_X+Q, @dup_X+Q, @_lit_X+Q, 1, @less_X+Q, @_jz_X+Q, @accept_4+Q + ' drop swap - exit + word @drop_X+Q, @swap_X+Q, @minus_X+Q, @exit_X+Q + ' >r key +accept_4 word @tor_X+Q, @key_X+Q + ' dup 0d = over 0a = or + word @dup_X+Q, @_lit_X+Q, $0d, @equal_X+Q, @_lit_X+Q, 1, @pick_X+Q, @_lit_X+Q, $0a, @equal_X+Q, @or_X+Q + ' _jz _accept2 + word @_jz_X+Q, @accept_2+Q + ' cr drop swap - + word @_lit_X+Q, 13, @emit_X+Q, @_lit_X+Q, 10, @emit_X+Q, @drop_X+Q, @swap_X+Q, @minus_X+Q + ' r> drop exit + word @fromr_X+Q, @drop_X+Q, @exit_X+Q + ' dup 8 = _jz _accept3 +accept_2 word @dup_X+Q, @_lit_X+Q, 8, @equal_X+Q, @_jz_X+Q, @accept_3+Q + ' drop over over - _jz _accept1 + word @drop_X+Q, @_lit_X+Q, 1, @pick_X+Q, @_lit_X+Q, 1, @pick_X+Q, @minus_X+Q, @_jz_X+Q, @accept_1+Q + ' 1 - r> 1 + >r + word @_lit_X+Q, 1, @minus_X+Q, @fromr_X+Q, @_lit_X+Q, 1, @plus_X+Q, @tor_X+Q + ' 8 emit bl emit 8 emit _jmp _accept1 + word @_lit_X+Q, 8, @emit_X+Q, @_lit_X+Q, 32, @emit_X+Q, @_lit_X+Q, 8, @emit_X+Q, @_jmp_X+Q, @accept_1+Q + ' dup emit over c! 1 + +accept_3 word @dup_X+Q, @emit_X+Q, @_lit_X+Q, 1, @pick_X+Q, @cstore_X+Q, @_lit_X+Q, 1, @plus_X+Q + ' r> 1 - >r _jmp _accept1 + word @fromr_X+Q, @_lit_X+Q, 1, @minus_X+Q, @tor_X+Q, @_jmp_X+Q, @accept_1+Q, 0 + long + + ' : refill tib 200 accept #tib ! 0 >in ! ; +refill_L word @accept_L+Q + byte FLAG_DEF, 6, "refill" + long +refill_X word execlistfunc, 0 + word @tib_X+Q, @_lit_X+Q, 200, @accept_X+Q, @poundtib_X+Q, @store_X+Q, @_lit_X+Q, 0, @greaterin_X+Q, @store_X+Q, 0 + long + + ' : compile, here w! 2 allot ; +compcomma_L word @refill_L+Q + byte FLAG_DEF, 8, "compile," + long +compcomma_X word execlistfunc, 0 + word @here_X+Q, @wstore_X+Q, @_lit_X+Q, 2, @allot_X+Q, 0 + long + +'******************************************************************************* +' The boot interpreter follows below. +'******************************************************************************* + ' : xboot ( This word runs a simple interpreter ) +xboot_L word @compcomma_L+Q + byte FLAG_DEF, 5, "xboot" + long +xboot_X word execlistfunc, 0 + + ' 20 word 0 pick c@ _jz _xboot2 ( Get word, refill if empty ) +xboot_1 word @_lit_X+Q, $20, @word_X+Q, @_lit_X+Q, 0, @pick_X+Q, @cfetch_X+Q, @_jz_X+Q, @xboot_2+Q + + ' find 0 pick _jz _xboot3 ( Find word, get number if not found ) + word @find_X+Q, @_lit_X+Q, 0, @pick_X+Q, @_jz_X+Q, @xboot_3+Q + + ' state @ = _jz _xboot4 ( Go execute if not compile mode or immediate ) + word @state_X+Q, @fetch_X+Q, @equal_X+Q, @_jz_X+Q, @xboot_4+Q + + ' compile, _jmp _xboot1 ( Otherwise, compile and loop again ) + word @compcomma_X+Q, @_jmp_X+Q, @xboot_1+Q + + ' execute _jmp _xboot1 ( Execute and loop again ) +xboot_4 word @execute_X+Q, @_jmp_X+Q, @xboot_1+Q + + ' drop count _gethex ( Get number ) +xboot_3 word @drop_X+Q, @count_X+Q, @_gethex_X+Q + + ' state @ _jz _xboot1 ( Loop again if not compile mode ) + word @state_X+Q, @fetch_X+Q, @_jz_X+Q, @xboot_1+Q + + ' ['] _lit , , _jmp _xboot1 ( Otherwise, compile number and loop again ) + word @_lit_X+Q, @_lit_X+Q, @compcomma_X+Q, @compcomma_X+Q, @_jmp_X+Q, @xboot_1+Q + + ' drop refill _jmp _xboot1 ( Refill and loop again ) +xboot_2 word @drop_X+Q, @refill_X+Q, @_lit_X+Q, 13, @emit_X+Q, @_jmp_X+Q, @xboot_1+Q, 0 + long + +switch_L word @xboot_L+Q + byte FLAG_DEF, 6, "switch" + long +switch_X word execlistfunc, 0 + word @_lit_X+Q, @getchar_X+Q, @_lit_X+Q, @key_B+Q, @store_X+Q, 0 + long + +_last long + +_loop_L word @switch_L+Q + byte FLAG_CORE, 5, "_loop" + long +_loop_X word _loopfunc, 0 + +_here long + +'******************************************************************************* +' The Forth source files follow below. They will be compiled into the +' dictionary, which will over-write the source data. Some padding space is +' included to ensure that we don't over-write the source data before it is +' compiled. +'******************************************************************************* + long 0[100] +infile file "init.fth" + file "comus.fth" + 'file "see.fth" + file "propwords.fth" + 'file "sd.fth" + 'file "sdutils.fth" + 'file "linux.fth" + 'file "cd.fth" + 'file "ted.fth" + 'file "bufser.fth" + 'file "i2c.fth" + 'file "fds.fth" + 'file "time.fth" + 'file "toggle.fth" + 'file "primes.fth" + file "chess.fth" + +'******************************************************************************* +' Enable serial output, print version string and switch to serial input +'******************************************************************************* + byte 13 + byte " 1 verbose !" + byte " pfthversion type cr" + byte " switch", 13 + + +{ ++-------------------------------------------------------------------- +| TERMS OF USE: MIT License ++-------------------------------------------------------------------- +Permission is hereby granted, free of charge, to any person obtaining +a copy of this software and associated documentation files +(the "Software"), to deal in the Software without restriction, +including without limitation the rights to use, copy, modify, merge, +publish, distribute, sublicense, and/or sell copies of the Software, +and to permit persons to whom the Software is furnished to do so, +subject to the following conditions: + +The above copyright notice and this permission notice shall be +included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. ++------------------------------------------------------------------ +} diff --git a/pfth/chkcore.fth b/pfth/chkcore.fth new file mode 100644 index 0000000..4c19707 --- /dev/null +++ b/pfth/chkcore.fth @@ -0,0 +1,145 @@ +( This program checks for all 133 ANS Forth core words ) + +: checkword 1 + ' 0 = + if swap 1 + swap source type ." failed" 13 emit 10 emit then ; + +: checkdone swap dup + if swap dup rot rot swap - . ." out of " + else drop ." All " then + . ." ANS Forth core words implemented" 13 emit 10 emit ; + +0 0 +checkword ! +checkword # +checkword #> +checkword #S +checkword ' +checkword ( +checkword * +checkword */ +checkword */MOD +checkword + +checkword +! +checkword +LOOP +checkword , +checkword - +checkword . +checkword ." +checkword / +checkword /MOD +checkword 0< +checkword 0= +checkword 1+ +checkword 1- +checkword 2! +checkword 2* +checkword 2/ +checkword 2@ +checkword 2DROP +checkword 2DUP +checkword 2OVER +checkword 2SWAP +checkword : +checkword ; +checkword < +checkword <# +checkword = +checkword > +checkword >BODY +checkword >IN +checkword >NUMBER +checkword >R +checkword ?DUP +checkword @ +checkword ABORT +checkword ABORT" +checkword ABS +checkword ACCEPT +checkword ALIGN +checkword ALIGNED +checkword ALLOT +checkword AND +checkword BASE +checkword BEGIN +checkword BL +checkword C! +checkword C, +checkword C@ +checkword CELL+ +checkword CELLS +checkword CHAR +checkword CHAR+ +checkword CHARS +checkword CONSTANT +checkword COUNT +checkword CR +checkword CREATE +checkword DECIMAL +checkword DEPTH +checkword DO +checkword DOES> +checkword DROP +checkword DUP +checkword ELSE +checkword EMIT +checkword ENVIRONMENT? +checkword EVALUATE +checkword EXECUTE +checkword EXIT +checkword FILL +checkword FIND +checkword FM/MOD +checkword HERE +checkword HOLD +checkword I +checkword IF +checkword IMMEDIATE +checkword INVERT +checkword J +checkword KEY +checkword LEAVE +checkword LITERAL +checkword LOOP +checkword LSHIFT +checkword M* +checkword MAX +checkword MIN +checkword MOD +checkword MOVE +checkword NEGATE +checkword OR +checkword OVER +checkword POSTPONE +checkword QUIT +checkword R> +checkword R@ +checkword RECURSE +checkword REPEAT +checkword ROT +checkword RSHIFT +checkword S" +checkword S>D +checkword SIGN +checkword SM/REM +checkword SOURCE +checkword SPACE +checkword SPACES +checkword STATE +checkword SWAP +checkword THEN +checkword TYPE +checkword U. +checkword U< +checkword UM* +checkword UM/MOD +checkword UNLOOP +checkword UNTIL +checkword VARIABLE +checkword WHILE +checkword WORD +checkword XOR +checkword [ +checkword ['] +checkword [CHAR] +checkword ] +checkdone diff --git a/pfth/chkcorex.fth b/pfth/chkcorex.fth new file mode 100644 index 0000000..880990c --- /dev/null +++ b/pfth/chkcorex.fth @@ -0,0 +1,57 @@ +( This program checks for all 45 ANS Forth core ext words ) + +: checkword 1 + ' 0 = + if swap 1 + swap source type ." failed" 13 emit 10 emit then ; + +: checkdone swap dup + if swap dup rot rot swap - . ." out of " + else drop ." All " then + . ." ANS Forth core ext words implemented" 13 emit 10 emit ; + +0 0 +checkword #tib +checkword .( +checkword .r +checkword 0<> +checkword 0> +checkword 2>r +checkword 2r> +checkword 2r@ +checkword :noname +checkword <> +checkword ?do +checkword again +checkword c" +checkword case +checkword compile, +checkword convert +checkword endcase +checkword endof +checkword erase +checkword expect +checkword false +checkword hex +checkword marker +checkword nip +checkword of +checkword pad +checkword parse +checkword pick +checkword query +checkword refill +checkword restore-input +checkword roll +checkword save-input +checkword source-id +checkword span +checkword tib +checkword to +checkword true +checkword tuck +checkword u.r +checkword u> +checkword value +checkword within +checkword [compile] +checkword \ +checkdone diff --git a/pfth/comus.fth b/pfth/comus.fth new file mode 100644 index 0000000..b876b67 --- /dev/null +++ b/pfth/comus.fth @@ -0,0 +1,29 @@ +( Useful non-standard words ) +: @+ dup cell+ swap @ ; +: !+ over ! cell+ ; +: c@+ dup char+ swap c@ ; +: c!+ over c! char+ ; +: between 1+ within ; +: bounds over + swap ; +: buffer: create allot ; +: cell 4 ; +: cell- cell - ; +: not 0= ; +: parse-word bl word count ; +: perform @ execute ; +: >= < 0= ; +: <= > 0= ; +: -rot rot rot ; +: 2- 2 - ; +: 2+ 2 + ; +: 3dup dup 2over rot ; +: 4dup 2over 2over ; +: noop ; +: off false swap ! ; +: on true swap ! ; +: for ['] >r compile, ['] _lit compile, 0 compile, ['] >r compile, here ; + immediate +: next ['] _lit compile, 1 compile, ['] _loop compile, ['] _jz compile, + compile, ['] r> compile, ['] r> compile, ['] 2drop compile, ; immediate +: zstrlen dup begin dup c@ while 1+ repeat swap - ; +: zcount dup zstrlen ; diff --git a/pfth/fds.fth b/pfth/fds.fth new file mode 100644 index 0000000..5a58de4 --- /dev/null +++ b/pfth/fds.fth @@ -0,0 +1,88 @@ +\ ############################################################################ +\ # fds.fth - This program implements a full duplex serial port. It is base +\ # on the FullDuplexSerial Spin object. +\ # +\ # Copyright (c) 2012 Dave Hein +\ # MIT Licensed +\ ############################################################################ + +0 value fds_cog +create fds_vars 68 allot + +: fds_var create , does> @ fds_vars + ; + + 0 fds_var rx_head + 4 fds_var rx_tail + 8 fds_var tx_head +12 fds_var tx_tail +16 fds_var rx_pin +20 fds_var tx_pin +24 fds_var rxtx_mode +28 fds_var bit_ticks +32 fds_var buffer_ptr +36 fds_var rx_buffer +52 fds_var tx_buffer + +hex + +create fds_entry + a0bca9f0 , 80fca810 , 08bcaa54 , a0fcb201 , 2cbcb255 , 80fca804 , 08bcaa54 , + a0fcbe01 , 2cbcbe55 , 80fca804 , 08bcae54 , 80fca804 , 08bcb054 , 80fca804 , + 08bcb454 , a0bcc05a , 80fcc010 , 627cae04 , 617cae02 , 689be85f , 68abec5f , + a0fcc833 , 5cbcbc64 , 627cae01 , 613cb3f2 , 5c640016 , a0fcb809 , a0bcba58 , + 28fcba01 , 80bcbbf1 , 80bcba58 , 5cbcbc64 , a0bca85d , 84bca9f1 , c17ca800 , + 5c4c001f , 613cb3f2 , 30fcb601 , e4fcb81e , 28fcb617 , 60fcb6ff , 627cae01 , + 6cd4b6ff , 08bcabf0 , 80bcaa5a , 003cb655 , 84bcaa5a , 80fcaa01 , 60fcaa0f , + 083cabf0 , 5c7c0016 , 5cbcc85e , a0bca9f0 , 80fca808 , 08bcaa54 , 80fca804 , + 08bcac54 , 863caa56 , 5c680033 , 80bcac60 , 00bcc256 , 84bcac60 , 80fcac01 , + 60fcac0f , 083cac54 , 68fcc300 , 2cfcc202 , 68fcc201 , a0fcc40b , a0bcc7f1 , + 627cae04 , 617cae02 , 6ce0c201 , 29fcc201 , 70abe85f , 7497ec5f , 80bcc658 , + 5cbcc85e , a0bca863 , 84bca9f1 , c17ca800 , 5c4c004d , e4fcc446 , 5c7c0033 , + +decimal + +: fds_start ( rxpin txpin mode baudrate ... ) + >r + fds_vars 16 0 fill + rxtx_mode ! tx_pin ! rx_pin ! + clkfreq@ r> / bit_ticks ! + rx_buffer buffer_ptr ! + 0 dira! + fds_entry rx_head cognew + 1+ dup to fds_cog +; + +: fds_stop fds_cog if fds_cog 1- cogstop 0 to fds_cog then ; + +: fds_rxcheck rx_tail @ rx_head @ 2dup . . cr = + if + -1 + else + rx_tail @ rx_buffer + c@ + rx_tail @ 1+ 15 and rx_tail ! + then +; + +: fds_rx begin fds_rxcheck dup -1 = while drop repeat ; + +: fds_tx begin tx_tail @ tx_head @ 1+ 15 and <> until + tx_head @ tx_buffer + c! + tx_head @ 1+ 15 and tx_head ! + rxtx_mode @ 8 and if fds_rx then +; + +: fdstest + ." Type 'q' to quit" cr + 31 30 0 115200 fds_start + drop + begin + fds_rx + dup [char] q <> + while + [char] < fds_tx fds_tx [char] > fds_tx + repeat + drop + fds_stop + 1 30 lshift dira! +; + diff --git a/pfth/i2c.fth b/pfth/i2c.fth new file mode 100644 index 0000000..0c8852d --- /dev/null +++ b/pfth/i2c.fth @@ -0,0 +1,217 @@ +\ ############################################################################ +\ # i2c.fth - This program reads and writes EEPROMs using the I2C protocol. +\ # This program is based on Mike Green's basic_i2c_driver, which is found in +\ # the Parallax Object Exchange (OBEX). +\ # +\ # The first parameter for all routines is the pin number of the clock. It +\ # is assumed that the data pin number is one greater than the clock pin +\ # number. +\ # +\ # Copyright (c) 2012 Dave Hein +\ # MIT Licensed +\ ############################################################################ + +: i2c_dira_sda_high dup dira@ or dira! ; +: i2c_outa_sda_high dup outa@ or outa! ; +: i2c_dira_scl_high over dira@ or dira! ; +: i2c_outa_scl_high over outa@ or outa! ; +: i2c_dira_sda_low dup invert dira@ and dira! ; +: i2c_outa_sda_low dup invert outa@ and outa! ; +: i2c_dira_scl_low over invert dira@ and dira! ; +: i2c_outa_scl_low over invert outa@ and outa! ; + +\ This routine should be called before calling any of the other ones to ensure +\ that the EEPROM is in a known ready state. +: i2c_init ( scl ... ) + 1 swap lshift dup 2* \ sda := scl + 1 + i2c_outa_scl_high \ outa[scl] := 1 + i2c_dira_scl_high \ dira[scl] := 1 + i2c_dira_sda_low \ dira[sda] := 0 + 9 0 do \ repeat 9 + i2c_outa_scl_low \ outa[scl] := 0 + i2c_outa_scl_high \ outa[scl[ := 1 + dup ina@ and if leave then \ if ina[sda] quit + loop + 2drop +; + +\ This routine sends a start bit +: i2c_start ( scl ... ) + 1 swap lshift dup 2* \ sda := scl + 1 + i2c_outa_scl_high \ outa[scl]~~ + i2c_dira_scl_high \ dira[scl]~~ + i2c_outa_sda_high \ outa[sda]~~ + i2c_dira_sda_high \ dira[sda]~~ + i2c_outa_sda_low \ outa[sda]~ + i2c_outa_scl_low \ outa[scl]~ + 2drop +; + +\ This routine sends a stop bit +: i2c_stop ( scl ... ) + 1 swap lshift dup 2* \ sda := scl + 1 + i2c_outa_scl_high \ outa[scl]~~ + i2c_outa_sda_high \ outa[sda]~~ + i2c_dira_scl_low \ dira[scl]~ + i2c_dira_sda_low \ dira[sda]~ + 2drop +; + +\ This routine sends one byte and returns the ACK bit +: i2c_write ( scl data ... ackbit ) + 23 lshift swap \ data <<= 23 + 1 swap lshift dup 2* \ sda := scl + 1 + 8 0 do \ repeat 8 + rot 2* dup 0< + if + rot rot + i2c_outa_sda_high \ outa[sda] := 1 + else + rot rot + i2c_outa_sda_low \ outa[sda] := 0 + then + i2c_outa_scl_high \ outa[scl]~~ + i2c_outa_scl_low \ dira[scl]~ + loop + i2c_dira_sda_low \ dira[sda]~ + i2c_outa_scl_high \ outa[scl]~~ + dup ina@ and >r \ ackbit := ina[sda] + i2c_outa_scl_low \ outa[scl]~ + i2c_outa_sda_low \ outa[sda]~ + i2c_dira_sda_high \ dira[sda]~~ + 2drop drop + r> \ return ackbit +; + +\ This routine reads one byte from the EEPROM +: i2c_read ( scl ackbit ... data ) + >r \ save ackbit + 0 swap \ data := 0 + 1 swap lshift dup 2* \ sda := scl + 1 + i2c_dira_sda_low \ dira[sda]~ + 8 0 do \ repeat 8 + i2c_outa_scl_high \ outa[scl]~~ + rot 2* \ data <<= 1 + over ina@ and if 1 or then \ if ina[sda] data |= 1 + rot rot + i2c_outa_scl_low \ outa[scl]~ + loop + r> if + i2c_outa_sda_high \ outa[sda]~~ + else + i2c_outa_sda_low \ outa[sda]~ + then + i2c_dira_sda_high \ dira[sda]~~ + i2c_outa_scl_high \ outa[scl]~~ + i2c_outa_scl_low \ outa[scl]~ + i2c_outa_sda_low \ outa[sda]~ + 2drop \ return data +; + +\ This routine reads up to one page of data from the EEPROM +: i2c_readpage ( scl devsel addrreg dataptr count ... ackbit ) + >r >r \ Move count and dataptr to the return stack + dup 15 rshift 14 and rot or \ Assemble the devsel byte + dup >r rot dup dup >r \ Copy devsel and scl to the return stack + i2c_start \ Send a start bit + swap \ Arrange the scl and devsel on the stack + i2c_write drop \ Send the devsel byte + dup 8 rshift 255 and r@ swap \ Extract the second address byte + i2c_write drop \ Send the second address byte + 255 and r@ swap \ Extract the third address byte + i2c_write drop \ Send it + r@ \ Get the scl from the return stack + i2c_start \ Send a start bit + r> r> 1 or over >r \ Get the scl and devsel byte and set the LSB + i2c_write drop \ Send the devsel byte + r> r> r> 1 ?do \ Get scl, dataptr and count and start do loop + over 0 i2c_read over c! 1+ \ Read a byte from the EEPROM and save it + loop + over 1 i2c_read swap c! \ Read the last byte from the EEPROM + i2c_stop \ Send a stop bit + 0 \ Return the ack bit +; + +variable i2c_var + +\ This routine reads a byte from the specified address +: i2c_readbyte ( scl devsel addrreg ) + i2c_var 1 i2c_readpage drop i2c_var c@ ; + +\ This routine reads a word from the specified address +: i2c_readword ( scl devsel addrreg ) + 0 i2c_var ! i2c_var 2 i2c_readpage drop i2c_var @ ; + +\ This routine reads a long from the specified address +: i2c_readlong ( scl devsel addrreg ) + i2c_var 4 i2c_readpage drop i2c_var @ ; + +\ This routine writes up to one page of data to the EEPROM +: i2c_writepage ( scl devsel addrreg dataptr count ... ackbit ) + >r >r \ ( scl devsel addrreg ) r( count dataptr ) + dup 15 rshift 14 and rot or \ ( scl addrreg devsel ) + rot dup >r \ ( addrreg devsel scl ) r( count dataptr scl ) + i2c_start \ ( addrreg devsel ) r( count dataptr scl ) + r@ swap \ ( addrreg slc devsel ) r( count dataptr scl ) + i2c_write drop \ ( addrreg ) r( count dataptr scl ) + dup 8 rshift 255 and r@ swap + i2c_write drop + 255 and r@ swap + i2c_write drop + r> r> r> 0 ?do + 2dup c@ i2c_write drop 1+ + loop + drop + i2c_stop + 0 +; + +\ This routine writes a byte to the specified address +: i2c_writebyte ( scl devsel addrreg data ) + i2c_var ! i2c_var 1 i2c_writepage drop ; + +\ This routine writes a word to the specified address +: i2c_writeword ( scl devsel addrreg data ) + i2c_var ! i2c_var 2 i2c_writepage drop ; + +\ This routine writes a long to the specified address +: i2c_writelong ( scl devsel addrreg data ) + i2c_var ! i2c_var 4 i2c_writepage drop ; + +\ This routine returns a zero if the EEPROM is ready after a write +\ Otherwise it returns a non-zero value +: i2c_writewait ( scl devsel addrreg ) + 15 rshift 14 and or + over i2c_start + over >r i2c_write + r> i2c_stop +; + +\ This word will be run at startup +: startup + 1 30 lshift dup outa! dira! + pfthversion type cr +; + +\ Set up the cog config struct +: setupconfig + 496 cog@ \ Get config struct address from PAR + dup 16 + @ \ Get the address of the return stack + 4 + over 12 + ! \ Add four and set initial address of return stack + ['] interpret >body \ Get the address of the Forth interpreter + over 16 + @ ! \ Write it to the return stack + ['] startup >body \ Get the address of the startup word + swap ! \ Write it to the intial value of the program counter +; + +\ Save the hub RAM to EEPROM +: eesave + setupconfig + 28 i2c_init + 512 0 + do + 28 160 i 64 * dup 64 i2c_writepage drop + begin 28 160 0 i2c_writewait 0= until + i 7 and 7 = if [char] . emit then + loop +; diff --git a/pfth/init.fth b/pfth/init.fth new file mode 100644 index 0000000..92a6f38 --- /dev/null +++ b/pfth/init.fth @@ -0,0 +1,401 @@ +: link>flags 2 + ; +: immediate 81 last @ link>flags c! ; +: \ 100 word drop ; immediate + +\ The above lines implement the words to allow for "\" comments +\ All numbers are in hex at this point. + +\ DEFINE CELL SIZE +: cellsize 4 ; +: cellmask 3 ; +: compsize 2 ; +: compmask 1 ; + +\ BASIC STACK WORDS +: rot 2 roll ; +: over 1 pick ; +: 2dup over over ; +: 2drop drop drop ; +: 2swap 3 roll 3 roll ; +: 2over 3 pick 3 pick ; + +\ WORD HEADER ACCESSORS +: >does 2 + ; +: >body 4 + ; +: name>xt dup c@ + 4 + 0 4 - and ; +: link>name 3 + ; +: link>xt link>name name>xt ; +: link>does link>xt 2 + ; +: link>body link>xt 4 + ; + +\ DEFINE BASIC WORD BUILDERS +: source tib #tib @ ; +\ : compile, , ; +: ' 20 word find 0 = 0 = and ; +: _does r> dup >r 2 + last @ link>does w! ; +: _setjmp 0a last @ link>flags c! ; +: literal 0 compile, compile, ; immediate + last @ link>body dup @ swap 2 + w! \ Patch in address of _lit +: postpone ' compile, ; immediate +: ['] ' postpone literal ; immediate +: [compile] ' postpone literal ['] compile, compile, ; immediate +: does> [compile] _does [compile] exit ; immediate + +\ CONDITIONAL EXECUTION AND LOOPING +: if ['] _jz compile, here 2 allot ; immediate +: else ['] _jmp compile, here 2 + swap w! here 2 allot ; immediate +: then here swap w! ; immediate +: begin here ; immediate +: until ['] _jz compile, compile, ; immediate +: again ['] _jmp compile, compile, ; immediate +: while ['] _jz compile, here 2 allot ; immediate +: repeat ['] _jmp compile, here 2 + swap w! compile, ; immediate +: do ['] _lit compile, here 2 allot ['] drop compile, + ['] swap compile, ['] >r compile, ['] >r compile, here ; immediate +: ?do ['] 2dup compile, ['] > compile, ['] _jz compile, here 2 allot + ['] swap compile, ['] >r compile, ['] >r compile, here ; immediate +\ : _loop r> swap r> + r> dup >r swap dup >r > 0 = swap >r ; +: loop ['] _lit compile, 1 compile, ['] _loop compile, ['] _jz compile, compile, ['] r> compile, + ['] r> compile, here swap w! ['] 2drop compile, ; immediate +: +loop ['] _loop compile, ['] _jz compile, compile, ['] r> compile, + ['] r> compile, here swap w! ['] 2drop compile, ; immediate +: leave r> r> drop r> dup >r >r >r ; +: i r> r> dup >r swap >r ; +: j r> r> r> r> dup >r swap >r swap >r swap >r ; + +\ DEFINE >FLAGS AND >LINK +: >flags begin 1 - dup c@ 80 and until ; +: >link >flags 2 - ; + +\ DEFINE DEFER AND IS +\ Change code pointer from varfunc to deferfunc +: defer create last @ link>xt dup w@ 3 + swap w! ; +: is state @ + if [compile] >body ' >does postpone literal [compile] w! + else >body ' >does w! + then ; immediate + +\ REDEFINE REFILL AS A DEFERRED WORD +' refill +defer refill +is refill + +\ DEFINE "(" COMMENT WORD NOW THAT WE CAN LOOP +: ( begin + #tib @ >in @ + ?do tib i + c@ 29 = if i 1 + >in ! r> r> drop drop exit then loop + refill 0 = + until ; immediate + +( PAD AND PRINT SUPPORT ) +create pad 100 allot +create printptr 4 allot +: _d2a dup 0a < if 30 else 57 then + ; +: _a2d dup 30 < + if + drop 0 1 - + else + dup 39 > + if + dup 41 < + if + drop 0 1 - + else + dup 5a > + if + dup 61 < + if + drop 0 1 - + else + dup 7a > + if + drop 0 1 - + else + 57 - + then + then + else + 37 - + then + then + else + 30 - + then + then + dup base @ < 0 = + if + drop 0 1 - + then +; +: c!-- dup >r c! r> 1 - ; +: cprint printptr @ c! printptr @ 1 - printptr ! ; + +( DOUBLE WORDS ) +: s>d 0 pick 0 < ; +: m* * s>d ; +: um* * 0 ; +: d+ drop 1 roll drop + s>d ; +: d- drop 1 roll drop - s>d ; +: d* drop 1 roll drop * s>d ; +: d/ drop 1 roll drop / s>d ; +: dmod drop 1 roll drop mod s>d ; +: _u/ over over swap 1 rshift swap / dup + dup >r over * rot swap - swap < 1 + r> + ; +: u/ over 0 < if _u/ else / then ; +: ud/ drop 1 roll drop u/ 0 ; +: _umod swap dup 1 rshift 2 pick mod dup + swap 1 and + swap mod ; +: umod over 0 < if _umod else mod then ; +: udmod drop 1 roll drop umod 0 ; + +( CORE WORDS ) +: +! dup @ rot + swap ! ; +: /mod over over >r >r mod r> r> / ; +: [ state 0 ! ; +: ] state 1 ! ; +: r@ r> r> dup >r swap >r ; +: sm/rem >r 2dup r@ s>d d/ drop r> swap >r s>d dmod drop r> ; +: um/mod >r 2dup r@ s>d ud/ drop r> swap >r s>d udmod drop r> ; +: fm/mod over over xor 1 31 lshift and if sm/rem else sm/rem then ; ( TODO ) +: */mod >r m* r> sm/rem ; +: */ */mod swap drop ; +: <# pad ff + printptr ! ; +: hold cprint ; +\ : # over over base @ 0 udmod drop _d2a cprint base @ 0 ud/ ; +: # drop dup base @ umod _d2a cprint base @ u/ 0 ; +: #s begin # over over or 0 = until ; +: #> drop drop printptr @ 1 + dup pad 100 + swap - ; +: sign 0 < if 2d hold then ; +: abs dup 0 < if 0 swap - then ; +: type 0 ?do dup c@ emit 1 + loop drop ; +: ._ dup abs 0 <# #s rot sign #> type ; +: . ._ 20 emit ; + +: >number dup 0 ?do >r dup c@ _a2d dup 0 < if drop r> leave else swap >r >r + base @ 0 d* r> 0 d+ r> 1 + r> 1 - then loop ; +: 0= 0 = ; +: 0< 0 < ; +: 1+ 1 + ; +: 1- 1 - ; +: 2! swap over ! cellsize + ! ; +: 2* dup + ; +: 2/ dup 80000000 and swap 1 rshift or ; +: 2@ dup cellsize + @ swap @ ; +: ?dup dup if dup then ; +: aligned cellmask + 0 cellsize - and ; +: align here aligned here - allot ; +: bl 20 ; +: c, here c! 1 allot ; +: cell+ cellsize + ; +: cells cellsize * ; +: char+ 1 + ; +: chars ; +\ : count dup char+ swap c@ ; +: char 20 word count 0= if drop 0 else c@ then ; +: [char] char postpone literal ; immediate +\ : constant create here ! cellsize allot does> @ ; +: constant create , last @ link>xt dup w@ 3 - swap w! ; +: cr 0a emit 0d emit ; +: decimal 0a base ! ; +: environment? drop drop 0 ; +: fill swap >r swap r> 0 ?do 2dup c! 1 + loop 2drop ; +: hex 10 base ! ; +: invert 0 1 - xor ; +: max 2dup < if swap then drop ; +: min 2dup > if swap then drop ; +\ : cmove >r swap r> 0 ?do 2dup c@ swap c! 1+ swap 1+ swap loop 2drop ; +: cmove> >r swap r> dup >r 1- dup >r + swap r> + swap r> ?do 2dup c@ swap c! + 1- swap 1- swap loop 2drop ; +: move r> 2dup > if r> cmove else r> cmove> then ; +: negate 0 swap - ; +: recurse last @ , ; immediate +: _lit" r> dup 1 + swap dup c@ dup rot + compsize + 0 compsize - and >r ; + 84 last @ link>flags c! ( Set STRING flag ) +: _compile" [char] " word count dup >r dup >r c, here r> cmove r> allot + compsize here - compmask and allot ; immediate +create s"buf 50 allot +: s" state @ if ['] _lit" compile, postpone _compile" else + [char] " word count >r s"buf r@ cmove s"buf r> then ; immediate +: ." postpone s" ['] type compile, ; immediate +: _abort" if type abort else drop drop then ; +\ : abort" postpone s" ['] _abort" compile, ; immediate +: abort" postpone s" ['] _abort" compile, ; +: space 20 emit ; +: spaces 0 ?do space loop ; +: u._ 0 <# #s #> type ; +: u. u._ 20 emit ; +: u< over over xor 1 31 lshift and if swap then < ; +: unloop r> r> r> drop drop >r ; +: variable create cellsize allot ; + +( CORE EXT ) +: 0<> 0= invert ; +: 0> 0 > ; +: 2>r r> rot >r swap >r >r ; +: 2r> r> r> r> rot >r swap ; +: 2r@ r> r> r> 2dup >r >r swap rot >r ; +: <> = 0= ; +: erase 0 ?do dup 0 swap ! 1 + loop drop ; +variable span +: expect accept span ! ; +: false 0 ; +: marker create last @ , does> @ dup dp ! @ last ! ; +: nip swap drop ; +: parse word count ; +: true 0 1 - ; +: tuck swap over ; +: to ' >body state @ if postpone literal [compile] ! else ! then ; immediate +\ : value create here ! cellsize allot does> @ ; +: value create , last @ link>xt dup w@ 3 - swap w! ; +: within over - >r - r> u< ; +: .r_ >r dup abs 0 <# #s rot sign #> dup r> swap - spaces type ; +: .r .r_ 20 emit ; +: u.r_ >r 0 <# #s #> dup r> swap - spaces type ; +: u.r .r_ 20 emit ; +: u> over over xor 80000000 and if swap then > ; +: unused 8000 here - ; +: case 0 ; immediate +: of ['] over compile, ['] = compile, + ['] _jz compile, here 4 allot ['] drop compile, ; immediate +: endof ['] _jmp compile, here 2 + swap w! here 2 allot ; immediate +: endcase ['] drop compile, begin ?dup while here swap w! repeat ; immediate +: c" ['] _lit" compile, postpone _compile" ['] drop compile, ['] 1- compile, ; immediate +: .( [char] ) word count type ; immediate +: :noname align here ['] words @ , [ ; + +( DOUBLE ) +: d= rot = rot rot = and ; +: d0= or 0 = ; +: 2constant create swap , , does> dup @ swap cellsize + @ ; + +( STRING ) +: blank 0 ?do dup bl swap c! 1+ loop drop ; +: -trailing dup 0 ?do 2dup + 1- c@ bl = if 1- else leave then loop ; +: /string dup >r - swap r> + swap ; + +( TOOLS ) +: ? @ . ; +: .s 3c emit depth ._ 3e emit 20 emit depth 0 ?do depth i - 1 - pick . loop ; +: dump 0 ?do i 0f and 0 = if cr dup . then dup c@ 3 .r 1 + loop drop cr ; +: forget 20 word find if >link dup dp ! w@ last ! else abort" ?" then ; +: .name dup link>name count type space ; +: ?newline dup >r link>name c@ dup rot + 1 + dup 4e > if cr else swap then drop r> ; +: words 0 last @ begin dup while ?newline .name w@ repeat 2drop ; + +( UTILITY ) +: at-xy 2 emit swap emit emit ; +: page 0 emit ; + +( VERSION STRING ) +: pfthversion s" pfth 1.03" ; + +create evalmode 0 , +0 value source-id +create srcstk0 30 allot +srcstk0 value srcstk + +: resetstack depth 0 < + if + begin depth while 0 repeat + else + begin depth while drop repeat + then +; + +: getnumber 2dup >r >r swap dup c@ [char] - = + if + swap + dup 1 < + if + 2drop 2drop r> r> 1 + else + swap 1 + swap 1 - + >number dup + if + 2drop 2drop r> r> 1 + else + 2drop drop negate 0 r> r> 2drop + then + then + else + swap + >number dup + if + 2drop 2drop r> r> 1 + else + 2drop drop 0 r> r> 2drop + then + then +; + +: compilenumber + dup ['] _lit compile, compile, + dup ffff 10 lshift and + if + 10 rshift + ['] _lit compile, compile, + ['] _lit compile, 10 compile, + ['] lshift compile, + ['] or compile, + else + drop + then +; + +: _interpret + begin + 20 word dup c@ + while + find dup + if + state @ = + if + compile, + else + execute + then + else + dup rot count getnumber + if + type ." ?" cr + else + state @ + if + compilenumber + then + then + then + repeat + drop +; + +: .savesrc ." _savesrc " srcstk0 . srcstk . cr ; +: .loadsrc ." _loadsrc " srcstk0 . srcstk . cr ; + +: _savesrc ( .savesrc ) tib srcstk ! #tib @ srcstk 4 + ! >in @ srcstk 8 + ! source-id srcstk 0c + ! srcstk 10 + to srcstk ; +: _loadsrc srcstk 10 - to srcstk ( .loadsrc ) srcstk @ to tib srcstk 4 + @ #tib ! srcstk 8 + @ >in ! srcstk 0c + @ to source-id ; +: evaluate _savesrc 0 1 - to source-id #tib ! to tib 0 >in ! _interpret _loadsrc ; + +( INTERPRETER ) +: interpret +begin + _interpret + depth 0 < + if + ." Stack Underflow" cr + resetstack + else + source-id 0 1 - = + if + _loadsrc + \ 0 to source-id + else + source-id 0= + if ." ok" cr then + refill + then + then +again +; + +decimal +interpret + diff --git a/pfth/license.txt b/pfth/license.txt new file mode 100644 index 0000000..f5b4c0e --- /dev/null +++ b/pfth/license.txt @@ -0,0 +1,22 @@ ++-------------------------------------------------------------------- +| TERMS OF USE: MIT License ++-------------------------------------------------------------------- +Permission is hereby granted, free of charge, to any person obtaining +a copy of this software and associated documentation files +(the "Software"), to deal in the Software without restriction, +including without limitation the rights to use, copy, modify, merge, +publish, distribute, sublicense, and/or sell copies of the Software, +and to permit persons to whom the Software is furnished to do so, +subject to the following conditions: + +The above copyright notice and this permission notice shall be +included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. ++------------------------------------------------------------------ diff --git a/pfth/linux.fth b/pfth/linux.fth new file mode 100644 index 0000000..4ecbc8f --- /dev/null +++ b/pfth/linux.fth @@ -0,0 +1,88 @@ +\ List the files in the current directory +: ls + get-file-struct + 0= if exit then + spi_currdir @ + begin + dup spi_buf spi_readblock drop + 512 0 do + i spi_buf + dup c@ + if + dup c@ 229 = \ Deleted file + over 11 + c@ 15 = or \ Long name + if + drop + else + dup 11 type bl emit + dup 28 + @ . + cr drop + then + else + r> r> 2drop 2drop exit + then + 32 +loop + 1+ + again +; + +\ Type the file to the console +: cat bl word count 0 open-file + if + drop + ." Could not open file" cr + else + begin + dup pad 100 rot read-line drop + 0 = if drop close-file drop exit then + pad swap type cr + again + then +; + +\ Remove a file +: rm bl word count delete-file + if + ." File does not exist" cr + then +; + +\ Copy a file to another file +: cp + bl word count 0 open-file + if + drop + ." Could not open input file" cr + exit + then + bl word count 0 create-file + if + drop + ." Could not open output file" cr + close-file drop + exit + then + begin + over pad 100 rot read-file drop dup + while + over pad -rot write-file drop + repeat + drop + close-file drop + close-file drop +; + +\ Copy a file to memory and then to another file +: cp1 bl word count 0 open-file + if + drop + ." Could not open file" cr + else + dup here unused rot read-file drop + swap close-file drop + bl word count 0 create-file drop + dup rot here swap rot + write-file drop + close-file drop + then +; + diff --git a/pfth/mount.spin b/pfth/mount.spin new file mode 100644 index 0000000..6f5e179 --- /dev/null +++ b/pfth/mount.spin @@ -0,0 +1,136 @@ +' This code is extracted from FSRW 2.6 + +con + SECTORSIZE = 512 + SECTORSHIFT = 9 + DIRSHIFT = 5 + + SPI_engine_cog = 0 + SPI_command = 1 + SPI_block_index = 2 + SPI_buffer_address = 3 + SD_rootdir = 4 + SD_filesystem = 5 + SD_clustershift = 6 + SD_dataregion = 7 + SD_fat1 = 8 + SD_sectorsperfat = 9 + SD_currdir = 10 + + ERR_BLOCK_NOT_LONG_ALIGNED = -4 + ERR_SPI_ENGINE_NOT_RUNNING = -999 + + +obj + sdspi : "safe_spi" + +dat + pdate long 0 + lastread long 0 + dirty long 0 + filesystem long 0 + clustershift long 0 + clustersize long 0 + fat1 long 0 + sectorsperfat long 0 + dataregion long 0 + rootdir long 0 + rootdirend long 0 + endofchain long 0 + totclusters long 0 + buf byte 0[512] + +pri brword(b) +' +' Read a byte-reversed word from a (possibly odd) address. +' + return (byte[b]) + ((byte[b][1]) << 8) + +pri brlong(b) +' +' Read a byte-reversed long from a (possibly odd) address. +' + return brword(b) + (brword(b+2) << 16) + +pri getfstype : r + if (brlong(@buf+$36) == constant("F" + ("A" << 8) + ("T" << 16) + ("1" << 24)) and buf[$3a]=="6") + return 1 + if (brlong(@buf+$52) == constant("F" + ("A" << 8) + ("T" << 16) + ("3" << 24)) and buf[$56]=="2") + return 2 + ' return r (default return) + +pub mount_explicit(spi_vars, DO, CLK, DI, CS) : r | start, sectorspercluster, reserved, rootentries, sectors +{{ +' Mount a volume. The address passed in is passed along to the block +' layer; see the currently used block layer for documentation. If the +' volume mounts, a 0 is returned, else abort is called. +}} + if (pdate == 0) + pdate := constant(((2009-1980) << 25) + (1 << 21) + (27 << 16) + (7 << 11)) + 'unmount + sdspi.start_explicit(spi_vars, DO, CLK, DI, CS) + lastread := -1 + dirty := 0 + readblock(spi_vars, 0, @buf) + if (getfstype > 0) + start := 0 + else + start := brlong(@buf+$1c6) + readblock(spi_vars, start, @buf) + filesystem := getfstype + if (filesystem == 0) + abort(-20) ' not a fat16 or fat32 volume + if (brword(@buf+$0b) <> SECTORSIZE) + abort(-21) ' bad bytes per sector + sectorspercluster := buf[$0d] + if (sectorspercluster & (sectorspercluster - 1)) + abort(-22) ' bad sectors per cluster + clustershift := 0 + repeat while (sectorspercluster > 1) + clustershift++ + sectorspercluster >>= 1 + sectorspercluster := 1 << clustershift + clustersize := SECTORSIZE << clustershift + reserved := brword(@buf+$0e) + if (buf[$10] <> 2) + abort(-23) ' not two FATs + sectors := brword(@buf+$13) + if (sectors == 0) + sectors := brlong(@buf+$20) + fat1 := start + reserved + if (filesystem == 2) + rootentries := 16 << clustershift + sectorsperfat := brlong(@buf+$24) + dataregion := (fat1 + 2 * sectorsperfat) - 2 * sectorspercluster + rootdir := (dataregion + (brword(@buf+$2c) << clustershift)) << SECTORSHIFT + rootdirend := rootdir + (rootentries << DIRSHIFT) + endofchain := $ffffff0 + else + rootentries := brword(@buf+$11) + sectorsperfat := brword(@buf+$16) + rootdir := (fat1 + 2 * sectorsperfat) << SECTORSHIFT + rootdirend := rootdir + (rootentries << DIRSHIFT) + dataregion := 1 + ((rootdirend - 1) >> SECTORSHIFT) - 2 * sectorspercluster + endofchain := $fff0 + if (brword(@buf+$1fe) <> $aa55) + abort(-24) ' bad FAT signature + totclusters := ((sectors - dataregion + start) >> clustershift) + long[spi_vars][SD_rootdir] := rootdir >> SECTORSHIFT + long[spi_vars][SD_filesystem] := filesystem + long[spi_vars][SD_clustershift] := clustershift + long[spi_vars][SD_dataregion] := dataregion + long[spi_vars][SD_fat1] := fat1 + long[spi_vars][SD_sectorsperfat] := sectorsperfat + long[spi_vars][SD_currdir] := rootdir >> SECTORSHIFT + +PUB readblock(spi_vars, block_index, buffer_address) + if long[spi_vars][SPI_engine_cog] == 0 + abort ERR_SPI_ENGINE_NOT_RUNNING + if (buffer_address & 3) + abort ERR_BLOCK_NOT_LONG_ALIGNED + long[spi_vars][SPI_block_index] := block_index + long[spi_vars][SPI_buffer_address] := buffer_address + long[spi_vars][SPI_command] := "r" + repeat while long[spi_vars][SPI_command] == "r" + if long[spi_vars][SPI_command] < 0 + abort SPI_command \ No newline at end of file diff --git a/pfth/mountos.spin b/pfth/mountos.spin new file mode 100644 index 0000000..a94b4a1 --- /dev/null +++ b/pfth/mountos.spin @@ -0,0 +1,150 @@ +' This code is extracted from FSRW 2.6 + +con + SECTORSIZE = 512 + SECTORSHIFT = 9 + DIRSHIFT = 5 + + 'SPI_engine_cog = 0 + 'SPI_command = 1 + 'SPI_block_index = 2 + 'SPI_buffer_address = 3 + SD_rootdir = 4 + SD_filesystem = 5 + SD_clustershift = 6 + SD_dataregion = 7 + SD_fat1 = 8 + SD_sectorsperfat = 9 + SD_currdir = 10 + + ERR_BLOCK_NOT_LONG_ALIGNED = -4 + ERR_SPI_ENGINE_NOT_RUNNING = -999 + + +obj + sdspi : "safe_spi" + sys : "sysdefs" + +dat + pdate long 0 + lastread long 0 + dirty long 0 + filesystem long 0 + clustershift long 0 + clustersize long 0 + fat1 long 0 + sectorsperfat long 0 + dataregion long 0 + rootdir long 0 + rootdirend long 0 + endofchain long 0 + totclusters long 0 + buf byte 0[512] + +pri brword(b) +' +' Read a byte-reversed word from a (possibly odd) address. +' + return (byte[b]) + ((byte[b][1]) << 8) + +pri brlong(b) +' +' Read a byte-reversed long from a (possibly odd) address. +' + return brword(b) + (brword(b+2) << 16) + +pri getfstype : r + if (brlong(@buf+$36) == constant("F" + ("A" << 8) + ("T" << 16) + ("1" << 24)) and buf[$3a]=="6") + return 1 + if (brlong(@buf+$52) == constant("F" + ("A" << 8) + ("T" << 16) + ("3" << 24)) and buf[$56]=="2") + return 2 + ' return r (default return) + +pub mount_explicit(spi_vars) : r | start, sectorspercluster, reserved, rootentries, sectors +{{ +' Mount a volume. The address passed in is passed along to the block +' layer; see the currently used block layer for documentation. If the +' volume mounts, a 0 is returned, else abort is called. +}} + if (pdate == 0) + pdate := constant(((2009-1980) << 25) + (1 << 21) + (27 << 16) + (7 << 11)) + 'unmount + 'sdspi.start_explicit(spi_vars, DO, CLK, DI, CS) + lastread := -1 + dirty := 0 + readblock(0, @buf) + if (getfstype > 0) + start := 0 + else + start := brlong(@buf+$1c6) + readblock(start, @buf) + filesystem := getfstype + if (filesystem == 0) + abort(-20) ' not a fat16 or fat32 volume + if (brword(@buf+$0b) <> SECTORSIZE) + abort(-21) ' bad bytes per sector + sectorspercluster := buf[$0d] + if (sectorspercluster & (sectorspercluster - 1)) + abort(-22) ' bad sectors per cluster + clustershift := 0 + repeat while (sectorspercluster > 1) + clustershift++ + sectorspercluster >>= 1 + sectorspercluster := 1 << clustershift + clustersize := SECTORSIZE << clustershift + reserved := brword(@buf+$0e) + if (buf[$10] <> 2) + abort(-23) ' not two FATs + sectors := brword(@buf+$13) + if (sectors == 0) + sectors := brlong(@buf+$20) + fat1 := start + reserved + if (filesystem == 2) + rootentries := 16 << clustershift + sectorsperfat := brlong(@buf+$24) + dataregion := (fat1 + 2 * sectorsperfat) - 2 * sectorspercluster + rootdir := (dataregion + (brword(@buf+$2c) << clustershift)) << SECTORSHIFT + rootdirend := rootdir + (rootentries << DIRSHIFT) + endofchain := $ffffff0 + else + rootentries := brword(@buf+$11) + sectorsperfat := brword(@buf+$16) + rootdir := (fat1 + 2 * sectorsperfat) << SECTORSHIFT + rootdirend := rootdir + (rootentries << DIRSHIFT) + dataregion := 1 + ((rootdirend - 1) >> SECTORSHIFT) - 2 * sectorspercluster + endofchain := $fff0 + if (brword(@buf+$1fe) <> $aa55) + abort(-24) ' bad FAT signature + totclusters := ((sectors - dataregion + start) >> clustershift) + long[spi_vars][SD_rootdir] := rootdir >> SECTORSHIFT + long[spi_vars][SD_filesystem] := filesystem + long[spi_vars][SD_clustershift] := clustershift + long[spi_vars][SD_dataregion] := dataregion + long[spi_vars][SD_fat1] := fat1 + long[spi_vars][SD_sectorsperfat] := sectorsperfat + long[spi_vars][SD_currdir] := rootdir >> SECTORSHIFT + +{ +PUB readblock(spi_vars, block_index, buffer_address) + if long[spi_vars][SPI_engine_cog] == 0 + abort ERR_SPI_ENGINE_NOT_RUNNING + if (buffer_address & 3) + abort ERR_BLOCK_NOT_LONG_ALIGNED + long[spi_vars][SPI_block_index] := block_index + long[spi_vars][SPI_buffer_address] := buffer_address + long[spi_vars][SPI_command] := "r" + repeat while long[spi_vars][SPI_command] == "r" + if long[spi_vars][SPI_command] < 0 + abort SPI_command +} +PUB readblock(block_index, buffer_address) + if long[sys#SPI_engine_cog] == 0 + abort ERR_SPI_ENGINE_NOT_RUNNING + if (buffer_address & 3) + abort ERR_BLOCK_NOT_LONG_ALIGNED + long[sys#SPI_block_index] := block_index + long[sys#SPI_buffer_address] := buffer_address + long[sys#SPI_command] := "r" + repeat while long[sys#SPI_command] == "r" + if long[sys#SPI_command] < 0 + abort long[sys#SPI_command] diff --git a/pfth/ospfth.spin b/pfth/ospfth.spin new file mode 100644 index 0000000..871dd67 --- /dev/null +++ b/pfth/ospfth.spin @@ -0,0 +1,1567 @@ +{ +############################################################################ +# PFTH - This program implements a Forth interpreter. +# +# Copyright (c) 2012, 2013 Dave Hein +# MIT Licensed +############################################################################ +} +con + '_clkmode = xtal1+pll16x + '_clkfreq = 80_000_000 + _clkmode = xinput + _xinfreq = $4e495053 ' SPIN + + + ' SD Pin Definitions + 'DO = 10 + 'CLK = 11 + 'DI = 9 + 'CS = 25 + + Q = 16 ' Object Offset + + FLAG_IMMEDIATE = 1 + FLAG_CORE = $10 | $80 + FLAG_LIT = $12 | $80 + FLAG_VAR = $20 | $80 + FLAG_DEF = $00 | $80 + FLAG_JMP = $0A | $80 + FLAG_SEMI = FLAG_CORE | FLAG_IMMEDIATE + +obj + spi : "mountos" + +'******************************************************************************* +' This Spin code waits three seconds and then starts the Forth cog +'******************************************************************************* +pub main(argc, argv) + 'waitcnt(clkfreq+cnt) + 'spi.mount_explicit(@spi_vars, DO, CLK, DI, CS) + config(argc, argv) + coginit(cogid, @forth, @pfthconfig) + +' Get the Spinix configuration, which +' is located after the last argument +PUB config(argc, argv) | ptr + spi.mount_explicit(@spi_vars) + hostcwd_B := GetVarVal(string("PWD")) + argc_B := argc + argv_B := argv + +PUB FindVar(str) | ptr + ptr := $7e50 + repeat while byte[ptr] + if strcomp(str, ptr+1) + if byte[ptr] <> "A" + return ptr + ptr += strsize(ptr) + 1 + ptr += strsize(ptr) + 1 + +PUB GetVarVal(name) | ptr + if (ptr := FindVar(name)) + return ptr + strsize(ptr) + 1 + return @rootstr + +dat +rootstr byte "/", 0, 0, 0 +pfthconfig long @xboot_1+Q ' Initial word to execute + long @stack+Q+16 ' Starting stack pointer + long @stack+Q+16 ' Empty stack pointer value + long @retstk+Q ' Starting return pointer + long @retstk+Q ' Empty return pointer value +stack long 0[100] ' Data stack +retstk long 0[100] ' Return stack + +'******************************************************************************* +' pfth cog code +'******************************************************************************* + org 0 +forth +parm mov parm, par +parm1 rdlong pc, parm +parm2 add parm, #4 +parm3 rdlong stackptr, parm +parm4 add parm, #4 +temp rdlong stackptr0, parm +temp1 add parm, #4 +temp2 rdlong returnptr, parm +temp3 add parm, #4 +temp4 rdlong returnptr0, parm + jmp #innerloop ' Begin execution + +'******************************************************************************* +' Execute the words contained in the body of a word +' Changes parm, temp1 +'******************************************************************************* +execlistfunc add parm, #4 ' Get body from XT +innerloopcall wrlong pc, returnptr ' Push PC to return stack + add returnptr, #4 + mov pc, parm ' Set new value for PC + +'******************************************************************************* +' Get an execution token from the location pointed to by the program counter +' Increment the program counter, fetch the code pointer and jump to it +' Changes parm, temp1, pc +'******************************************************************************* +innerloop rdword parm, pc wz + if_z jmp #exitfunc + add pc, #2 + rdword temp1, parm + jmp temp1 + +pc long @xboot_1+Q ' Program Counter + +'******************************************************************************* +' Stop executing the current word, and return to the calling word +' No Changes +'******************************************************************************* +exitfunc sub returnptr, #4 + rdlong pc, returnptr + jmp #innerloop + +'******************************************************************************* +' Abort or quit execution, and return to the interpreter +' No Changes +'******************************************************************************* +abortfunc mov stackptr, stackptr0 +quitfunc mov returnptr, returnptr0 + add returnptr, #4 ' Use second entry return stack + rdlong pc, returnptr + jmp #innerloop + +'******************************************************************************* +' Push the value contained in the word's body onto the stack +' No changes +'******************************************************************************* +confunc add parm, #4 + rdlong parm1, parm + jmp #push_jmp + +'******************************************************************************* +' Push the address of the word's body onto the stack +' Execute the words pointed to by the does pointer, if non-zero +' No changes +'******************************************************************************* +varfunc mov parm1, parm + add parm1, #4 + call #push + +'******************************************************************************* +' Execute the words pointed to by the does pointer, if non-zero +' No changes +'******************************************************************************* +deferfunc add parm, #2 ' DOES> pointer + rdword parm, parm wz + if_z jmp #innerloop ' Done with varfunc + jmp #innerloopcall ' Execute DOES> code + +'******************************************************************************* +' Execute the word on the stack +' Changes parm, temp1 +'******************************************************************************* +executefunc sub stackptr, #4 + rdlong parm, stackptr + rdlong temp1, parm + jmp temp1 ' Execute code + +'******************************************************************************* +' Execute the PASM instruction on the TOS using the next value on the stack as +' the destination register data. Return the result on the stack. +' Changes parm1, parm2 +'******************************************************************************* +cogx1func call #pop2 + mov cogx1instr, parm2 + movd cogx1instr, #parm1 + nop +cogx1instr nop + jmp #push_jmp + +'******************************************************************************* +' Duplicate the top of stack +' Changes parm1 +'******************************************************************************* +dupfunc call #pop + call #push + jmp #push_jmp + +'******************************************************************************* +' Swap the top two items on the stack +' Changes parm1, parm2 +'******************************************************************************* +swapfunc call #pop2 + wrlong parm2, stackptr + add stackptr, #4 + jmp #push_jmp + +'******************************************************************************* +' Get the next word from the input buffer using the delimiter from the stack +' Changes parm, parm1, parm2, temp1, temp2 +'******************************************************************************* +wordfunc sub stackptr, #4 + rdlong parm, stackptr + call #word_del + mov temp1, a_wordbuf +' mov temp1, #1 +' shl temp1, #15 +' sub temp1, parm2 +' sub temp1, #1 + wrlong temp1, stackptr + add stackptr, #4 + wrbyte parm2, temp1 + cmps parm2, #0 wc, wz + if_c_or_z jmp #innerloop +:loop add temp1, #1 + rdbyte temp2, parm1 + add parm1, #1 + wrbyte temp2, temp1 + djnz parm2, #:loop + jmp #innerloop + +'******************************************************************************* +' Find the word specfied on the stack in the dictionary +' Changes parm1, parm2, temp4 +'******************************************************************************* +findfunc call #pop + mov temp4, parm1 + add parm1, #1 + rdbyte parm2, temp4 + call #findword + mov parm1, parm wz + if_z jmp #findfunc1 + call #link2xt + call #push + add parm, #2 ' Point to flag byte + rdbyte parm1, parm + and parm1, #1 ' Check immediate bit + shl parm1, #1 + sub parm1, #1 ' Return 1 if set, -1 if not + call #push + jmp #innerloop +findfunc1 mov parm1, temp4 + call #push + mov parm1, #0 + call #push + jmp #innerloop + +'******************************************************************************* +' Send the character from the stack to the output port +' Changes parm +'******************************************************************************* +emitfunc call #pop + mov parm, parm1 + call #putch + jmp #innerloop + +'******************************************************************************* +' Get a character from the input port and put it on the stack +' Changes parm, parm1 +'******************************************************************************* +getcharfunc call #getch + mov parm1, parm + jmp #push_jmp + +'******************************************************************************* +' Get a character from the files stored in memory and put it on the stack +' Changes parm1 +'******************************************************************************* +getfcharfunc rdbyte parm1, infileptr + add infileptr, #1 + jmp #push_jmp + +'******************************************************************************* +' Get an address and value from the stack, and store the value at the address +' No changes +'******************************************************************************* +storefunc call #pop2 + wrlong parm1, parm2 + jmp #innerloop + +'******************************************************************************* +' Fetch a value from the address specified on the stack, and put it on the stack +' Changes parm1 +'******************************************************************************* +fetchfunc call #pop + rdlong parm1, parm1 + jmp #push_jmp + +'******************************************************************************* +' Get an address and word from the stack, and store the word at the address +' No changes +'******************************************************************************* +wstorefunc call #pop2 + wrword parm1, parm2 + jmp #innerloop + +'******************************************************************************* +' Fetch a word from the address specified on the stack, and put it on the stack +' Changes parm1 +'******************************************************************************* +wfetchfunc call #pop + rdword parm1, parm1 + jmp #push_jmp + +'******************************************************************************* +' Get an address and byte from the stack, and store the byte at the address +' No changes +'******************************************************************************* +cstorefunc call #pop2 + wrbyte parm1, parm2 + jmp #innerloop + +'******************************************************************************* +' Fetch a byte from the address specified on the stack, and put it on the stack +' Changes parm1 +'******************************************************************************* +cfetchfunc call #pop + rdbyte parm1, parm1 + jmp #push_jmp + +'******************************************************************************* +' Add two values from the stack, and write the result back to the stack +' Changes parm1, parm2 +'******************************************************************************* +plusfunc call #pop2 + add parm1, parm2 + jmp #push_jmp + +'******************************************************************************* +' Subtract two values from the stack, and write the result back to the stack +' Changes parm1, parm2 +'******************************************************************************* +minusfunc call #pop2 + sub parm1, parm2 + jmp #push_jmp + +'******************************************************************************* +' Multiply two values from the stack, and write the result back to the stack +' Changes parm1, parm2 +'******************************************************************************* +multfunc call #pop2 + call #multiply + jmp #push_jmp + +'******************************************************************************* +' Divide two values from the stack, and write the result back to the stack +' Changes parm1, parm2 +'******************************************************************************* +dividefunc call #pop2 + call #divide + mov parm1, parm2 + test parm3, #1 wc + if_c neg parm1, parm1 + jmp #push_jmp + +'******************************************************************************* +' Compute the modulus from two values from the stack, and write the result back +' to the stack +' Changes parm1, parm2 +'******************************************************************************* +modfunc call #pop2 + call #divide + test parm3, #2 wc + if_c neg parm1, parm1 + jmp #push_jmp + +'******************************************************************************* +' Compare two values from the stack to determine if the second one is less than +' the first one, and write the result back to the stack +' Changes parm1, parm2 +'******************************************************************************* +lessfunc call #pop2 + cmps parm1, parm2 wc + if_c neg parm1, #1 + if_nc mov parm1, #0 + jmp #push_jmp + +'******************************************************************************* +' Compare two values from the stack to determine if they are equal, and write +' the result back to the stack +' Changes parm1, parm2 +'******************************************************************************* +equalfunc call #pop2 + cmp parm1, parm2 wz + if_z neg parm1, #1 + if_nz mov parm1, #0 + jmp #push_jmp + +'******************************************************************************* +' Compare two values from the stack to determine if the second one is greater +' than the first one, and write the result back to the stack +' Changes parm1, parm2 +'******************************************************************************* +greaterfunc call #pop2 + cmps parm1, parm2 wc, wz + if_nz_and_nc neg parm1, #1 + if_z_or_c mov parm1, #0 + jmp #push_jmp + +'******************************************************************************* +' Compute the logical AND of two values from the stack, and write the result +' back to the stack +' Changes parm1, parm2 +'******************************************************************************* +andfunc call #pop2 + and parm1, parm2 + jmp #push_jmp + +'******************************************************************************* +' Compute the logical OR of two values from the stack, and write the result +' back to the stack +' Changes parm1, parm2 +'******************************************************************************* +orfunc call #pop2 + or parm1, parm2 + jmp #push_jmp + +'******************************************************************************* +' Compute the logical XOR of two values from the stack, and write the result +' back to the stack +' Changes parm1, parm2 +'******************************************************************************* +xorfunc call #pop2 + xor parm1, parm2 + jmp #push_jmp + +'******************************************************************************* +' Right-shift the second value on the stack by the number of bits specified by +' the first value on the stack, and write the result to the stack +' Changes parm1, parm2 +'******************************************************************************* +rshiftfunc call #pop2 + shr parm1, parm2 + jmp #push_jmp + +'******************************************************************************* +' Left-shift the second value on the stack by the number of bits specified by +' the first value on the stack, and write the result to the stack +' Changes parm1, parm2 +'******************************************************************************* +lshiftfunc call #pop2 + shl parm1, parm2 + jmp #push_jmp + +'******************************************************************************* +' Push the stack depth to the stack +' Changes parm1 +'******************************************************************************* +depthfunc mov parm1, stackptr + sub parm1, stackptr0 + sar parm1, #2 + jmp #push_jmp + +'******************************************************************************* +' Drop the top value from the stack +' No changes +'******************************************************************************* +dropfunc sub stackptr, #4 + jmp #innerloop + +'******************************************************************************* +' Use the value on top of the stack as an index to another value in the stack, +' and write its value to the stack +' No changes +'******************************************************************************* +pickfunc call #pop + call #indexstack + jmp #push_jmp + +'******************************************************************************* +' Use the value on top of the stack as and index to remove another value from +' the stack, and place it at the top of the stack. +' Changes temp1, temp2, temp3, temp4 +'******************************************************************************* +rollfunc call #pop + cmp parm1, #0 wc, wz + if_c_or_z jmp #innerloop + mov temp3, parm1 + call #indexstack + mov temp2, temp1 +:loop add temp2, #4 + rdlong temp4, temp2 + wrlong temp4, temp1 + add temp1, #4 + djnz temp3, #:loop + wrlong parm1, temp1 + jmp #innerloop + +'******************************************************************************* +' Pop the value from the top of the stack, and push it onto the return stack. +' No changes +'******************************************************************************* +torfunc call #pop + wrlong parm1, returnptr + add returnptr, #4 + jmp #innerloop + +'******************************************************************************* +' Pop the value from the top of the return stack and push it to the stack. +' Changes parm1 +'******************************************************************************* +fromrfunc sub returnptr, #4 + rdlong parm1, returnptr + jmp #push_jmp + +'******************************************************************************* +' Push the value on the stack pointed to by the PC and increment the PC +' Changes parm1 +'******************************************************************************* +_litfunc rdword parm1, pc + add pc, #2 + jmp #push_jmp + +'******************************************************************************* +' Convert the string described by the address and length on the top of the +' stack to a hex number, and push it to the stack +' Changes parm1 +'******************************************************************************* +_gethexfunc call #pop2 + call #gethex + mov parm1, parm + jmp #push_jmp + +'******************************************************************************* +' Create a variable, and add it to the dictionary +' Changes parm3 +'******************************************************************************* +createfunc mov parm3, #varfunc + mov parm4, #FLAG_VAR + call #create + jmp #innerloop + +'******************************************************************************* +' Create an executable word, and add it to the dictionary. Set the compile +' state to -1 +' Changes parm3, temp1 +'******************************************************************************* +colonfunc mov parm3, #execlistfunc + mov parm4, #FLAG_DEF + call #create + if_z jmp #innerloop + neg temp1, #1 + wrlong temp1, a_state + jmp #innerloop + +'******************************************************************************* +' Compile a zero into memory indicating the end of an executable word, and set +' the compile flag to zero +' Changes temp1, temp2 +'******************************************************************************* +semicolonfunc mov temp1, #0 + wrlong temp1, a_state + rdlong temp2, a_dp + wrword temp1, temp2 + add temp2, #2 + wrlong temp2, a_dp + jmp #innerloop + +'******************************************************************************* +' Fetch a value from the specified cog address, and put it on the stack +' the compile flag to zero +' Changes parm1 +'******************************************************************************* +cogfetchfunc call #pop + movs cogfetch1, parm1 + nop +cogfetch1 mov parm1, 0-0 + jmp #push_jmp + +'******************************************************************************* +' Get a cog address and value from the stack, and store the value at the address +' the compile flag to zero +' Changes parm1, parm2 +'******************************************************************************* +cogstorefunc call #pop2 + movd cogstore1, parm2 + nop +cogstore1 mov 0-0, parm1 + jmp #innerloop + + +'******************************************************************************* +' Print out an 8-digit hex number to the output port. +' Changes parm +'******************************************************************************* +dotxfunc mov parm, #"$" + call #putch + call #pop + call #printhex + mov parm, #" " + call #putch + jmp #innerloop + +'******************************************************************************* +' If top of stack is zero, jump to address contained in location at current PC. +' Otherwise, increment the PC +' Changes parm1 +'******************************************************************************* +_jzfunc call #pop + if_z rdword pc, pc + if_nz add pc, #2 + jmp #innerloop + +'******************************************************************************* +' Copy bytes from the source to the destination +' Changes parm1 +'******************************************************************************* +cmovefunc sub stackptr, #4 + rdlong parm3, stackptr + call #pop2 + cmps parm3, #0 wz, wc + if_c_or_z jmp #innerloop +:loop rdbyte temp1, parm1 + add parm1, #1 + wrbyte temp1, parm2 + add parm2, #1 + djnz parm3, #:loop + jmp #innerloop + +'******************************************************************************* +' Perform the increment and compare for the loop word +' Changes parm1, parm2, parm3 +'******************************************************************************* +_loopfunc call #pop ' Get increment + sub returnptr, #8 + rdlong parm3, returnptr ' Get upper limit + add returnptr, #4 + rdlong parm2, returnptr ' Get index + add parm1, parm2 ' index + increment + wrlong parm1, returnptr ' Push index back + add returnptr, #4 + cmps parm1, parm3 wc + if_nc neg parm1, #1 + if_c mov parm1, #0 + jmp #push_jmp + +'******************************************************************************* +' The following code implements the basic functions used by the kernel words +'******************************************************************************* + +'******************************************************************************* +' Create a word entry in the dictionary +' Changes parm, parm1, parm2, temp1, temp2 +'******************************************************************************* +create mov parm, #" " + call #word_del + if_z jmp create_ret + rdlong temp1, a_dp ' Align DP + add temp1, #3 + and temp1, minus4 + rdlong temp2, a_last + wrword temp2, temp1 ' Write the link pointer + wrlong temp1, a_last ' Update LAST + add temp1, #2 + + wrbyte parm4, temp1 ' Write the flag + add temp1, #1 + wrbyte parm2, temp1 ' Write the length + add temp1, #1 + cmps parm2, #0 wc, wz + if_c_or_z jmp #create_done +:loop rdbyte temp2, parm1 ' Copy the name + add parm1, #1 + wrbyte temp2, temp1 + add temp1, #1 wz + djnz parm2, #:loop + +create_done mov temp2, #0 ' Pad with 0's to align +:loop1 test temp1, #3 wz + if_z jmp #create_aligned + wrbyte temp2, temp1 + add temp1, #1 + jmp #:loop1 + +create_aligned wrword parm3, temp1 ' Write the code pointer + add temp1, #2 + wrword temp2, temp1 ' Write the DOES> pointer + add temp1, #2 wz ' Clear zero flag + + wrlong temp1, a_dp +create_ret ret + +'******************************************************************************* +' Get one character from the input port. +' Input none +' Changes parm, temp, temp1, temp2 +' Output parm +'******************************************************************************* +getch mov parm, ina + and parm, inbit wz + if_nz jmp #getch + mov temp2, cnt + mov temp, bitcycles + shr temp, #1 + add temp2, temp + mov temp1, #10 +:loop waitcnt temp2, bitcycles + mov temp, ina + and temp, inbit + ror parm, #1 + or parm, temp + djnz temp1, #:loop + ror parm, #31 - 8 + and parm, #255 +getch_ret ret + +inbit long $80000000 +bitcycles long 80_000_000 / 115_200 + +'******************************************************************************* +' Send one character to the output port. +' Input parm +' Changes parm, temp1, temp2 +' Output none +'******************************************************************************* +putch rdlong temp1, a_verbose wz + if_z jmp putch_ret + or parm, #$100 + shl parm, #1 + mov temp1, #10 + mov temp2, bitcycles + add temp2, cnt +:loop shr parm, #1 wc + if_c or outa, outbit + if_nc andn outa, outbit + waitcnt temp2, bitcycles + djnz temp1, #:loop +putch_ret ret + +outbit long $40000000 + +'******************************************************************************* +' Skip the specified character in the input buffer +' Input parm +' Changes temp, temp1 +' Output none +'******************************************************************************* +skipchar cmps temp1, temp2 wc + if_nc jmp skipchar_ret + rdlong temp, a_tib + add temp, temp1 + rdbyte temp, temp + cmp temp, parm wz + if_nz jmp skipchar_ret + add temp1, #1 + jmp #skipchar +skipchar_ret ret + +'******************************************************************************* +' Find the next occurance of the specified character in the input buffer +' Input parm +' Changes temp, temp1 +' Output none +'******************************************************************************* +findchar cmps temp1, temp2 wc + if_nc jmp findchar_ret + rdlong temp, a_tib + add temp, temp1 + rdbyte temp, temp + cmp temp, parm wz + if_z jmp findchar_ret + add temp1, #1 + jmp #findchar +findchar_ret ret + +'******************************************************************************* +' Find the next word in the input buffer delimited by the specified character +' Input parm +' Changes parm1, parm2, temp1, temp2 +' Output none +'******************************************************************************* +word_del + rdlong temp1, a_inputidx + rdlong temp2, a_inputlen + call #skipchar + mov parm1, temp1 + call #findchar + mov parm2, temp1 + sub parm2, parm1 wz + rdlong temp, a_tib + add parm1, temp + cmps temp1, temp2 wc + if_c add temp1, #1 + wrlong temp1, a_inputidx +word_del_ret ret + +'******************************************************************************* +' Find the specified word in the dictionary +' Input parm1, parm2 +' Changes parm, parm3, parm4 +' Output parm +'******************************************************************************* +findword rdlong parm, a_last wz + if_z jmp findword_ret +:loop mov parm3, parm + add parm3, #3 + rdbyte parm4, parm3 + add parm3, #1 + call #compare + if_z jmp findword_ret + rdword parm, parm wz + if_nz jmp #:loop +findword_ret ret + +'******************************************************************************* +' Do a case insensitive comparison of two character strings +' Input parm1, parm2, parm3, parm4 +' Changes parm3, parm4, temp, temp1, temp2 +' Outut Z +'******************************************************************************* +compare cmps parm2, #1 wc, wz + if_c jmp compare_ret + cmp parm2, parm4 wz + if_nz jmp compare_ret + mov temp, parm1 +:loop rdbyte temp1, temp + call #toupper + mov temp2, temp1 + rdbyte temp1, parm3 + call #toupper + cmp temp1, temp2 wz + if_nz jmp compare_ret + add temp, #1 + add parm3, #1 + djnz parm4, #:loop +compare_ret ret + +'******************************************************************************* +' Convert a character to uppercase +' Input temp1 +' Changes temp1 +' Ouput temp1 +'******************************************************************************* +toupper cmp temp1, #"a" wc + if_c jmp toupper_ret + cmp temp1, #"z" wc, wz + if_nc_and_nz jmp toupper_ret + sub temp1, #"a" - "A" +toupper_ret ret + + +'******************************************************************************* +' Print an 8-digit hex value to the output port +' Input parm1 +' Changes parm, parm1, parm2 +' Output none +'******************************************************************************* +printhex mov parm2, #8 +:loop rol parm1, #4 + mov parm, #15 + and parm, parm1 + add parm, a_hexstr + rdbyte parm, parm + call #putch + djnz parm2, #:loop +printhex_ret ret + + +'******************************************************************************* +' Convert a string to a hex number +' Input parm1, parm2 +' Changes parm, temp, temp1, temp2 +' Output parm +'******************************************************************************* +gethex mov parm, #0 + cmps parm2, #0 wc, wz + if_c_or_z jmp gethex_ret + mov temp1, parm1 + mov temp2, parm2 +:loop rdbyte temp, temp1 + add temp1, #1 + sub temp, #"0" + cmps temp, #10 wc + if_nc sub temp, #"a"-"0"-10 + shl parm, #4 + add parm, temp + djnz temp2, #:loop +gethex_ret ret + +'******************************************************************************* +' Push a value onto the data stack +' Input parm1 +' No changes +' Output none +'******************************************************************************* +push wrlong parm1, stackptr + add stackptr, #4 +push_ret ret + +'******************************************************************************* +' Push a value onto the data stack and jump to the innerloop +' Input parm1 +' No changes +' Output none +'******************************************************************************* +push_jmp wrlong parm1, stackptr + add stackptr, #4 + jmp #innerloop + +'******************************************************************************* +' Pop two values off of the data stack +' Input none +' Changes parm1, parm2 +' Output parm1, parm2 +'******************************************************************************* +pop2 sub stackptr, #4 + rdlong parm2, stackptr + +'******************************************************************************* +' Pop one value off of the data stack +' Input none +' Changes parm1 +' Ouput parm1 +'******************************************************************************* +pop sub stackptr, #4 + rdlong parm1, stackptr wz +pop_ret +pop2_ret ret + +'******************************************************************************* +' Read a value on the stack based on an index number +' Changes parm1, temp1 +'******************************************************************************* +indexstack neg temp1, parm1 + shl temp1, #2 + sub temp1, #4 + add temp1, stackptr + rdlong parm1, temp1 +indexstack_ret ret + +'******************************************************************************* +' Compute the XT from the address of the link +' Input: parm1 +' Output: parm1 +' Changes: temp1 +'******************************************************************************* +link2xt mov temp1, parm1 + add temp1, #3 + rdbyte parm1, temp1 ' Get name length + add parm1, temp1 + add parm1, #4 + and parm1, minus4 ' Align +link2xt_ret ret + +'******************************************************************************* +' Multiply two 32-bit numbers +' Changes parm2, temp1, temp2 +'******************************************************************************* +multiply mov temp1, #0 + mov temp2, #32 + shr parm1, #1 wc +mmul if_c add temp1, parm2 wc + rcr temp1, #1 wc + rcr parm1, #1 wc + djnz temp2, #mmul +multiply_ret ret + +'******************************************************************************* +' Divide two 32-bit numbers producing a quotient and a remainder +' Changes parm1, parm2, parm3, temp1, temp2 +'******************************************************************************* +divide mov temp2, #32 + mov temp1, #0 + abs parm1, parm1 wc + muxc parm3, #%11 + abs parm2, parm2 wc,wz + if_c xor parm3, #%01 +' if_nz jmp #mdiv +' mov parm1, #0 +' jmp divide_ret +mdiv shr parm2, #1 wc,wz + rcr temp1, #1 + if_nz djnz temp2, #mdiv +mdiv2 cmpsub parm1, temp1 wc + rcl parm2, #1 + shr temp1, #1 + djnz temp2, #mdiv2 +divide_ret ret + +'******************************************************************************* +' These are working registers. The parm registers are generally used to pass +' parameters from one routine to another, and the temp registers are used as +' temporary storage within a routine. +'******************************************************************************* + +'******************************************************************************* +' Addresses of variables in the dictionary, and the hex table +'******************************************************************************* +a_hexstr long @hexstr+Q +a_last long @last+Q +a_state long @state+Q +a_dp long @dp+Q +a_tib long @tib+Q +a_verbose long @verbose+Q +a_inputidx long @greaterin+Q +a_inputlen long @poundtib+Q +a_wordbuf long @wordbuf+Q + +'******************************************************************************* +' The data and return stack pointers, and their base addresses +'******************************************************************************* +stackptr long 0 +stackptr0 long 0 +returnptr long 0 +returnptr0 long 0 + +'******************************************************************************* +' The input file pointer used during initialization +'******************************************************************************* +infileptr long @infile+Q + +'******************************************************************************* +' Constants +'******************************************************************************* +minus4 long -4 + + fit $1f0 + +'******************************************************************************* +' Input buffer and hex table +'******************************************************************************* +hexstr byte "0123456789abcdef" +inputbuf byte 0[200] +wordbuf byte 0[200] + +'******************************************************************************* +' This is the beginning of the dictionary. The kernel words are specified below +'******************************************************************************* +exit_L word 0 + byte FLAG_CORE, 4, "exit" + long +exit_X word exitfunc, 0 + +quit_L word @exit_L+Q + byte FLAG_CORE, 4, "quit" + long +quit_X word quitfunc, 0 + +abort_L word @quit_L+Q + byte FLAG_CORE, 5, "abort" + long +abort_X word abortfunc, 0 + +execute_L word @abort_L+Q + byte FLAG_CORE, 7, "execute" + long +execute_X word executefunc, 0 + +word_L word @execute_L+Q + byte FLAG_CORE, 4, "word" + long +word_X word wordfunc, 0 + +find_L word @word_L+Q + byte FLAG_CORE, 4, "find" + long +find_X word findfunc, 0 + +getchar_L word @find_L+Q + byte FLAG_CORE, 7, "getchar" + long +getchar_X word getcharfunc, 0 + +getfchar_L word @getchar_L+Q + byte FLAG_CORE, 8, "getfchar" + long +getfchar_X word getfcharfunc, 0 + +key_L word @getfchar_L+Q + byte FLAG_CORE, 3, "key" + long +key_X word deferfunc, @key_B+Q +key_B word @getfchar_X+Q, 0 + +create_L word @key_L+Q + byte FLAG_CORE, 6, "create" + long +create_X word createfunc, 0 + +_lit_L word @create_L+Q + byte FLAG_LIT, 4, "_lit" + long +_lit_X word _litfunc, 0 + +_gethex_L word @_lit_L+Q + byte FLAG_CORE, 7, "_gethex" + long +_gethex_X word _gethexfunc, 0 + +emit_L word @_gethex_L+Q + byte FLAG_CORE, 4, "emit" + long +emit_X word emitfunc, 0 + +store_L word @emit_L+Q + byte FLAG_CORE, 1, "!" + long +store_X word storefunc, 0 + +fetch_L word @store_L+Q + byte FLAG_CORE, 1, "@" + long +fetch_X word fetchfunc, 0 + +wstore_L word @fetch_L+Q + byte FLAG_CORE, 2, "w!" + long +wstore_X word wstorefunc, 0 + +wfetch_L word @wstore_L+Q + byte FLAG_CORE, 2, "w@" + long +wfetch_X word wfetchfunc, 0 + +cstore_L word @wfetch_L+Q + byte FLAG_CORE, 2, "c!" + long +cstore_X word cstorefunc, 0 + +cfetch_L word @cstore_L+Q + byte FLAG_CORE, 2, "c@" + long +cfetch_X word cfetchfunc, 0 + +plus_L word @cfetch_L+Q + byte FLAG_CORE, 1, "+" + long +plus_X word plusfunc, 0 + +minus_L word @plus_L+Q + byte FLAG_CORE, 1, "-" + long +minus_X word minusfunc, 0 + +multiply_L word @minus_L+Q + byte FLAG_CORE, 1, "*" + long +multiply_X word multfunc, 0 + +divide_L word @multiply_L+Q + byte FLAG_CORE, 1, "/" + long +divide_X word dividefunc, 0 + +mod_L word @divide_L+Q + byte FLAG_CORE, 3, "mod" + long +mod_X word modfunc, 0 + +and_L word @mod_L+Q + byte FLAG_CORE, 3, "and" + long +and_X word andfunc, 0 + +or_L word @and_L+Q + byte FLAG_CORE, 2, "or" + long +or_X word orfunc, 0 + +xor_L word @or_L+Q + byte FLAG_CORE, 3, "xor" + long +xor_X word xorfunc, 0 + +less_L word @xor_L+Q + byte FLAG_CORE, 1, "<" + long +less_X word lessfunc, 0 + +equal_L word @less_L+Q + byte FLAG_CORE, 1, "=" + long +equal_X word equalfunc, 0 + +greater_L word @equal_L+Q + byte FLAG_CORE, 1, ">" + long +greater_X word greaterfunc, 0 + +rshift_L word @greater_L+Q + byte FLAG_CORE, 6, "rshift" + long +rshift_X word rshiftfunc, 0 + +lshift_L word @rshift_L+Q + byte FLAG_CORE, 6, "lshift" + long +lshift_X word lshiftfunc, 0 + +depth_L word @lshift_L+Q + byte FLAG_CORE, 5, "depth" + long +depth_X word depthfunc, 0 + +tib_L word @depth_L+Q + byte FLAG_VAR, 3, "tib" + long +tib_X word varfunc, @tib+Q+4 +tib long @inputbuf+Q + word @fetch_X+Q, 0 + long + +poundtib_L word @tib_L+Q + byte FLAG_VAR, 4, "#tib" + long +poundtib_X word varfunc, 0 +poundtib long 0 + +greaterin_L word @poundtib_L+Q + byte FLAG_VAR, 3, ">in" + long +greaterin_X word varfunc, 0 +greaterin long 0 + +dp_L word @greaterin_L+Q + byte FLAG_VAR, 2, "dp" + long +dp_X word varfunc, 0 +dp long @_here+Q + +last_L word @dp_L+Q + byte FLAG_VAR, 4, "last" + long +last_X word varfunc, 0 +last long @_last+Q + +state_L word @last_L+Q + byte FLAG_VAR, 5, "state" + long +state_X word varfunc, 0 +state long 0 + +base_L word @state_L+Q + byte FLAG_VAR, 4, "base" + long +base_X word varfunc, 0 +base long 16 + +verbose_L word @base_L+Q + byte FLAG_VAR, 7, "verbose" + long +verbose_X word varfunc, 0 +verbose long 0 + +forth_L word @verbose_L+Q + byte FLAG_VAR, 5, "forth" + long +forth_X word varfunc, 0 + long @forth+Q + +drop_L word @forth_L+Q + byte FLAG_CORE, 4, "drop" + long +drop_X word dropfunc, 0 + +dup_L word @drop_L+Q + byte FLAG_CORE, 3, "dup" + long +dup_X word dupfunc, 0 + +swap_L word @dup_L+Q + byte FLAG_CORE, 4, "swap" + long +swap_X word swapfunc, 0 + +pick_L word @swap_L+Q + byte FLAG_CORE, 4, "pick" + long +pick_X word pickfunc, 0 + +roll_L word @pick_L+Q + byte FLAG_CORE, 4, "roll" + long +roll_X word rollfunc, 0 + +tor_L word @roll_L+Q + byte FLAG_CORE, 2, ">r" + long +tor_X word torfunc, 0 + +fromr_L word @tor_L+Q + byte FLAG_CORE, 2, "r>" + long +fromr_X word fromrfunc, 0 + +colon_L word @fromr_L+Q + byte FLAG_CORE, 1, ":" + long +colon_X word colonfunc, 0 + +semicolon_L word @colon_L+Q + byte FLAG_SEMI, 1, ";" + long +semicolon_X word semicolonfunc, 0 + +cogfetch_L word @semicolon_L+Q + byte FLAG_CORE, 4, "cog@" + long +cogfetch_X word cogfetchfunc, 0 + +cogstore_L word @cogfetch_L+Q + byte FLAG_CORE, 4, "cog!" + long +cogstore_X word cogstorefunc, 0 + +cogx1_L word @cogstore_L+Q + byte FLAG_CORE, 5, "cogx1" + long +cogx1_X word cogx1func, 0 + +_jz_L word @cogx1_L+Q + byte FLAG_JMP, 3, "_jz" + long +_jz_X word _jzfunc, 0 + +cmove_L word @_jz_L+Q + byte FLAG_CORE, 5, "cmove" + long +cmove_X word cmovefunc, 0 + +dotx_L word @cmove_L+Q + byte FLAG_CORE, 2, ".x" + long +dotx_X word dotxfunc, 0 + +'******************************************************************************* +' SPI/SD Variables +'******************************************************************************* +spi_vars_L word @dotx_L+Q + byte FLAG_VAR, 8, "spi_vars" + long +spi_vars_X word varfunc, 0 +spi_vars long 0 ' SPI_engine_cog + long 0 ' SPI_command + long 0 ' SPI_block_index + long 0 ' SPI_buffer_address + long 0 ' SD_rootdir + long 0 ' SD_filesystem + long 0 ' SD_clustershift + long 0 ' SD_dataregion + long 0 ' SD_fat1 + long 0 ' SD_sectorsperfat + long 0 ' SD_currdir + +argc_L word @spi_vars_L+Q + byte FLAG_VAR, 4, "argc" + long +argc_X word confunc, 0 +argc_B long 0 + +argv_L word @argc_L+Q + byte FLAG_VAR, 4, "argv" + long +argv_X word confunc, 0 +argv_B long 0 + +hostcwd_L word @argv_L+Q + byte FLAG_VAR, 7, "hostcwd" + long +hostcwd_X word confunc, 0 +hostcwd_B long 0 + +'******************************************************************************* +' A small number of compiled words follow below. These are used by the boot +' interpreter. +'******************************************************************************* + ' : here dp @ ; +here_L word @hostcwd_L+Q + byte FLAG_DEF, 4, "here" + long +here_X word execlistfunc, 0 + word @dp_X+Q, @fetch_X+Q, 0 + long + + ' : allot dp @ + dp ! ; +allot_L word @here_L+Q + byte FLAG_DEF, 5, "allot" + long +allot_X word execlistfunc, 0 + word @dp_X+Q, @fetch_X+Q, @plus_X+Q, @dp_X+Q, @store_X+Q, 0 + long + + ' : , here ! 4 allot ; +comma_L word @allot_L+Q + byte FLAG_DEF, 1, "," + long +comma_X word execlistfunc, 0 + word @here_X+Q, @store_X+Q, @_lit_X+Q, 4, @allot_X+Q, 0 + long + + ' : _jmp r> @ >r ; +_jmp_L word @comma_L+Q + byte FLAG_JMP, 4, "_jmp" + long +_jmp_X word execlistfunc, 0 + word @fromr_X+Q, @wfetch_X+Q, @tor_X+Q, 0 + long + + ' : count 0 pick 1 + 1 roll c@ ; +count_L word @_jmp_L+Q + byte FLAG_DEF, 5, "count" + long +count_X word execlistfunc, 0 + word @_lit_X+Q, 0, @pick_X+Q, @_lit_X+Q, 1, @plus_X+Q, @_lit_X+Q, 1, @roll_X+Q, @cfetch_X+Q, 0 + long + + ' : accept ( addr size -- num ) \ Accept a string from the input source +accept_L word @count_L+Q + byte FLAG_DEF, 6, "accept" + long +accept_X word execlistfunc, 0 + ' >r dup + word @tor_X+Q, @dup_X+Q + ' r> dup 1 < _jz _accept4 +accept_1 word @fromr_X+Q, @dup_X+Q, @_lit_X+Q, 1, @less_X+Q, @_jz_X+Q, @accept_4+Q + ' drop swap - exit + word @drop_X+Q, @swap_X+Q, @minus_X+Q, @exit_X+Q + ' >r key +accept_4 word @tor_X+Q, @key_X+Q + ' dup 0d = over 0a = or + word @dup_X+Q, @_lit_X+Q, $0d, @equal_X+Q, @_lit_X+Q, 1, @pick_X+Q, @_lit_X+Q, $0a, @equal_X+Q, @or_X+Q + ' _jz _accept2 + word @_jz_X+Q, @accept_2+Q + ' cr drop swap - + word @_lit_X+Q, 13, @emit_X+Q, @_lit_X+Q, 10, @emit_X+Q, @drop_X+Q, @swap_X+Q, @minus_X+Q + ' r> drop exit + word @fromr_X+Q, @drop_X+Q, @exit_X+Q + ' dup 8 = _jz _accept3 +accept_2 word @dup_X+Q, @_lit_X+Q, 8, @equal_X+Q, @_jz_X+Q, @accept_3+Q + ' drop over over - _jz _accept1 + word @drop_X+Q, @_lit_X+Q, 1, @pick_X+Q, @_lit_X+Q, 1, @pick_X+Q, @minus_X+Q, @_jz_X+Q, @accept_1+Q + ' 1 - r> 1 + >r + word @_lit_X+Q, 1, @minus_X+Q, @fromr_X+Q, @_lit_X+Q, 1, @plus_X+Q, @tor_X+Q + ' 8 emit bl emit 8 emit _jmp _accept1 + word @_lit_X+Q, 8, @emit_X+Q, @_lit_X+Q, 32, @emit_X+Q, @_lit_X+Q, 8, @emit_X+Q, @_jmp_X+Q, @accept_1+Q + ' dup emit over c! 1 + +accept_3 word @dup_X+Q, @emit_X+Q, @_lit_X+Q, 1, @pick_X+Q, @cstore_X+Q, @_lit_X+Q, 1, @plus_X+Q + ' r> 1 - >r _jmp _accept1 + word @fromr_X+Q, @_lit_X+Q, 1, @minus_X+Q, @tor_X+Q, @_jmp_X+Q, @accept_1+Q, 0 + long + + ' : refill tib 200 accept #tib ! 0 >in ! ; +refill_L word @accept_L+Q + byte FLAG_DEF, 6, "refill" + long +refill_X word execlistfunc, 0 + word @tib_X+Q, @_lit_X+Q, 200, @accept_X+Q, @poundtib_X+Q, @store_X+Q, @_lit_X+Q, 0, @greaterin_X+Q, @store_X+Q, 0 + long + + ' : compile, here w! 2 allot ; +compcomma_L word @refill_L+Q + byte FLAG_DEF, 8, "compile," + long +compcomma_X word execlistfunc, 0 + word @here_X+Q, @wstore_X+Q, @_lit_X+Q, 2, @allot_X+Q, 0 + long + +'******************************************************************************* +' The boot interpreter follows below. +'******************************************************************************* + ' : xboot ( This word runs a simple interpreter ) +xboot_L word @compcomma_L+Q + byte FLAG_DEF, 5, "xboot" + long +xboot_X word execlistfunc, 0 + + ' 20 word 0 pick c@ _jz _xboot2 ( Get word, refill if empty ) +xboot_1 word @_lit_X+Q, $20, @word_X+Q, @_lit_X+Q, 0, @pick_X+Q, @cfetch_X+Q, @_jz_X+Q, @xboot_2+Q + + ' find 0 pick _jz _xboot3 ( Find word, get number if not found ) + word @find_X+Q, @_lit_X+Q, 0, @pick_X+Q, @_jz_X+Q, @xboot_3+Q + + ' state @ = _jz _xboot4 ( Go execute if not compile mode or immediate ) + word @state_X+Q, @fetch_X+Q, @equal_X+Q, @_jz_X+Q, @xboot_4+Q + + ' compile, _jmp _xboot1 ( Otherwise, compile and loop again ) + word @compcomma_X+Q, @_jmp_X+Q, @xboot_1+Q + + ' execute _jmp _xboot1 ( Execute and loop again ) +xboot_4 word @execute_X+Q, @_jmp_X+Q, @xboot_1+Q + + ' drop count _gethex ( Get number ) +xboot_3 word @drop_X+Q, @count_X+Q, @_gethex_X+Q + + ' state @ _jz _xboot1 ( Loop again if not compile mode ) + word @state_X+Q, @fetch_X+Q, @_jz_X+Q, @xboot_1+Q + + ' ['] _lit , , _jmp _xboot1 ( Otherwise, compile number and loop again ) + word @_lit_X+Q, @_lit_X+Q, @compcomma_X+Q, @compcomma_X+Q, @_jmp_X+Q, @xboot_1+Q + + ' drop refill _jmp _xboot1 ( Refill and loop again ) +xboot_2 word @drop_X+Q, @refill_X+Q, @_lit_X+Q, 13, @emit_X+Q, @_jmp_X+Q, @xboot_1+Q, 0 + long + +switch_L word @xboot_L+Q + byte FLAG_DEF, 6, "switch" + long +switch_X word execlistfunc, 0 + word @_lit_X+Q, @getchar_X+Q, @_lit_X+Q, @key_B+Q, @store_X+Q, 0 + long + +_last long + +_loop_L word @switch_L+Q + byte FLAG_CORE, 5, "_loop" + long +_loop_X word _loopfunc, 0 + +_here long + +'******************************************************************************* +' The Forth source files follow below. They will be compiled into the +' dictionary, which will over-write the source data. Some padding space is +' included to ensure that we don't over-write the source data before it is +' compiled. +'******************************************************************************* + long 0[100] +infile file "init.fth" + file "comus.fth" + 'file "see.fth" + file "propwords.fth" + file "sd.fth" + 'file "sdutils.fth" + 'file "linux.fth" + file "cd.fth" + 'file "ted.fth" + 'file "bufser.fth" + 'file "i2c.fth" + 'file "fds.fth" + 'file "time.fth" + 'file "toggle.fth" + 'file "primes.fth" + 'file "chess.fth" + +'******************************************************************************* +' Enable serial output, print version string and switch to serial input +'******************************************************************************* + byte 13 + byte " 1 verbose !" + byte " pfthversion type cr" + byte " switch include _startup.fth", 13 + 'byte " switch", 13 + + +{ ++-------------------------------------------------------------------- +| TERMS OF USE: MIT License ++-------------------------------------------------------------------- +Permission is hereby granted, free of charge, to any person obtaining +a copy of this software and associated documentation files +(the "Software"), to deal in the Software without restriction, +including without limitation the rights to use, copy, modify, merge, +publish, distribute, sublicense, and/or sell copies of the Software, +and to permit persons to whom the Software is furnished to do so, +subject to the following conditions: + +The above copyright notice and this permission notice shall be +included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. ++------------------------------------------------------------------ +} diff --git a/pfth/pfth.spin b/pfth/pfth.spin new file mode 100644 index 0000000..fd51559 --- /dev/null +++ b/pfth/pfth.spin @@ -0,0 +1,1536 @@ +{ +############################################################################ +# PFTH - This program implements a Forth interpreter. +# +# Copyright (c) 2012, 2013 Dave Hein +# MIT Licensed +############################################################################ +} +con + _clkmode = xtal1+pll16x + _clkfreq = 80_000_000 + + ' SD Pin Definitions + 'DO = 10 + 'CLK = 11 + 'DI = 9 + 'CS = 25 + + Q = 16 ' Object Offset + + FLAG_IMMEDIATE = 1 + FLAG_CORE = $10 | $80 + FLAG_LIT = $12 | $80 + FLAG_VAR = $20 | $80 + FLAG_DEF = $00 | $80 + FLAG_JMP = $0A | $80 + FLAG_SEMI = FLAG_CORE | FLAG_IMMEDIATE + +obj + 'spi : "mount" + +'******************************************************************************* +' This Spin code waits three seconds and then starts the Forth cog +'******************************************************************************* +pub main(argc, argv) + waitcnt(clkfreq+cnt) + 'spi.mount_explicit(@spi_vars, DO, CLK, DI, CS) + coginit(cogid, @forth, @pfthconfig) + +dat +pfthconfig long @xboot_1+Q ' Initial word to execute + long @stack+Q+16 ' Starting stack pointer + long @stack+Q+16 ' Empty stack pointer value + long @retstk+Q ' Starting return pointer + long @retstk+Q ' Empty return pointer value +stack long 0[100] ' Data stack +retstk long 0[100] ' Return stack + +'******************************************************************************* +' pfth cog code +'******************************************************************************* + org 0 +forth +parm mov parm, par +parm1 rdlong pc, parm +parm2 add parm, #4 +parm3 rdlong stackptr, parm +parm4 add parm, #4 +temp rdlong stackptr0, parm +temp1 add parm, #4 +temp2 rdlong returnptr, parm +temp3 add parm, #4 +temp4 rdlong returnptr0, parm + jmp #innerloop ' Begin execution + +'******************************************************************************* +' Execute the words contained in the body of a word +' Changes parm, temp1 +'******************************************************************************* +execlistfunc add parm, #4 ' Get body from XT +innerloopcall wrlong pc, returnptr ' Push PC to return stack + add returnptr, #4 + mov pc, parm ' Set new value for PC + +'******************************************************************************* +' Get an execution token from the location pointed to by the program counter +' Increment the program counter, fetch the code pointer and jump to it +' Changes parm, temp1, pc +'******************************************************************************* +innerloop rdword parm, pc wz + if_z jmp #exitfunc + add pc, #2 + rdword temp1, parm + jmp temp1 + +pc long @xboot_1+Q ' Program Counter + +'******************************************************************************* +' Stop executing the current word, and return to the calling word +' No Changes +'******************************************************************************* +exitfunc sub returnptr, #4 + rdlong pc, returnptr + jmp #innerloop + +'******************************************************************************* +' Abort or quit execution, and return to the interpreter +' No Changes +'******************************************************************************* +abortfunc mov stackptr, stackptr0 +quitfunc mov returnptr, returnptr0 + add returnptr, #4 ' Use second entry return stack + rdlong pc, returnptr + jmp #innerloop + +'******************************************************************************* +' Push the value contained in the word's body onto the stack +' No changes +'******************************************************************************* +confunc add parm, #4 + rdlong parm1, parm + jmp #push_jmp + +'******************************************************************************* +' Push the address of the word's body onto the stack +' Execute the words pointed to by the does pointer, if non-zero +' No changes +'******************************************************************************* +varfunc mov parm1, parm + add parm1, #4 + call #push + +'******************************************************************************* +' Execute the words pointed to by the does pointer, if non-zero +' No changes +'******************************************************************************* +deferfunc add parm, #2 ' DOES> pointer + rdword parm, parm wz + if_z jmp #innerloop ' Done with varfunc + jmp #innerloopcall ' Execute DOES> code + +'******************************************************************************* +' Execute the word on the stack +' Changes parm, temp1 +'******************************************************************************* +executefunc sub stackptr, #4 + rdlong parm, stackptr + rdlong temp1, parm + jmp temp1 ' Execute code + +'******************************************************************************* +' Execute the PASM instruction on the TOS using the next value on the stack as +' the destination register data. Return the result on the stack. +' Changes parm1, parm2 +'******************************************************************************* +cogx1func call #pop2 + mov cogx1instr, parm2 + movd cogx1instr, #parm1 + nop +cogx1instr nop + jmp #push_jmp + +'******************************************************************************* +' Duplicate the top of stack +' Changes parm1 +'******************************************************************************* +dupfunc call #pop + call #push + jmp #push_jmp + +'******************************************************************************* +' Swap the top two items on the stack +' Changes parm1, parm2 +'******************************************************************************* +swapfunc call #pop2 + wrlong parm2, stackptr + add stackptr, #4 + jmp #push_jmp + +'******************************************************************************* +' Get the next word from the input buffer using the delimiter from the stack +' Changes parm, parm1, parm2, temp1, temp2 +'******************************************************************************* +wordfunc sub stackptr, #4 + rdlong parm, stackptr + call #word_del + mov temp1, #1 + shl temp1, #15 + sub temp1, parm2 + sub temp1, #1 + wrlong temp1, stackptr + add stackptr, #4 + wrbyte parm2, temp1 + cmps parm2, #0 wc, wz + if_c_or_z jmp #innerloop +:loop add temp1, #1 + rdbyte temp2, parm1 + add parm1, #1 + wrbyte temp2, temp1 + djnz parm2, #:loop + jmp #innerloop + +'******************************************************************************* +' Find the word specfied on the stack in the dictionary +' Changes parm1, parm2, temp4 +'******************************************************************************* +findfunc call #pop + mov temp4, parm1 + add parm1, #1 + rdbyte parm2, temp4 + call #findword + mov parm1, parm wz + if_z jmp #findfunc1 + call #link2xt + call #push + add parm, #2 ' Point to flag byte + rdbyte parm1, parm + and parm1, #1 ' Check immediate bit + shl parm1, #1 + sub parm1, #1 ' Return 1 if set, -1 if not + call #push + jmp #innerloop +findfunc1 mov parm1, temp4 + call #push + mov parm1, #0 + call #push + jmp #innerloop + +'******************************************************************************* +' Send the character from the stack to the output port +' Changes parm +'******************************************************************************* +emitfunc call #pop + mov parm, parm1 + call #putch + jmp #innerloop + +'******************************************************************************* +' Get a character from the input port and put it on the stack +' Changes parm, parm1 +'******************************************************************************* +getcharfunc call #getch + mov parm1, parm + jmp #push_jmp + +'******************************************************************************* +' Get a character from the files stored in memory and put it on the stack +' Changes parm1 +'******************************************************************************* +getfcharfunc rdbyte parm1, infileptr + add infileptr, #1 + jmp #push_jmp + +'******************************************************************************* +' Get an address and value from the stack, and store the value at the address +' No changes +'******************************************************************************* +storefunc call #pop2 + wrlong parm1, parm2 + jmp #innerloop + +'******************************************************************************* +' Fetch a value from the address specified on the stack, and put it on the stack +' Changes parm1 +'******************************************************************************* +fetchfunc call #pop + rdlong parm1, parm1 + jmp #push_jmp + +'******************************************************************************* +' Get an address and word from the stack, and store the word at the address +' No changes +'******************************************************************************* +wstorefunc call #pop2 + wrword parm1, parm2 + jmp #innerloop + +'******************************************************************************* +' Fetch a word from the address specified on the stack, and put it on the stack +' Changes parm1 +'******************************************************************************* +wfetchfunc call #pop + rdword parm1, parm1 + jmp #push_jmp + +'******************************************************************************* +' Get an address and byte from the stack, and store the byte at the address +' No changes +'******************************************************************************* +cstorefunc call #pop2 + wrbyte parm1, parm2 + jmp #innerloop + +'******************************************************************************* +' Fetch a byte from the address specified on the stack, and put it on the stack +' Changes parm1 +'******************************************************************************* +cfetchfunc call #pop + rdbyte parm1, parm1 + jmp #push_jmp + +'******************************************************************************* +' Add two values from the stack, and write the result back to the stack +' Changes parm1, parm2 +'******************************************************************************* +plusfunc call #pop2 + add parm1, parm2 + jmp #push_jmp + +'******************************************************************************* +' Subtract two values from the stack, and write the result back to the stack +' Changes parm1, parm2 +'******************************************************************************* +minusfunc call #pop2 + sub parm1, parm2 + jmp #push_jmp + +'******************************************************************************* +' Multiply two values from the stack, and write the result back to the stack +' Changes parm1, parm2 +'******************************************************************************* +multfunc call #pop2 + call #multiply + jmp #push_jmp + +'******************************************************************************* +' Divide two values from the stack, and write the result back to the stack +' Changes parm1, parm2 +'******************************************************************************* +dividefunc call #pop2 + call #divide + mov parm1, parm2 + test parm3, #1 wc + if_c neg parm1, parm1 + jmp #push_jmp + +'******************************************************************************* +' Compute the modulus from two values from the stack, and write the result back +' to the stack +' Changes parm1, parm2 +'******************************************************************************* +modfunc call #pop2 + call #divide + test parm3, #2 wc + if_c neg parm1, parm1 + jmp #push_jmp + +'******************************************************************************* +' Compare two values from the stack to determine if the second one is less than +' the first one, and write the result back to the stack +' Changes parm1, parm2 +'******************************************************************************* +lessfunc call #pop2 + cmps parm1, parm2 wc + if_c neg parm1, #1 + if_nc mov parm1, #0 + jmp #push_jmp + +'******************************************************************************* +' Compare two values from the stack to determine if they are equal, and write +' the result back to the stack +' Changes parm1, parm2 +'******************************************************************************* +equalfunc call #pop2 + cmp parm1, parm2 wz + if_z neg parm1, #1 + if_nz mov parm1, #0 + jmp #push_jmp + +'******************************************************************************* +' Compare two values from the stack to determine if the second one is greater +' than the first one, and write the result back to the stack +' Changes parm1, parm2 +'******************************************************************************* +greaterfunc call #pop2 + cmps parm1, parm2 wc, wz + if_nz_and_nc neg parm1, #1 + if_z_or_c mov parm1, #0 + jmp #push_jmp + +'******************************************************************************* +' Compute the logical AND of two values from the stack, and write the result +' back to the stack +' Changes parm1, parm2 +'******************************************************************************* +andfunc call #pop2 + and parm1, parm2 + jmp #push_jmp + +'******************************************************************************* +' Compute the logical OR of two values from the stack, and write the result +' back to the stack +' Changes parm1, parm2 +'******************************************************************************* +orfunc call #pop2 + or parm1, parm2 + jmp #push_jmp + +'******************************************************************************* +' Compute the logical XOR of two values from the stack, and write the result +' back to the stack +' Changes parm1, parm2 +'******************************************************************************* +xorfunc call #pop2 + xor parm1, parm2 + jmp #push_jmp + +'******************************************************************************* +' Right-shift the second value on the stack by the number of bits specified by +' the first value on the stack, and write the result to the stack +' Changes parm1, parm2 +'******************************************************************************* +rshiftfunc call #pop2 + shr parm1, parm2 + jmp #push_jmp + +'******************************************************************************* +' Left-shift the second value on the stack by the number of bits specified by +' the first value on the stack, and write the result to the stack +' Changes parm1, parm2 +'******************************************************************************* +lshiftfunc call #pop2 + shl parm1, parm2 + jmp #push_jmp + +'******************************************************************************* +' Push the stack depth to the stack +' Changes parm1 +'******************************************************************************* +depthfunc mov parm1, stackptr + sub parm1, stackptr0 + sar parm1, #2 + jmp #push_jmp + +'******************************************************************************* +' Drop the top value from the stack +' No changes +'******************************************************************************* +dropfunc sub stackptr, #4 + jmp #innerloop + +'******************************************************************************* +' Use the value on top of the stack as an index to another value in the stack, +' and write its value to the stack +' No changes +'******************************************************************************* +pickfunc call #pop + call #indexstack + jmp #push_jmp + +'******************************************************************************* +' Use the value on top of the stack as and index to remove another value from +' the stack, and place it at the top of the stack. +' Changes temp1, temp2, temp3, temp4 +'******************************************************************************* +rollfunc call #pop + cmp parm1, #0 wc, wz + if_c_or_z jmp #innerloop + mov temp3, parm1 + call #indexstack + mov temp2, temp1 +:loop add temp2, #4 + rdlong temp4, temp2 + wrlong temp4, temp1 + add temp1, #4 + djnz temp3, #:loop + wrlong parm1, temp1 + jmp #innerloop + +'******************************************************************************* +' Pop the value from the top of the stack, and push it onto the return stack. +' No changes +'******************************************************************************* +torfunc call #pop + wrlong parm1, returnptr + add returnptr, #4 + jmp #innerloop + +'******************************************************************************* +' Pop the value from the top of the return stack and push it to the stack. +' Changes parm1 +'******************************************************************************* +fromrfunc sub returnptr, #4 + rdlong parm1, returnptr + jmp #push_jmp + +'******************************************************************************* +' Push the value on the stack pointed to by the PC and increment the PC +' Changes parm1 +'******************************************************************************* +_litfunc rdword parm1, pc + add pc, #2 + jmp #push_jmp + +'******************************************************************************* +' Convert the string described by the address and length on the top of the +' stack to a hex number, and push it to the stack +' Changes parm1 +'******************************************************************************* +_gethexfunc call #pop2 + call #gethex + mov parm1, parm + jmp #push_jmp + +'******************************************************************************* +' Create a variable, and add it to the dictionary +' Changes parm3 +'******************************************************************************* +createfunc mov parm3, #varfunc + mov parm4, #FLAG_VAR + call #create + jmp #innerloop + +'******************************************************************************* +' Create an executable word, and add it to the dictionary. Set the compile +' state to -1 +' Changes parm3, temp1 +'******************************************************************************* +colonfunc mov parm3, #execlistfunc + mov parm4, #FLAG_DEF + call #create + if_z jmp #innerloop + neg temp1, #1 + wrlong temp1, a_state + jmp #innerloop + +'******************************************************************************* +' Compile a zero into memory indicating the end of an executable word, and set +' the compile flag to zero +' Changes temp1, temp2 +'******************************************************************************* +semicolonfunc mov temp1, #0 + wrlong temp1, a_state + rdlong temp2, a_dp + wrword temp1, temp2 + add temp2, #2 + wrlong temp2, a_dp + jmp #innerloop + +'******************************************************************************* +' Fetch a value from the specified cog address, and put it on the stack +' the compile flag to zero +' Changes parm1 +'******************************************************************************* +cogfetchfunc call #pop + movs cogfetch1, parm1 + nop +cogfetch1 mov parm1, 0-0 + jmp #push_jmp + +'******************************************************************************* +' Get a cog address and value from the stack, and store the value at the address +' the compile flag to zero +' Changes parm1, parm2 +'******************************************************************************* +cogstorefunc call #pop2 + movd cogstore1, parm2 + nop +cogstore1 mov 0-0, parm1 + jmp #innerloop + + +'******************************************************************************* +' Print out an 8-digit hex number to the output port. +' Changes parm +'******************************************************************************* +dotxfunc mov parm, #"$" + call #putch + call #pop + call #printhex + mov parm, #" " + call #putch + jmp #innerloop + +'******************************************************************************* +' If top of stack is zero, jump to address contained in location at current PC. +' Otherwise, increment the PC +' Changes parm1 +'******************************************************************************* +_jzfunc call #pop + if_z rdword pc, pc + if_nz add pc, #2 + jmp #innerloop + +'******************************************************************************* +' Copy bytes from the source to the destination +' Changes parm1 +'******************************************************************************* +cmovefunc sub stackptr, #4 + rdlong parm3, stackptr + call #pop2 + cmps parm3, #0 wz, wc + if_c_or_z jmp #innerloop +:loop rdbyte temp1, parm1 + add parm1, #1 + wrbyte temp1, parm2 + add parm2, #1 + djnz parm3, #:loop + jmp #innerloop + +'******************************************************************************* +' Perform the increment and compare for the loop word +' Changes parm1, parm2, parm3 +'******************************************************************************* +_loopfunc call #pop ' Get increment + sub returnptr, #8 + rdlong parm3, returnptr ' Get upper limit + add returnptr, #4 + rdlong parm2, returnptr ' Get index + add parm1, parm2 ' index + increment + wrlong parm1, returnptr ' Push index back + add returnptr, #4 + cmps parm1, parm3 wc + if_nc neg parm1, #1 + if_c mov parm1, #0 + jmp #push_jmp + +'******************************************************************************* +' The following code implements the basic functions used by the kernel words +'******************************************************************************* + +'******************************************************************************* +' Create a word entry in the dictionary +' Changes parm, parm1, parm2, temp1, temp2 +'******************************************************************************* +create mov parm, #" " + call #word_del + if_z jmp create_ret + rdlong temp1, a_dp ' Align DP + add temp1, #3 + and temp1, minus4 + rdlong temp2, a_last + wrword temp2, temp1 ' Write the link pointer + wrlong temp1, a_last ' Update LAST + add temp1, #2 + + wrbyte parm4, temp1 ' Write the flag + add temp1, #1 + wrbyte parm2, temp1 ' Write the length + add temp1, #1 + cmps parm2, #0 wc, wz + if_c_or_z jmp #create_done +:loop rdbyte temp2, parm1 ' Copy the name + add parm1, #1 + wrbyte temp2, temp1 + add temp1, #1 wz + djnz parm2, #:loop + +create_done mov temp2, #0 ' Pad with 0's to align +:loop1 test temp1, #3 wz + if_z jmp #create_aligned + wrbyte temp2, temp1 + add temp1, #1 + jmp #:loop1 + +create_aligned wrword parm3, temp1 ' Write the code pointer + add temp1, #2 + wrword temp2, temp1 ' Write the DOES> pointer + add temp1, #2 wz ' Clear zero flag + + wrlong temp1, a_dp +create_ret ret + +'******************************************************************************* +' Get one character from the input port. +' Input none +' Changes parm, temp, temp1, temp2 +' Output parm +'******************************************************************************* +getch mov parm, ina + and parm, inbit wz + if_nz jmp #getch + mov temp2, cnt + mov temp, bitcycles + shr temp, #1 + add temp2, temp + mov temp1, #10 +:loop waitcnt temp2, bitcycles + mov temp, ina + and temp, inbit + ror parm, #1 + or parm, temp + djnz temp1, #:loop + ror parm, #31 - 8 + and parm, #255 +getch_ret ret + +inbit long $80000000 +bitcycles long 80_000_000 / 115_200 + +'******************************************************************************* +' Send one character to the output port. +' Input parm +' Changes parm, temp1, temp2 +' Output none +'******************************************************************************* +putch rdlong temp1, a_verbose wz + if_z jmp putch_ret + or parm, #$100 + shl parm, #1 + rol parm, #30 + mov temp1, #10 + mov temp2, bitcycles + add temp2, cnt +:loop mov outa, parm + ror parm, #1 + waitcnt temp2, bitcycles + djnz temp1, #:loop +putch_ret ret + +outbit long $40000000 + +'******************************************************************************* +' Skip the specified character in the input buffer +' Input parm +' Changes temp, temp1 +' Output none +'******************************************************************************* +skipchar cmps temp1, temp2 wc + if_nc jmp skipchar_ret + rdlong temp, a_tib + add temp, temp1 + rdbyte temp, temp + cmp temp, parm wz + if_nz jmp skipchar_ret + add temp1, #1 + jmp #skipchar +skipchar_ret ret + +'******************************************************************************* +' Find the next occurance of the specified character in the input buffer +' Input parm +' Changes temp, temp1 +' Output none +'******************************************************************************* +findchar cmps temp1, temp2 wc + if_nc jmp findchar_ret + rdlong temp, a_tib + add temp, temp1 + rdbyte temp, temp + cmp temp, parm wz + if_z jmp findchar_ret + add temp1, #1 + jmp #findchar +findchar_ret ret + +'******************************************************************************* +' Find the next word in the input buffer delimited by the specified character +' Input parm +' Changes parm1, parm2, temp1, temp2 +' Output none +'******************************************************************************* +word_del + rdlong temp1, a_inputidx + rdlong temp2, a_inputlen + call #skipchar + mov parm1, temp1 + call #findchar + mov parm2, temp1 + sub parm2, parm1 wz + rdlong temp, a_tib + add parm1, temp + cmps temp1, temp2 wc + if_c add temp1, #1 + wrlong temp1, a_inputidx +word_del_ret ret + +'******************************************************************************* +' Find the specified word in the dictionary +' Input parm1, parm2 +' Changes parm, parm3, parm4 +' Output parm +'******************************************************************************* +findword rdlong parm, a_last wz + if_z jmp findword_ret +:loop mov parm3, parm + add parm3, #3 + rdbyte parm4, parm3 + add parm3, #1 + call #compare + if_z jmp findword_ret + rdword parm, parm wz + if_nz jmp #:loop +findword_ret ret + +'******************************************************************************* +' Do a case insensitive comparison of two character strings +' Input parm1, parm2, parm3, parm4 +' Changes parm3, parm4, temp, temp1, temp2 +' Outut Z +'******************************************************************************* +compare cmps parm2, #1 wc, wz + if_c jmp compare_ret + cmp parm2, parm4 wz + if_nz jmp compare_ret + mov temp, parm1 +:loop rdbyte temp1, temp + call #toupper + mov temp2, temp1 + rdbyte temp1, parm3 + call #toupper + cmp temp1, temp2 wz + if_nz jmp compare_ret + add temp, #1 + add parm3, #1 + djnz parm4, #:loop +compare_ret ret + +'******************************************************************************* +' Convert a character to uppercase +' Input temp1 +' Changes temp1 +' Ouput temp1 +'******************************************************************************* +toupper cmp temp1, #"a" wc + if_c jmp toupper_ret + cmp temp1, #"z" wc, wz + if_nc_and_nz jmp toupper_ret + sub temp1, #"a" - "A" +toupper_ret ret + + +'******************************************************************************* +' Print an 8-digit hex value to the output port +' Input parm1 +' Changes parm, parm1, parm2 +' Output none +'******************************************************************************* +printhex mov parm2, #8 +:loop rol parm1, #4 + mov parm, #15 + and parm, parm1 + add parm, a_hexstr + rdbyte parm, parm + call #putch + djnz parm2, #:loop +printhex_ret ret + + +'******************************************************************************* +' Convert a string to a hex number +' Input parm1, parm2 +' Changes parm, temp, temp1, temp2 +' Output parm +'******************************************************************************* +gethex mov parm, #0 + cmps parm2, #0 wc, wz + if_c_or_z jmp gethex_ret + mov temp1, parm1 + mov temp2, parm2 +:loop rdbyte temp, temp1 + add temp1, #1 + sub temp, #"0" + cmps temp, #10 wc + if_nc sub temp, #"a"-"0"-10 + shl parm, #4 + add parm, temp + djnz temp2, #:loop +gethex_ret ret + +'******************************************************************************* +' Push a value onto the data stack +' Input parm1 +' No changes +' Output none +'******************************************************************************* +push wrlong parm1, stackptr + add stackptr, #4 +push_ret ret + +'******************************************************************************* +' Push a value onto the data stack and jump to the innerloop +' Input parm1 +' No changes +' Output none +'******************************************************************************* +push_jmp wrlong parm1, stackptr + add stackptr, #4 + jmp #innerloop + +'******************************************************************************* +' Pop two values off of the data stack +' Input none +' Changes parm1, parm2 +' Output parm1, parm2 +'******************************************************************************* +pop2 sub stackptr, #4 + rdlong parm2, stackptr + +'******************************************************************************* +' Pop one value off of the data stack +' Input none +' Changes parm1 +' Ouput parm1 +'******************************************************************************* +pop sub stackptr, #4 + rdlong parm1, stackptr wz +pop_ret +pop2_ret ret + +'******************************************************************************* +' Read a value on the stack based on an index number +' Changes parm1, temp1 +'******************************************************************************* +indexstack neg temp1, parm1 + shl temp1, #2 + sub temp1, #4 + add temp1, stackptr + rdlong parm1, temp1 +indexstack_ret ret + +'******************************************************************************* +' Compute the XT from the address of the link +' Input: parm1 +' Output: parm1 +' Changes: temp1 +'******************************************************************************* +link2xt mov temp1, parm1 + add temp1, #3 + rdbyte parm1, temp1 ' Get name length + add parm1, temp1 + add parm1, #4 + and parm1, minus4 ' Align +link2xt_ret ret + +'******************************************************************************* +' Multiply two 32-bit numbers +' Changes parm2, temp1, temp2 +'******************************************************************************* +multiply mov temp1, #0 + mov temp2, #32 + shr parm1, #1 wc +mmul if_c add temp1, parm2 wc + rcr temp1, #1 wc + rcr parm1, #1 wc + djnz temp2, #mmul +multiply_ret ret + +'******************************************************************************* +' Divide two 32-bit numbers producing a quotient and a remainder +' Changes parm1, parm2, parm3, temp1, temp2 +'******************************************************************************* +divide mov temp2, #32 + mov temp1, #0 + abs parm1, parm1 wc + muxc parm3, #%11 + abs parm2, parm2 wc,wz + if_c xor parm3, #%01 +' if_nz jmp #mdiv +' mov parm1, #0 +' jmp divide_ret +mdiv shr parm2, #1 wc,wz + rcr temp1, #1 + if_nz djnz temp2, #mdiv +mdiv2 cmpsub parm1, temp1 wc + rcl parm2, #1 + shr temp1, #1 + djnz temp2, #mdiv2 +divide_ret ret + +'******************************************************************************* +' These are working registers. The parm registers are generally used to pass +' parameters from one routine to another, and the temp registers are used as +' temporary storage within a routine. +'******************************************************************************* + +'******************************************************************************* +' Addresses of variables in the dictionary, and the hex table +'******************************************************************************* +a_hexstr long @hexstr+Q +a_last long @last+Q +a_state long @state+Q +a_dp long @dp+Q +a_tib long @tib+Q +a_verbose long @verbose+Q +a_inputidx long @greaterin+Q +a_inputlen long @poundtib+Q + +'******************************************************************************* +' The data and return stack pointers, and their base addresses +'******************************************************************************* +stackptr long 0 +stackptr0 long 0 +returnptr long 0 +returnptr0 long 0 + +'******************************************************************************* +' The input file pointer used during initialization +'******************************************************************************* +infileptr long @infile+Q + +'******************************************************************************* +' Constants +'******************************************************************************* +minus4 long -4 + + fit $1f0 + +'******************************************************************************* +' Input buffer and hex table +'******************************************************************************* +hexstr byte "0123456789abcdef" +inputbuf byte 0[200] + +'******************************************************************************* +' This is the beginning of the dictionary. The kernel words are specified below +'******************************************************************************* +exit_L word 0 + byte FLAG_CORE, 4, "exit" + long +exit_X word exitfunc, 0 + +quit_L word @exit_L+Q + byte FLAG_CORE, 4, "quit" + long +quit_X word quitfunc, 0 + +abort_L word @quit_L+Q + byte FLAG_CORE, 5, "abort" + long +abort_X word abortfunc, 0 + +execute_L word @abort_L+Q + byte FLAG_CORE, 7, "execute" + long +execute_X word executefunc, 0 + +word_L word @execute_L+Q + byte FLAG_CORE, 4, "word" + long +word_X word wordfunc, 0 + +find_L word @word_L+Q + byte FLAG_CORE, 4, "find" + long +find_X word findfunc, 0 + +getchar_L word @find_L+Q + byte FLAG_CORE, 7, "getchar" + long +getchar_X word getcharfunc, 0 + +getfchar_L word @getchar_L+Q + byte FLAG_CORE, 8, "getfchar" + long +getfchar_X word getfcharfunc, 0 + +key_L word @getfchar_L+Q + byte FLAG_CORE, 3, "key" + long +key_X word deferfunc, @key_B+Q +key_B word @getfchar_X+Q, 0 + +create_L word @key_L+Q + byte FLAG_CORE, 6, "create" + long +create_X word createfunc, 0 + +_lit_L word @create_L+Q + byte FLAG_LIT, 4, "_lit" + long +_lit_X word _litfunc, 0 + +_gethex_L word @_lit_L+Q + byte FLAG_CORE, 7, "_gethex" + long +_gethex_X word _gethexfunc, 0 + +emit_L word @_gethex_L+Q + byte FLAG_CORE, 4, "emit" + long +emit_X word emitfunc, 0 + +store_L word @emit_L+Q + byte FLAG_CORE, 1, "!" + long +store_X word storefunc, 0 + +fetch_L word @store_L+Q + byte FLAG_CORE, 1, "@" + long +fetch_X word fetchfunc, 0 + +wstore_L word @fetch_L+Q + byte FLAG_CORE, 2, "w!" + long +wstore_X word wstorefunc, 0 + +wfetch_L word @wstore_L+Q + byte FLAG_CORE, 2, "w@" + long +wfetch_X word wfetchfunc, 0 + +cstore_L word @wfetch_L+Q + byte FLAG_CORE, 2, "c!" + long +cstore_X word cstorefunc, 0 + +cfetch_L word @cstore_L+Q + byte FLAG_CORE, 2, "c@" + long +cfetch_X word cfetchfunc, 0 + +plus_L word @cfetch_L+Q + byte FLAG_CORE, 1, "+" + long +plus_X word plusfunc, 0 + +minus_L word @plus_L+Q + byte FLAG_CORE, 1, "-" + long +minus_X word minusfunc, 0 + +multiply_L word @minus_L+Q + byte FLAG_CORE, 1, "*" + long +multiply_X word multfunc, 0 + +divide_L word @multiply_L+Q + byte FLAG_CORE, 1, "/" + long +divide_X word dividefunc, 0 + +mod_L word @divide_L+Q + byte FLAG_CORE, 3, "mod" + long +mod_X word modfunc, 0 + +and_L word @mod_L+Q + byte FLAG_CORE, 3, "and" + long +and_X word andfunc, 0 + +or_L word @and_L+Q + byte FLAG_CORE, 2, "or" + long +or_X word orfunc, 0 + +xor_L word @or_L+Q + byte FLAG_CORE, 3, "xor" + long +xor_X word xorfunc, 0 + +less_L word @xor_L+Q + byte FLAG_CORE, 1, "<" + long +less_X word lessfunc, 0 + +equal_L word @less_L+Q + byte FLAG_CORE, 1, "=" + long +equal_X word equalfunc, 0 + +greater_L word @equal_L+Q + byte FLAG_CORE, 1, ">" + long +greater_X word greaterfunc, 0 + +rshift_L word @greater_L+Q + byte FLAG_CORE, 6, "rshift" + long +rshift_X word rshiftfunc, 0 + +lshift_L word @rshift_L+Q + byte FLAG_CORE, 6, "lshift" + long +lshift_X word lshiftfunc, 0 + +depth_L word @lshift_L+Q + byte FLAG_CORE, 5, "depth" + long +depth_X word depthfunc, 0 + +tib_L word @depth_L+Q + byte FLAG_VAR, 3, "tib" + long +tib_X word varfunc, @tib+Q+4 +tib long @inputbuf+Q + word @fetch_X+Q, 0 + long + +poundtib_L word @tib_L+Q + byte FLAG_VAR, 4, "#tib" + long +poundtib_X word varfunc, 0 +poundtib long 0 + +greaterin_L word @poundtib_L+Q + byte FLAG_VAR, 3, ">in" + long +greaterin_X word varfunc, 0 +greaterin long 0 + +dp_L word @greaterin_L+Q + byte FLAG_VAR, 2, "dp" + long +dp_X word varfunc, 0 +dp long @_here+Q + +last_L word @dp_L+Q + byte FLAG_VAR, 4, "last" + long +last_X word varfunc, 0 +last long @_last+Q + +state_L word @last_L+Q + byte FLAG_VAR, 5, "state" + long +state_X word varfunc, 0 +state long 0 + +base_L word @state_L+Q + byte FLAG_VAR, 4, "base" + long +base_X word varfunc, 0 +base long 16 + +verbose_L word @base_L+Q + byte FLAG_VAR, 7, "verbose" + long +verbose_X word varfunc, 0 +verbose long 0 + +forth_L word @verbose_L+Q + byte FLAG_VAR, 5, "forth" + long +forth_X word varfunc, 0 + long @forth+Q + +drop_L word @forth_L+Q + byte FLAG_CORE, 4, "drop" + long +drop_X word dropfunc, 0 + +dup_L word @drop_L+Q + byte FLAG_CORE, 3, "dup" + long +dup_X word dupfunc, 0 + +swap_L word @dup_L+Q + byte FLAG_CORE, 4, "swap" + long +swap_X word swapfunc, 0 + +pick_L word @swap_L+Q + byte FLAG_CORE, 4, "pick" + long +pick_X word pickfunc, 0 + +roll_L word @pick_L+Q + byte FLAG_CORE, 4, "roll" + long +roll_X word rollfunc, 0 + +tor_L word @roll_L+Q + byte FLAG_CORE, 2, ">r" + long +tor_X word torfunc, 0 + +fromr_L word @tor_L+Q + byte FLAG_CORE, 2, "r>" + long +fromr_X word fromrfunc, 0 + +colon_L word @fromr_L+Q + byte FLAG_CORE, 1, ":" + long +colon_X word colonfunc, 0 + +semicolon_L word @colon_L+Q + byte FLAG_SEMI, 1, ";" + long +semicolon_X word semicolonfunc, 0 + +cogfetch_L word @semicolon_L+Q + byte FLAG_CORE, 4, "cog@" + long +cogfetch_X word cogfetchfunc, 0 + +cogstore_L word @cogfetch_L+Q + byte FLAG_CORE, 4, "cog!" + long +cogstore_X word cogstorefunc, 0 + +cogx1_L word @cogstore_L+Q + byte FLAG_CORE, 5, "cogx1" + long +cogx1_X word cogx1func, 0 + +_jz_L word @cogx1_L+Q + byte FLAG_JMP, 3, "_jz" + long +_jz_X word _jzfunc, 0 + +cmove_L word @_jz_L+Q + byte FLAG_CORE, 5, "cmove" + long +cmove_X word cmovefunc, 0 + +dotx_L word @cmove_L+Q + byte FLAG_CORE, 2, ".x" + long +dotx_X word dotxfunc, 0 + +'******************************************************************************* +' SPI/SD Variables +'******************************************************************************* +spi_vars_L word @dotx_L+Q + byte FLAG_VAR, 8, "spi_vars" + long +spi_vars_X word varfunc, 0 +spi_vars long 0 ' SPI_engine_cog + long 0 ' SPI_command + long 0 ' SPI_block_index + long 0 ' SPI_buffer_address + long 0 ' SD_rootdir + long 0 ' SD_filesystem + long 0 ' SD_clustershift + long 0 ' SD_dataregion + long 0 ' SD_fat1 + long 0 ' SD_sectorsperfat + long 0 ' SD_currdir + +argc_L word @spi_vars_L+Q + byte FLAG_VAR, 4, "argc" + long +argc_X word confunc, 0 +argc_B long 0 + +argv_L word @argc_L+Q + byte FLAG_VAR, 4, "argv" + long +argv_X word confunc, 0 +argv_B long 0 + +hostcwd_L word @argv_L+Q + byte FLAG_VAR, 7, "hostcwd" + long +hostcwd_X word confunc, 0 +hostcwd_B long 0 + +'******************************************************************************* +' A small number of compiled words follow below. These are used by the boot +' interpreter. +'******************************************************************************* + ' : here dp @ ; +here_L word @hostcwd_L+Q + byte FLAG_DEF, 4, "here" + long +here_X word execlistfunc, 0 + word @dp_X+Q, @fetch_X+Q, 0 + long + + ' : allot dp @ + dp ! ; +allot_L word @here_L+Q + byte FLAG_DEF, 5, "allot" + long +allot_X word execlistfunc, 0 + word @dp_X+Q, @fetch_X+Q, @plus_X+Q, @dp_X+Q, @store_X+Q, 0 + long + + ' : , here ! 4 allot ; +comma_L word @allot_L+Q + byte FLAG_DEF, 1, "," + long +comma_X word execlistfunc, 0 + word @here_X+Q, @store_X+Q, @_lit_X+Q, 4, @allot_X+Q, 0 + long + + ' : _jmp r> @ >r ; +_jmp_L word @comma_L+Q + byte FLAG_JMP, 4, "_jmp" + long +_jmp_X word execlistfunc, 0 + word @fromr_X+Q, @wfetch_X+Q, @tor_X+Q, 0 + long + + ' : count 0 pick 1 + 1 roll c@ ; +count_L word @_jmp_L+Q + byte FLAG_DEF, 5, "count" + long +count_X word execlistfunc, 0 + word @_lit_X+Q, 0, @pick_X+Q, @_lit_X+Q, 1, @plus_X+Q, @_lit_X+Q, 1, @roll_X+Q, @cfetch_X+Q, 0 + long + + ' : accept ( addr size -- num ) \ Accept a string from the input source +accept_L word @count_L+Q + byte FLAG_DEF, 6, "accept" + long +accept_X word execlistfunc, 0 + ' >r dup + word @tor_X+Q, @dup_X+Q + ' r> dup 1 < _jz _accept4 +accept_1 word @fromr_X+Q, @dup_X+Q, @_lit_X+Q, 1, @less_X+Q, @_jz_X+Q, @accept_4+Q + ' drop swap - exit + word @drop_X+Q, @swap_X+Q, @minus_X+Q, @exit_X+Q + ' >r key +accept_4 word @tor_X+Q, @key_X+Q + ' dup 0d = over 0a = or + word @dup_X+Q, @_lit_X+Q, $0d, @equal_X+Q, @_lit_X+Q, 1, @pick_X+Q, @_lit_X+Q, $0a, @equal_X+Q, @or_X+Q + ' _jz _accept2 + word @_jz_X+Q, @accept_2+Q + ' cr drop swap - + word @_lit_X+Q, 13, @emit_X+Q, @_lit_X+Q, 10, @emit_X+Q, @drop_X+Q, @swap_X+Q, @minus_X+Q + ' r> drop exit + word @fromr_X+Q, @drop_X+Q, @exit_X+Q + ' dup 8 = _jz _accept3 +accept_2 word @dup_X+Q, @_lit_X+Q, 8, @equal_X+Q, @_jz_X+Q, @accept_3+Q + ' drop over over - _jz _accept1 + word @drop_X+Q, @_lit_X+Q, 1, @pick_X+Q, @_lit_X+Q, 1, @pick_X+Q, @minus_X+Q, @_jz_X+Q, @accept_1+Q + ' 1 - r> 1 + >r + word @_lit_X+Q, 1, @minus_X+Q, @fromr_X+Q, @_lit_X+Q, 1, @plus_X+Q, @tor_X+Q + ' 8 emit bl emit 8 emit _jmp _accept1 + word @_lit_X+Q, 8, @emit_X+Q, @_lit_X+Q, 32, @emit_X+Q, @_lit_X+Q, 8, @emit_X+Q, @_jmp_X+Q, @accept_1+Q + ' dup emit over c! 1 + +accept_3 word @dup_X+Q, @emit_X+Q, @_lit_X+Q, 1, @pick_X+Q, @cstore_X+Q, @_lit_X+Q, 1, @plus_X+Q + ' r> 1 - >r _jmp _accept1 + word @fromr_X+Q, @_lit_X+Q, 1, @minus_X+Q, @tor_X+Q, @_jmp_X+Q, @accept_1+Q, 0 + long + + ' : refill tib 200 accept #tib ! 0 >in ! ; +refill_L word @accept_L+Q + byte FLAG_DEF, 6, "refill" + long +refill_X word execlistfunc, 0 + word @tib_X+Q, @_lit_X+Q, 200, @accept_X+Q, @poundtib_X+Q, @store_X+Q, @_lit_X+Q, 0, @greaterin_X+Q, @store_X+Q, 0 + long + + ' : compile, here w! 2 allot ; +compcomma_L word @refill_L+Q + byte FLAG_DEF, 8, "compile," + long +compcomma_X word execlistfunc, 0 + word @here_X+Q, @wstore_X+Q, @_lit_X+Q, 2, @allot_X+Q, 0 + long + +'******************************************************************************* +' The boot interpreter follows below. +'******************************************************************************* + ' : xboot ( This word runs a simple interpreter ) +xboot_L word @compcomma_L+Q + byte FLAG_DEF, 5, "xboot" + long +xboot_X word execlistfunc, 0 + + ' 20 word 0 pick c@ _jz _xboot2 ( Get word, refill if empty ) +xboot_1 word @_lit_X+Q, $20, @word_X+Q, @_lit_X+Q, 0, @pick_X+Q, @cfetch_X+Q, @_jz_X+Q, @xboot_2+Q + + ' find 0 pick _jz _xboot3 ( Find word, get number if not found ) + word @find_X+Q, @_lit_X+Q, 0, @pick_X+Q, @_jz_X+Q, @xboot_3+Q + + ' state @ = _jz _xboot4 ( Go execute if not compile mode or immediate ) + word @state_X+Q, @fetch_X+Q, @equal_X+Q, @_jz_X+Q, @xboot_4+Q + + ' compile, _jmp _xboot1 ( Otherwise, compile and loop again ) + word @compcomma_X+Q, @_jmp_X+Q, @xboot_1+Q + + ' execute _jmp _xboot1 ( Execute and loop again ) +xboot_4 word @execute_X+Q, @_jmp_X+Q, @xboot_1+Q + + ' drop count _gethex ( Get number ) +xboot_3 word @drop_X+Q, @count_X+Q, @_gethex_X+Q + + ' state @ _jz _xboot1 ( Loop again if not compile mode ) + word @state_X+Q, @fetch_X+Q, @_jz_X+Q, @xboot_1+Q + + ' ['] _lit , , _jmp _xboot1 ( Otherwise, compile number and loop again ) + word @_lit_X+Q, @_lit_X+Q, @compcomma_X+Q, @compcomma_X+Q, @_jmp_X+Q, @xboot_1+Q + + ' drop refill _jmp _xboot1 ( Refill and loop again ) +xboot_2 word @drop_X+Q, @refill_X+Q, @_lit_X+Q, 13, @emit_X+Q, @_jmp_X+Q, @xboot_1+Q, 0 + long + +switch_L word @xboot_L+Q + byte FLAG_DEF, 6, "switch" + long +switch_X word execlistfunc, 0 + word @_lit_X+Q, @getchar_X+Q, @_lit_X+Q, @key_B+Q, @store_X+Q, 0 + long + +_last long + +_loop_L word @switch_L+Q + byte FLAG_CORE, 5, "_loop" + long +_loop_X word _loopfunc, 0 + +_here long + +'******************************************************************************* +' The Forth source files follow below. They will be compiled into the +' dictionary, which will over-write the source data. Some padding space is +' included to ensure that we don't over-write the source data before it is +' compiled. +'******************************************************************************* + long 0[100] +infile file "init.fth" + file "comus.fth" + file "see.fth" + file "propwords.fth" + 'file "sd.fth" + 'file "sdutils.fth" + 'file "linux.fth" + 'file "cd.fth" + 'file "ted.fth" + 'file "bufser.fth" + file "i2c.fth" + file "fds.fth" + file "time.fth" + file "toggle.fth" + file "primes.fth" + 'file "chess.fth" + +'******************************************************************************* +' Enable serial output, print version string and switch to serial input +'******************************************************************************* + byte 13 + byte " 1 verbose !" + byte " pfthversion type cr" + byte " switch", 13 + + +{ ++-------------------------------------------------------------------- +| TERMS OF USE: MIT License ++-------------------------------------------------------------------- +Permission is hereby granted, free of charge, to any person obtaining +a copy of this software and associated documentation files +(the "Software"), to deal in the Software without restriction, +including without limitation the rights to use, copy, modify, merge, +publish, distribute, sublicense, and/or sell copies of the Software, +and to permit persons to whom the Software is furnished to do so, +subject to the following conditions: + +The above copyright notice and this permission notice shall be +included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. ++------------------------------------------------------------------ +} diff --git a/pfth/primes.fth b/pfth/primes.fth new file mode 100644 index 0000000..03d9864 --- /dev/null +++ b/pfth/primes.fth @@ -0,0 +1,62 @@ +\ primes.4th +\ +\ Example code for kForth +\ Copyright (c) 1998 Creative Consulting for Research and Education +\ + +\ Test for a prime number. Return the largest divisor (< n ) +\ and a flag indicating whether the number is prime or not. + +: ?prime ( n -- m flag | is n a prime number? ) +\ if flag is false (0), m is the largest divisor of n + abs + dup 3 > \ is n > 3 ? + if + abs + dup 2 /mod + swap 0= + if \ is n divisible by 2 ? + nip false + else + 1- \ check for divisibility starting + begin \ with n/2 - 1 and counting down + 2dup mod + over 1 > + and + while + 1- + repeat + nip + dup 1 <= + then + else + dup 1 > IF drop 1 true ELSE false THEN + then +; + +: test_prime ( n -- | test for prime number and display result ) + ?prime + if + ." is a prime number" drop + else + ." is NOT prime. Its largest divisor is " . + then + cr +; + +: list_primes ( n -- | list all the prime numbers from 2 to n ) + abs + dup 0> + if + 1+ 2 do + i ?prime + if + i . cr + then + drop + loop + else + drop + then +; + diff --git a/pfth/procarg.fth b/pfth/procarg.fth new file mode 100644 index 0000000..3a37c5e --- /dev/null +++ b/pfth/procarg.fth @@ -0,0 +1,11 @@ +\ Include a file from the arg list +: process-arg ( argnum ) + dup 0 > over argc < and + if + ." include " + 4 * argv + @ dup zcount type cr + zcount included + else + drop + then +; diff --git a/pfth/propwords.fth b/pfth/propwords.fth new file mode 100644 index 0000000..884462c --- /dev/null +++ b/pfth/propwords.fth @@ -0,0 +1,40 @@ +( PROP WORDS) +hex + +( REGISTER ACCESS ) +: cnt@ 1f1 cog@ ; +: ina@ 1f2 cog@ ; +: outa@ 1f4 cog@ ; +: outa! 1f4 cog! ; +: dira@ 1f6 cog@ ; +: dira! 1f6 cog! ; +: clkfreq@ 0 @ ; + +( BIT SETTING AND CLEARING ) +: dirasetbit dira@ or dira! ; +: diraclrbit invert dira@ and dira! ; +: outasetbit outa@ or outa! ; +: outaclrbit invert outa@ and outa! ; + +( HUBOPS ) +: cogid ( ... cogid ) 0 0cfc0001 cogx1 ; +: locknew ( ... locknum ) 0 0cfc0004 cogx1 ; +: lockret ( locknum ... ) 0cfc0005 cogx1 drop ; +: cogstop ( cognum ... ) 0c7c0003 cogx1 drop ; +: coginit ( codeptr dataptr cognum ... cognum ) + >r 0e lshift or 2 lshift r> or 0ffc0002 cogx1 ; +: cognew ( codeptr dataptr ... cognum ) 8 coginit ; +: waitcnt ( count ... count ) f8fc0000 cogx1 ; +: reboot 80 0cfc0000 cogx1 ; + +decimal + +( ANS UTILITY ) +: ms ( msec ... ) cnt@ swap clkfreq@ 1000 / * + waitcnt drop ; + +( ANS TOOLS EXT ) +: bye reboot ; + +( ENABLE SERIAL OUTPUT ) +1 30 lshift dup outa! dira! + diff --git a/pfth/rc4.fth b/pfth/rc4.fth new file mode 100644 index 0000000..d974dcc --- /dev/null +++ b/pfth/rc4.fth @@ -0,0 +1,42 @@ +0 value ii 0 value jj +0 value KeyAddr 0 value KeyLen +create SArray 256 allot \ state array of 256 bytes +: KeyArray KeyLen mod KeyAddr ; + +: get_byte + c@ ; +: set_byte + c! ; +: as_byte 255 and ; +: reset_ij 0 TO ii 0 TO jj ; +: i_update 1 + as_byte TO ii ; +: j_update ii SArray get_byte + as_byte TO jj ; +: swap_s_ij + jj SArray get_byte + ii SArray get_byte jj SArray set_byte + ii SArray set_byte +; + +: rc4_init ( KeyAddr KeyLen -- ) + 256 min TO KeyLen TO KeyAddr + 256 0 DO i i SArray set_byte LOOP + reset_ij + BEGIN + ii KeyArray get_byte jj + j_update + swap_s_ij + ii 255 < WHILE + ii i_update + REPEAT + reset_ij +; +: rc4_byte + ii i_update jj j_update + swap_s_ij + ii SArray get_byte jj SArray get_byte + as_byte SArray get_byte xor +; + +hex +create AKey 61 c, 8A c, 63 c, D2 c, FB c, +\ create AKey 97 c, 8A c, 99 c, D2 c, FB c, +: test cr 0 DO rc4_byte . LOOP cr ; +AKey 5 rc4_init +2C F9 4C EE DC 5 test \ output should be: F1 38 29 C9 DE + diff --git a/pfth/rc4time.fth b/pfth/rc4time.fth new file mode 100644 index 0000000..243ec57 --- /dev/null +++ b/pfth/rc4time.fth @@ -0,0 +1,49 @@ +0 value ii 0 value jj +0 value KeyAddr 0 value KeyLen +create SArray 256 allot \ state array of 256 bytes +create Results 100 allot +: KeyArray KeyLen mod KeyAddr ; + +: get_byte + c@ ; +: set_byte + c! ; +: as_byte 255 and ; +: reset_ij 0 TO ii 0 TO jj ; +: i_update 1 + as_byte TO ii ; +: j_update ii SArray get_byte + as_byte TO jj ; +: swap_s_ij + jj SArray get_byte + ii SArray get_byte jj SArray set_byte + ii SArray set_byte +; + +: rc4_init ( KeyAddr KeyLen -- ) + 256 min TO KeyLen TO KeyAddr + 256 0 DO i i SArray set_byte LOOP + reset_ij + BEGIN + ii KeyArray get_byte jj + j_update + swap_s_ij + ii 255 < WHILE + ii i_update + REPEAT + reset_ij +; +: rc4_byte + ii i_update jj j_update + swap_s_ij + ii SArray get_byte jj SArray get_byte + as_byte SArray get_byte xor +; + +\ : cnt@ 0 ; + +hex +create AKey 61 c, 8a c, 63 c, d2 c, fb c, +\ create AKey 97 c, 8a c, 99 c, d2 c, fb c, +: test 0 DO rc4_byte Results i + c! LOOP ; +: test1 cr 0 do Results i + c@ . loop cr ; +: time hex cnt@ +AKey 5 rc4_init +2c f9 4c ee dc 5 test \ output should be: f1 38 29 c9 de +cnt@ 5 test1 swap - 13880 / decimal . ." msec" cr ; +decimal + diff --git a/pfth/readme.txt b/pfth/readme.txt new file mode 100644 index 0000000..6a043d5 --- /dev/null +++ b/pfth/readme.txt @@ -0,0 +1,244 @@ + pfth Version 1.02 + August 19, 2013 + Dave Hein + (with additions from G. Herzog) + +INTRODUCTION +------------ + +pfth is an ANS Forth interpreter that runs on the Propeller. It is written in +PASM and Forth, and it can be built using the Prop Tool or BST. pfth +implements all 133 of the ANS Forth core words, and 38 of the 45 core ext +words. pfth will run on any Propeller board that supports the standard serial +interface. The default settings of the serial port are 115200 baud, 8 bits, no +parity and 1 stop bit. + +After loading and communications is established, use 'words' to verify that the +display is correct. The CR word is defined to emit both a carriage return and +a line feed. If your terminal displays an extra line you can either disable +the linefeed character on your terminal, or re-define CR to only emit a +carriage return. + + +VERSIONS OF SPIN FILES +---------------------- + +There are four versions of pfth. + +The first version can be used to build stand-alone Forth applications. It is +in pfth.spin. In this version, Forth programs are included using the Spin FILE +directive. + +A second version interfaces to an SD card and can execute Forth programs on +the SD card. This version is in sdpfth.spin. The program is set up for the C3 +card, but it can be modified to support other cards. + +The third version is named ospfth.spin, and runs under the Spinix operating +system. When Spinix starts up pfth it provides information about the SD pins, +the current working directory and a parameter list. The OS version of pfth +uses this information to initialize the SD card driver and change to the +working directory. It includes the file given by the parameter list. + +The final version is called chess.spin, and implements a chess program. After +the program is loaded type "chess" to start playing. A move is entered by +typing the source and destination postions separated by a "-". As an example, +the move "D2-D4" will move the white queen's pawn two spaces ahead. + + +LEXICON +------- + +pfth has over 50 kernel words, with most of them from the ANS core word set. +The source code for pfth is contained in pfth.spin, init.fth and other Forth +programs included at the end of pfth.spin. + +Two of the non-standard words implemented by pfth are cog@ and cog!. These are +are used to read and write cog memory locations. Their main purpose is to +access the Prop registers, such as CNT, INA and OUTA. + +Another non-standard word is cogx1, which is used to execute native Prop +instructions. The TOS contains the Prop instruction, and the next value on the +stack is used for the destination register value. The result of the execution +of the instruction is returned on the stack. + +Some other non-standard words are _lit, _gethex, _jz and .x. + +_lit is used to encode literal values. +_gethex is used during the boot phase to convert numeric strings to hex values. +_jz implements a jump-on-zero primitive. +.x is used for debug purposes, and it prints values as 8-digit hex numbers. + +Some of the kernel words are shown below. + +Interpreter Words Math and Logical Words Memory Access +----------------- ---------------------- ------------- +evaluate + ! +execute - @ +find * c! +word / c@ +refill mod +create and Console I/O +: or ----------- +; xor emit + < key + = accept +Program Termination > +------------------- lshift Primitive Words +abort rshift --------------- +exit _lit +quit Variables _gethex + --------- _jz +Stack Operations #tib .x +---------------- tib +drop >in Propeller Words +pick base --------------- +roll dp cog! +>r last cog@ +r> state cogx1 +depth +swap +dup + + +pfth also contains a small number of pre-compiled Forth words written +in Forth. These words are here, allot, ",", _jmp and count. The definition +of these words is as follows. + +: here dp @ ; +: allot dp @ + dp ! ; +: , here ! 4 allot ; +: _jmp r> @ >r ; +: count 0 pick 1 + 1 roll c@ ; + + +AT START UP +----------- + +When pfth starts up, it runs a small boot interpreter that compiles +the ANS dictionary contained in init.fth. This file is included in the +PASM binary image using the PASM FILE directive. Other Forth source files +may be included after init.fth to add additional words and to implement a +specific application. + +The boot interpreter can only handle 16-bit hex numbers, so pfth is in the hex +mode when first starting up. The boot interpreter has no error handling +capability, and is only used to build the initial dictionary. The boot +interpreter uses a limited vocabulary consisting of the following 20 +kernel words. + + dp state _lit _gethex : ; c@ @ ! + = pick roll drop + r> >r word find execute refill + +The boot interpreter is shown below in a pseudo-Forth language. The +labels (label1), (label2), etc. are actually encoded as PASM labels, but +they are shown symbolically in the code below. + +: xboot +(label1) + 20 word 0 pick c@ _jz (label2) ( Get word, refill if empty ) + find 0 pick _jz (label3) ( Find word, get number if not found ) + state @ = _jz (label4) ( Go execute if not compile mode or immediate ) + , _jmp (label1) ( Otherwise, compile and loop again ) +(label4) + execute _jmp (label1) ( Execute and loop again ) +(label3) + drop count _gethex ( Get number ) + state @ _jz (label1) ( Loop again if not compile mode ) + _lit _lit , , _jmp (label1) ( Otherwise, compile number and loop again ) +(label2) + drop refill _jmp (label1) ( Refill and loop again ) +; + +The boot interpreter compiles init.fth, which then runs the main interpreter. +The main interpreter performs some error handling by checking for undefined +words and stack underflows. It also handles negative numbers and numbers in +any base. + + +SOURCE PROGRAMS +--------------- + +There are a number of additional Forth source programs included in this +distribution that can be run by pfth. Open and read these as text files to +learn more details. + +comus.fth - provides a several useful non-standard words that are commonly + used. + +starting.fth - contains samples of code from the "Starting Forth" tutorial. + +rc4time.fth - implements the RC4 code included in the Wikipedia Forth entry. + It also displays the time required to run the RC4 code. + +i2c.fth - is a Forth implementation of Mike Green's basic_i2c_driver from + the OBEX. + +fds.fth - implementes some of the functions from the FullDuplexSerial + driver. It uses the binary PASM code from FullDuplexSerial. + +toggle.fth - will start up a cog running the Forth interpreter. It toggles + P15, but it can easily be modified to toggle another pin. + +ted.fth - a simple line-oriented text editor based on the ED text editor. + +linux.fth - implements some basic linux commands, such as ls, cat, rm and cp. + + +STARTING FORTH COGS +------------------- + +Forth cogs are started with cognew or coginit by specifying the Forth cog image +as the execution pointer and a 5-long structure as the data pointer. The +5-long structure is defined as follow + +First long: Address of the body of a word that will be executed on startup +Second long: Initial value of the stack pointer +Third long: Address of the beginning of the stack +Fourth long: Initial value of the return stack pointer +Fifth long: Address of the beginning the the return stack + + +CELL SIZE +--------- + +The cell size for pfth is 32 bits. The words !, @ and "," words access 32-bit +values that are 32-bit aligned. Additional words are provide for smaller unit +access, such as w! and w@ for word access, and c! and c@ for byte access. + +The compiled list cell size is 16 bits. The compile, word must be used when +compiling execution tokens into a list rather than just using the "," word. + + +DICTIONARY ENTRY FORMAT +----------------------- + +The format for the pfth dictionary entry is shown below. The beginning of a +word entry and its body are long-aligned. The link pointer contains the +address of the previous word in the dictionary. + +The flags byte contains various flag bits that indicate if the word is an +immediate word, a literal number or string, or a jump word. The name length +byte and the name string specify the name of the word. It is followed by +padding bytes that ensure long alignment. + +The code pointer contains the cog address of the PASM code that is to be +executed. The execution token for a word points to this field. The does> +pointer contains the hub address of a list of execution tokens that is to be +called. + +The body is a variable length field that contains the contents of a variable or +the list of execution tokens for a compiled word. The list for a compiled word +consists of one execution token per word, and is terminated by a zero-valued +word. + + Offset Content Size + ------ ------- ---- + 0 Link Pointer word + 2 Flags byte + 3 Name Length byte + 4 Name String Len bytes + 4+Len Padding (-Len) & 3 bytes + 4+(Len+3)&(-4) Code Pointer word + 6+(Len+3)&(-4) DOES> Pointer word + 8+(Len+3)&(-4) Body Variable + diff --git a/pfth/safe_spi.spin b/pfth/safe_spi.spin new file mode 100644 index 0000000..a45be75 --- /dev/null +++ b/pfth/safe_spi.spin @@ -0,0 +1,106 @@ +'****************************************************************************** +' Copyright (c) 2011, 2012, Dave Hein +' See end of file for terms of use. +'****************************************************************************** +{{ + SPI interface routines for SD & SDHC & MMC cards + + Jonathan "lonesock" Dummer + version 0.3.0 2009 July 19 + + Using multiblock SPI mode exclusively. + + This is the "SAFE" version...uses + * 1 instruction per bit writes + * 2 instructions per bit reads + + For the fsrw project: + fsrw.sf.net +}} +OBJ + sys : "sysdefs" + +CON + ' Rendezvoud locations + pSPI_engine_cog = sys#SPI_engine_cog + pSPI_command = sys#SPI_command + pSPI_block_index = sys#SPI_block_index + pSPI_buffer_address = sys#SPI_buffer_address + + ' Error codes + ERR_BLOCK_NOT_LONG_ALIGNED = -4 + ERR_SPI_ENGINE_NOT_RUNNING = -999 + +PUB readblock( block_index, buffer_address ) + if long[pSPI_engine_cog] == 0 + abort ERR_SPI_ENGINE_NOT_RUNNING + if (buffer_address & 3) + abort ERR_BLOCK_NOT_LONG_ALIGNED + long[pSPI_block_index] := block_index + long[pSPI_buffer_address] := buffer_address + long[pSPI_command] := "r" + repeat while long[pSPI_command] == "r" + if long[pSPI_command] < 0 + abort long[pSPI_command] + +PUB writeblock( block_index, buffer_address ) + if long[pSPI_engine_cog] == 0 + abort ERR_SPI_ENGINE_NOT_RUNNING + if (buffer_address & 3) + abort ERR_BLOCK_NOT_LONG_ALIGNED + long[pSPI_block_index] := block_index + long[pSPI_buffer_address] := buffer_address + long[pSPI_command] := "w" + repeat while long[pSPI_command] == "w" + if long[pSPI_command] < 0 + abort long[pSPI_command] + +PUB get_seconds + if long[pSPI_engine_cog] == 0 + abort ERR_SPI_ENGINE_NOT_RUNNING + long[pSPI_command] := "t" + repeat while long[pSPI_command] == "t" + ' seconds are in SPI_block_index, remainder is in SPI_buffer_address + return long[pSPI_block_index] + +PUB get_milliseconds : ms + if long[pSPI_engine_cog] == 0 + abort ERR_SPI_ENGINE_NOT_RUNNING + long[pSPI_command] := "t" + repeat while long[pSPI_command] == "t" + ' seconds are in SPI_block_index, remainder is in SPI_buffer_address + ms := long[pSPI_block_index] * 1000 + ms += long[pSPI_buffer_address] * 1000 / clkfreq + + +PUB release +{{ + I do not want to abort if the cog is not + running, as this is called from stop, which + is called from start/ [8^) +}} + if long[pSPI_engine_cog] + long[pSPI_command] := "z" + repeat while long[pSPI_command] == "z" + +'' MIT LICENSE +{{ +' Permission is hereby granted, free of charge, to any person obtaining +' a copy of this software and associated documentation files +' (the "Software"), to deal in the Software without restriction, +' including without limitation the rights to use, copy, modify, merge, +' publish, distribute, sublicense, and/or sell copies of the Software, +' and to permit persons to whom the Software is furnished to do so, +' subject to the following conditions: +' +' The above copyright notice and this permission notice shall be included +' in all copies or substantial portions of the Software. +' +' THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +' EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +' MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +' IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +' CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +' TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +' SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +}} diff --git a/pfth/sd.fth b/pfth/sd.fth new file mode 100644 index 0000000..0326409 --- /dev/null +++ b/pfth/sd.fth @@ -0,0 +1,475 @@ +\ SD-SPI Variables initialize by the boot code +: spi_var create , does> @ spi_vars + ; +\ 0 spi_var spi_engine_cog +\ 4 spi_var spi_command +\ 8 spi_var spi_block_index +\ 12 spi_var spi_buffer_address +16 spi_var spi_rootdir +20 spi_var spi_filesystem +24 spi_var spi_clustershift +28 spi_var spi_dataregion +32 spi_var spi_fat1 +36 spi_var spi_sectorsperfat +40 spi_var spi_currdir + +hex +7fd0 constant spi_engine_cog +7fd4 constant spi_command +7fd8 constant spi_block_index +7fdc constant spi_buffer_address +decimal + +\ Read a single 512-byte sector +: spi_readblock ( block_index buffer_address -- retval ) + spi_engine_cog @ 0= if 2drop -999 exit then \ ERR_SPI_ENGINE_NOT_RUNNING + dup 3 and if 2drop -4 exit then \ ERR_BLOCK_NOT_LONG_ALIGNED + spi_buffer_address ! + spi_block_index ! + [char] r spi_command ! + begin spi_command @ [char] r <> until \ Wait for command to complete + spi_command @ +; + +\ Write a single 512-byte sector +: spi_writeblock ( block_index buffer_address -- retval ) + spi_engine_cog @ 0= if 2drop -999 exit then \ ERR_SPI_ENGINE_NOT_RUNNING + dup 3 and if 2drop -4 exit then \ ERR_BLOCK_NOT_LONG_ALIGNED + spi_buffer_address ! + spi_block_index ! + [char] w spi_command ! + begin spi_command @ [char] w <> until \ Wait for command to complete + spi_command @ +; + +\ Allocate space for file structures +create file_info_1 9 cells allot 512 allot +create file_info_2 9 cells allot 512 allot + +\ Define file structure +0 value file_info +: file_var create , does> @ file_info + ; + 0 file_var file_sector0 + 4 file_var file_length + 8 file_var file_position +12 file_var file_sector +16 file_var file_remaining +20 file_var file_dsector +24 file_var file_doffset +28 file_var file_dirty +32 file_var file_opened +36 file_var spi_buf + +\ Clear the file_opened flags +file_info_1 to file_info 0 file_opened ! +file_info_2 to file_info 0 file_opened ! + +\ Convert a character to upper case +: toupper dup [char] a >= + if + dup [char] z <= + if + 32 - + then + then +; + +create fatname 12 allot + +\ Convert an 8.3 file name to FAT format +: convertfname ( addr num -- ) + fatname 11 bl fill + 8 0 do + dup 1 < + if 2drop r> r> 2drop exit then + over 1+ rot c@ dup [char] . = + if drop swap 1- leave + else + toupper i fatname + c! + swap 1- + then + loop + over c@ [char] . = + if 1- swap 1+ swap then + 11 8 do + dup 1 < + if 2drop r> r> 2drop exit then + over 1+ rot c@ + toupper i fatname + c! + swap 1- + loop + 2drop +; + +\ Compare two strings and return 0 if they match +: compare ( str1 len1 str2 len2 ... n ) + rot over <> if 2drop drop 1 exit then + 0 ?do + 2dup c@ swap c@ <> + if 2drop r> r> 2drop 1 exit then + 1+ swap 1+ + loop + 2drop 0 +; + +: initialize_file ( length sector0 dsector doffset -- fileid ior ) + file_doffset ! + file_dsector ! + dup file_sector0 ! + file_sector ! + dup file_length ! + dup 512 min file_remaining ! + if + file_sector0 @ spi_buf spi_readblock drop + then + 0 file_position ! + 0 file_dirty ! + 1 file_opened ! + 1 0 +; + +\ Get an available file structure +: get-file-struct + file_info_1 to file_info file_opened @ + 0= if 123 exit then + file_info_2 to file_info file_opened @ + 0= if 124 exit then + 0 + ." No file structs available" cr +; + +\ Attempt to open a file and return 0 on success +: open-file ( addr len fam -- fileid ior ) + drop + get-file-struct + dup + 0= if + drop 2drop + 1 1 exit + then + -rot + convertfname + spi_currdir @ + begin + dup spi_buf spi_readblock drop + 512 0 do + i spi_buf + dup c@ + if + dup 11 fatname 11 compare 0= + if + spi_filesystem @ 2 = + if dup 20 + w@ 16 lshift else 0 then + over 26 + w@ + spi_clustershift @ lshift spi_dataregion @ + + swap 28 + @ swap + rot r> r> drop + initialize_file + swap drop + exit + then + drop + else + r> r> 2drop 2drop 1 exit + then + 32 +loop + 1+ + again +; + +\ Set the file structure pointer +: set-file-struct ( fileid ) + dup 123 = if drop file_info_1 to file_info exit then + dup 124 = if drop file_info_2 to file_info exit then + ." Bad fileid " . cr +; + +\ Read one byte from the file. Return -1 on EOF +: file-getc + file_position @ file_length @ >= if -1 exit then + file_remaining @ 1 < + if + file_sector @ 1+ file_sector ! + file_sector @ spi_buf spi_readblock drop + file_length @ file_position @ - 512 min file_remaining ! + then + file_position @ 511 and spi_buf + c@ + file_position @ 1+ file_position ! + file_remaining @ 1- file_remaining ! +; + +: read-file ( addr size fileid -- len flag ior ) + set-file-struct + over swap 0 ?do + file-getc dup -1 = + if + drop swap - dup + if -1 else 0 then + unloop exit + else + over c! 1+ + then + loop + swap - 0 +; + +\ Read one line from the file. Return number of bytes read or -1 on EOF +: read-line ( addr size fileid -- len flag ior ) + set-file-struct + >r dup + begin + r@ 1 < if r> drop swap - -1 0 exit then + file-getc dup -1 = + if + r> 2drop swap - dup + if -1 0 exit then + 0 0 exit + else + dup 10 = if + r> 2drop + 2dup - if + dup 1- c@ 13 = if 1- then + then + swap - -1 0 exit + then + + over c! 1+ + r> 1- >r + then + again +; + +\ Include file +: included 0 open-file + if + drop ." Could not open file" cr + else + #tib @ >in ! + _savesrc + to source-id + refill + \ 0 #tib ! 0 >in ! + then +; + +\ Include file from intepreter +: include bl word count included ; + +\ Get the file size +: file-size ( fileid -- size ior ) drop file_length @ 0 ; + +\ Get the file postion +: file-position ( fileid -- position ior ) drop file_position @ 0 ; + +\ Repostion the file +: reposition-file ( position fileid -- ior ) drop + dup 0 < if drop 1 exit then + dup file_length @ >= if drop 1 exit then + dup -512 and file_position @ -512 and <> + if + dup 9 rshift file_sector0 @ + + dup file_sector ! + spi_buf spi_readblock drop + then + dup file_position ! + file_length @ swap - 512 min file_remaining ! + 0 +; + +: file_rewind + 0 0 reposition-file drop +; + +: file_update_length + file_dsector @ spi_buf spi_readblock drop + file_length @ + file_doffset @ 28 + spi_buf + ! + file_dsector @ spi_buf spi_writeblock drop +; + +: file_putc + file_position @ 511 and spi_buf + c! + file_position @ 1+ dup file_position ! file_length ! + file_position @ 511 and 0= + if + file_sector @ spi_buf spi_writeblock drop + file_sector @ 1+ file_sector ! + file_update_length + 0 + else + 1 + then + file_dirty ! +; + +: write-file ( addr len fileid -- ior ) + set-file-struct + 0 ?do + dup c@ file_putc + 1+ + loop + drop 0 +; + +: write-line ( addr len fileid -- ior ) + write-file + 10 file_putc +; + +: flush-file ( fileid -- ior ) + set-file-struct + file_dirty @ + if + file_sector @ spi_buf spi_writeblock drop + file_update_length + file_sector @ spi_buf spi_readblock drop + 0 file_dirty ! + then + 0 +; + +: find_free_cluster + spi_fat1 @ spi_sectorsperfat @ 0 + ?do + dup spi_buf spi_readblock drop + 1+ + 512 0 + do + spi_filesystem @ 2 = + if + spi_buf i + @ 0= + if + drop + j 128 * i 4 / + + unloop unloop exit + then + 4 + else + spi_buf i + w@ 0= + if + drop + j 256 * i 2 / + + unloop unloop exit + then + 2 + then + +loop + loop + drop 0 +; + +: open_directory + 32768 file_length ! + 0 file_position ! + 0 file_remaining ! + 0 file_dsector ! + 0 file_doffset ! + spi_currdir @ + dup file_sector0 ! + dup file_sector ! + spi_buf spi_readblock drop +; + +: directory_next + file_position @ 32 + dup file_position ! + 511 and 0= + if + file_sector @ 1+ dup file_sector ! + spi_buf spi_readblock drop + then +; + +: find_directory_end + open_directory + begin + file_position @ 511 and spi_buf + c@ + while + directory_next + repeat +; + +: allocate_cluster ( cluster ) + spi_filesystem @ 2 = + if + 4 * dup 512 / spi_fat1 @ + + dup spi_buf spi_readblock drop + swap 511 and spi_buf + 268435455 swap ! + else + 2* dup 512 / spi_fat1 @ + + dup spi_buf spi_readblock drop + swap 511 and spi_buf + 65535 swap w! + then + spi_buf spi_writeblock drop +; + +: create-file ( addr len fam -- fileid ior ) + >r 2dup r> open-file 0= + if swap drop swap drop 0 + 0 file_length ! + file_update_length + exit then + drop + get-file-struct + dup 0= if drop 2drop 1 1 exit then -rot + convertfname + find_free_cluster dup + find_directory_end + file_position @ 511 and spi_buf + + fatname over 11 + cmove + dup 11 + 21 0 fill + spi_filesystem @ 2 = + if + over 16 rshift over 20 + w! + then + 26 + w! + file_position @ 511 and >r + file_sector @ >r + file_position @ 32 + dup file_position ! + 511 and dup 0= + if + file_sector @ spi_buf spi_writeblock drop + file_sector @ 1+ file_sector ! + then + spi_buf + 0 swap c! + file_sector @ spi_buf spi_writeblock drop + dup + allocate_cluster + spi_clustershift @ lshift spi_dataregion @ + + 0 swap r> r> + initialize_file + swap drop +; + +: close-file ( fileid -- ior ) + flush-file + 0 file_opened ! +; + +: delete-file ( addr len -- ior ) + 0 open-file swap drop + if + 1 + else + file_dsector @ spi_buf spi_readblock drop + 229 file_doffset @ spi_buf + c! + file_dsector @ spi_buf spi_writeblock drop + 0 file_opened ! + 0 + then +; + +\ Create an new definition for refill +: _refill1 tib 200 source-id + if + source-id read-line drop + 0 = if + source-id close-file drop + drop \ 0 to source-id 0 + _loadsrc exit + then + else + accept + then + #tib ! 0 >in ! +; + +' _refill1 is refill + diff --git a/pfth/sdpfth.spin b/pfth/sdpfth.spin new file mode 100644 index 0000000..ff7fa5a --- /dev/null +++ b/pfth/sdpfth.spin @@ -0,0 +1,1536 @@ +{ +############################################################################ +# PFTH - This program implements a Forth interpreter. +# +# Copyright (c) 2012, 2013 Dave Hein +# MIT Licensed +############################################################################ +} +con + _clkmode = xtal1+pll16x + _clkfreq = 80_000_000 + + ' SD Pin Definitions + DO = 10 + CLK = 11 + DI = 9 + CS = 25 + + Q = 16 ' Object Offset + + FLAG_IMMEDIATE = 1 + FLAG_CORE = $10 | $80 + FLAG_LIT = $12 | $80 + FLAG_VAR = $20 | $80 + FLAG_DEF = $00 | $80 + FLAG_JMP = $0A | $80 + FLAG_SEMI = FLAG_CORE | FLAG_IMMEDIATE + +obj + spi : "mount" + +'******************************************************************************* +' This Spin code waits three seconds and then starts the Forth cog +'******************************************************************************* +pub main(argc, argv) + waitcnt(clkfreq+cnt) + spi.mount_explicit(@spi_vars, DO, CLK, DI, CS) + coginit(cogid, @forth, @pfthconfig) + +dat +pfthconfig long @xboot_1+Q ' Initial word to execute + long @stack+Q+16 ' Starting stack pointer + long @stack+Q+16 ' Empty stack pointer value + long @retstk+Q ' Starting return pointer + long @retstk+Q ' Empty return pointer value +stack long 0[100] ' Data stack +retstk long 0[100] ' Return stack + +'******************************************************************************* +' pfth cog code +'******************************************************************************* + org 0 +forth +parm mov parm, par +parm1 rdlong pc, parm +parm2 add parm, #4 +parm3 rdlong stackptr, parm +parm4 add parm, #4 +temp rdlong stackptr0, parm +temp1 add parm, #4 +temp2 rdlong returnptr, parm +temp3 add parm, #4 +temp4 rdlong returnptr0, parm + jmp #innerloop ' Begin execution + +'******************************************************************************* +' Execute the words contained in the body of a word +' Changes parm, temp1 +'******************************************************************************* +execlistfunc add parm, #4 ' Get body from XT +innerloopcall wrlong pc, returnptr ' Push PC to return stack + add returnptr, #4 + mov pc, parm ' Set new value for PC + +'******************************************************************************* +' Get an execution token from the location pointed to by the program counter +' Increment the program counter, fetch the code pointer and jump to it +' Changes parm, temp1, pc +'******************************************************************************* +innerloop rdword parm, pc wz + if_z jmp #exitfunc + add pc, #2 + rdword temp1, parm + jmp temp1 + +pc long @xboot_1+Q ' Program Counter + +'******************************************************************************* +' Stop executing the current word, and return to the calling word +' No Changes +'******************************************************************************* +exitfunc sub returnptr, #4 + rdlong pc, returnptr + jmp #innerloop + +'******************************************************************************* +' Abort or quit execution, and return to the interpreter +' No Changes +'******************************************************************************* +abortfunc mov stackptr, stackptr0 +quitfunc mov returnptr, returnptr0 + add returnptr, #4 ' Use second entry return stack + rdlong pc, returnptr + jmp #innerloop + +'******************************************************************************* +' Push the value contained in the word's body onto the stack +' No changes +'******************************************************************************* +confunc add parm, #4 + rdlong parm1, parm + jmp #push_jmp + +'******************************************************************************* +' Push the address of the word's body onto the stack +' Execute the words pointed to by the does pointer, if non-zero +' No changes +'******************************************************************************* +varfunc mov parm1, parm + add parm1, #4 + call #push + +'******************************************************************************* +' Execute the words pointed to by the does pointer, if non-zero +' No changes +'******************************************************************************* +deferfunc add parm, #2 ' DOES> pointer + rdword parm, parm wz + if_z jmp #innerloop ' Done with varfunc + jmp #innerloopcall ' Execute DOES> code + +'******************************************************************************* +' Execute the word on the stack +' Changes parm, temp1 +'******************************************************************************* +executefunc sub stackptr, #4 + rdlong parm, stackptr + rdlong temp1, parm + jmp temp1 ' Execute code + +'******************************************************************************* +' Execute the PASM instruction on the TOS using the next value on the stack as +' the destination register data. Return the result on the stack. +' Changes parm1, parm2 +'******************************************************************************* +cogx1func call #pop2 + mov cogx1instr, parm2 + movd cogx1instr, #parm1 + nop +cogx1instr nop + jmp #push_jmp + +'******************************************************************************* +' Duplicate the top of stack +' Changes parm1 +'******************************************************************************* +dupfunc call #pop + call #push + jmp #push_jmp + +'******************************************************************************* +' Swap the top two items on the stack +' Changes parm1, parm2 +'******************************************************************************* +swapfunc call #pop2 + wrlong parm2, stackptr + add stackptr, #4 + jmp #push_jmp + +'******************************************************************************* +' Get the next word from the input buffer using the delimiter from the stack +' Changes parm, parm1, parm2, temp1, temp2 +'******************************************************************************* +wordfunc sub stackptr, #4 + rdlong parm, stackptr + call #word_del + mov temp1, #1 + shl temp1, #15 + sub temp1, parm2 + sub temp1, #1 + wrlong temp1, stackptr + add stackptr, #4 + wrbyte parm2, temp1 + cmps parm2, #0 wc, wz + if_c_or_z jmp #innerloop +:loop add temp1, #1 + rdbyte temp2, parm1 + add parm1, #1 + wrbyte temp2, temp1 + djnz parm2, #:loop + jmp #innerloop + +'******************************************************************************* +' Find the word specfied on the stack in the dictionary +' Changes parm1, parm2, temp4 +'******************************************************************************* +findfunc call #pop + mov temp4, parm1 + add parm1, #1 + rdbyte parm2, temp4 + call #findword + mov parm1, parm wz + if_z jmp #findfunc1 + call #link2xt + call #push + add parm, #2 ' Point to flag byte + rdbyte parm1, parm + and parm1, #1 ' Check immediate bit + shl parm1, #1 + sub parm1, #1 ' Return 1 if set, -1 if not + call #push + jmp #innerloop +findfunc1 mov parm1, temp4 + call #push + mov parm1, #0 + call #push + jmp #innerloop + +'******************************************************************************* +' Send the character from the stack to the output port +' Changes parm +'******************************************************************************* +emitfunc call #pop + mov parm, parm1 + call #putch + jmp #innerloop + +'******************************************************************************* +' Get a character from the input port and put it on the stack +' Changes parm, parm1 +'******************************************************************************* +getcharfunc call #getch + mov parm1, parm + jmp #push_jmp + +'******************************************************************************* +' Get a character from the files stored in memory and put it on the stack +' Changes parm1 +'******************************************************************************* +getfcharfunc rdbyte parm1, infileptr + add infileptr, #1 + jmp #push_jmp + +'******************************************************************************* +' Get an address and value from the stack, and store the value at the address +' No changes +'******************************************************************************* +storefunc call #pop2 + wrlong parm1, parm2 + jmp #innerloop + +'******************************************************************************* +' Fetch a value from the address specified on the stack, and put it on the stack +' Changes parm1 +'******************************************************************************* +fetchfunc call #pop + rdlong parm1, parm1 + jmp #push_jmp + +'******************************************************************************* +' Get an address and word from the stack, and store the word at the address +' No changes +'******************************************************************************* +wstorefunc call #pop2 + wrword parm1, parm2 + jmp #innerloop + +'******************************************************************************* +' Fetch a word from the address specified on the stack, and put it on the stack +' Changes parm1 +'******************************************************************************* +wfetchfunc call #pop + rdword parm1, parm1 + jmp #push_jmp + +'******************************************************************************* +' Get an address and byte from the stack, and store the byte at the address +' No changes +'******************************************************************************* +cstorefunc call #pop2 + wrbyte parm1, parm2 + jmp #innerloop + +'******************************************************************************* +' Fetch a byte from the address specified on the stack, and put it on the stack +' Changes parm1 +'******************************************************************************* +cfetchfunc call #pop + rdbyte parm1, parm1 + jmp #push_jmp + +'******************************************************************************* +' Add two values from the stack, and write the result back to the stack +' Changes parm1, parm2 +'******************************************************************************* +plusfunc call #pop2 + add parm1, parm2 + jmp #push_jmp + +'******************************************************************************* +' Subtract two values from the stack, and write the result back to the stack +' Changes parm1, parm2 +'******************************************************************************* +minusfunc call #pop2 + sub parm1, parm2 + jmp #push_jmp + +'******************************************************************************* +' Multiply two values from the stack, and write the result back to the stack +' Changes parm1, parm2 +'******************************************************************************* +multfunc call #pop2 + call #multiply + jmp #push_jmp + +'******************************************************************************* +' Divide two values from the stack, and write the result back to the stack +' Changes parm1, parm2 +'******************************************************************************* +dividefunc call #pop2 + call #divide + mov parm1, parm2 + test parm3, #1 wc + if_c neg parm1, parm1 + jmp #push_jmp + +'******************************************************************************* +' Compute the modulus from two values from the stack, and write the result back +' to the stack +' Changes parm1, parm2 +'******************************************************************************* +modfunc call #pop2 + call #divide + test parm3, #2 wc + if_c neg parm1, parm1 + jmp #push_jmp + +'******************************************************************************* +' Compare two values from the stack to determine if the second one is less than +' the first one, and write the result back to the stack +' Changes parm1, parm2 +'******************************************************************************* +lessfunc call #pop2 + cmps parm1, parm2 wc + if_c neg parm1, #1 + if_nc mov parm1, #0 + jmp #push_jmp + +'******************************************************************************* +' Compare two values from the stack to determine if they are equal, and write +' the result back to the stack +' Changes parm1, parm2 +'******************************************************************************* +equalfunc call #pop2 + cmp parm1, parm2 wz + if_z neg parm1, #1 + if_nz mov parm1, #0 + jmp #push_jmp + +'******************************************************************************* +' Compare two values from the stack to determine if the second one is greater +' than the first one, and write the result back to the stack +' Changes parm1, parm2 +'******************************************************************************* +greaterfunc call #pop2 + cmps parm1, parm2 wc, wz + if_nz_and_nc neg parm1, #1 + if_z_or_c mov parm1, #0 + jmp #push_jmp + +'******************************************************************************* +' Compute the logical AND of two values from the stack, and write the result +' back to the stack +' Changes parm1, parm2 +'******************************************************************************* +andfunc call #pop2 + and parm1, parm2 + jmp #push_jmp + +'******************************************************************************* +' Compute the logical OR of two values from the stack, and write the result +' back to the stack +' Changes parm1, parm2 +'******************************************************************************* +orfunc call #pop2 + or parm1, parm2 + jmp #push_jmp + +'******************************************************************************* +' Compute the logical XOR of two values from the stack, and write the result +' back to the stack +' Changes parm1, parm2 +'******************************************************************************* +xorfunc call #pop2 + xor parm1, parm2 + jmp #push_jmp + +'******************************************************************************* +' Right-shift the second value on the stack by the number of bits specified by +' the first value on the stack, and write the result to the stack +' Changes parm1, parm2 +'******************************************************************************* +rshiftfunc call #pop2 + shr parm1, parm2 + jmp #push_jmp + +'******************************************************************************* +' Left-shift the second value on the stack by the number of bits specified by +' the first value on the stack, and write the result to the stack +' Changes parm1, parm2 +'******************************************************************************* +lshiftfunc call #pop2 + shl parm1, parm2 + jmp #push_jmp + +'******************************************************************************* +' Push the stack depth to the stack +' Changes parm1 +'******************************************************************************* +depthfunc mov parm1, stackptr + sub parm1, stackptr0 + sar parm1, #2 + jmp #push_jmp + +'******************************************************************************* +' Drop the top value from the stack +' No changes +'******************************************************************************* +dropfunc sub stackptr, #4 + jmp #innerloop + +'******************************************************************************* +' Use the value on top of the stack as an index to another value in the stack, +' and write its value to the stack +' No changes +'******************************************************************************* +pickfunc call #pop + call #indexstack + jmp #push_jmp + +'******************************************************************************* +' Use the value on top of the stack as and index to remove another value from +' the stack, and place it at the top of the stack. +' Changes temp1, temp2, temp3, temp4 +'******************************************************************************* +rollfunc call #pop + cmp parm1, #0 wc, wz + if_c_or_z jmp #innerloop + mov temp3, parm1 + call #indexstack + mov temp2, temp1 +:loop add temp2, #4 + rdlong temp4, temp2 + wrlong temp4, temp1 + add temp1, #4 + djnz temp3, #:loop + wrlong parm1, temp1 + jmp #innerloop + +'******************************************************************************* +' Pop the value from the top of the stack, and push it onto the return stack. +' No changes +'******************************************************************************* +torfunc call #pop + wrlong parm1, returnptr + add returnptr, #4 + jmp #innerloop + +'******************************************************************************* +' Pop the value from the top of the return stack and push it to the stack. +' Changes parm1 +'******************************************************************************* +fromrfunc sub returnptr, #4 + rdlong parm1, returnptr + jmp #push_jmp + +'******************************************************************************* +' Push the value on the stack pointed to by the PC and increment the PC +' Changes parm1 +'******************************************************************************* +_litfunc rdword parm1, pc + add pc, #2 + jmp #push_jmp + +'******************************************************************************* +' Convert the string described by the address and length on the top of the +' stack to a hex number, and push it to the stack +' Changes parm1 +'******************************************************************************* +_gethexfunc call #pop2 + call #gethex + mov parm1, parm + jmp #push_jmp + +'******************************************************************************* +' Create a variable, and add it to the dictionary +' Changes parm3 +'******************************************************************************* +createfunc mov parm3, #varfunc + mov parm4, #FLAG_VAR + call #create + jmp #innerloop + +'******************************************************************************* +' Create an executable word, and add it to the dictionary. Set the compile +' state to -1 +' Changes parm3, temp1 +'******************************************************************************* +colonfunc mov parm3, #execlistfunc + mov parm4, #FLAG_DEF + call #create + if_z jmp #innerloop + neg temp1, #1 + wrlong temp1, a_state + jmp #innerloop + +'******************************************************************************* +' Compile a zero into memory indicating the end of an executable word, and set +' the compile flag to zero +' Changes temp1, temp2 +'******************************************************************************* +semicolonfunc mov temp1, #0 + wrlong temp1, a_state + rdlong temp2, a_dp + wrword temp1, temp2 + add temp2, #2 + wrlong temp2, a_dp + jmp #innerloop + +'******************************************************************************* +' Fetch a value from the specified cog address, and put it on the stack +' the compile flag to zero +' Changes parm1 +'******************************************************************************* +cogfetchfunc call #pop + movs cogfetch1, parm1 + nop +cogfetch1 mov parm1, 0-0 + jmp #push_jmp + +'******************************************************************************* +' Get a cog address and value from the stack, and store the value at the address +' the compile flag to zero +' Changes parm1, parm2 +'******************************************************************************* +cogstorefunc call #pop2 + movd cogstore1, parm2 + nop +cogstore1 mov 0-0, parm1 + jmp #innerloop + + +'******************************************************************************* +' Print out an 8-digit hex number to the output port. +' Changes parm +'******************************************************************************* +dotxfunc mov parm, #"$" + call #putch + call #pop + call #printhex + mov parm, #" " + call #putch + jmp #innerloop + +'******************************************************************************* +' If top of stack is zero, jump to address contained in location at current PC. +' Otherwise, increment the PC +' Changes parm1 +'******************************************************************************* +_jzfunc call #pop + if_z rdword pc, pc + if_nz add pc, #2 + jmp #innerloop + +'******************************************************************************* +' Copy bytes from the source to the destination +' Changes parm1 +'******************************************************************************* +cmovefunc sub stackptr, #4 + rdlong parm3, stackptr + call #pop2 + cmps parm3, #0 wz, wc + if_c_or_z jmp #innerloop +:loop rdbyte temp1, parm1 + add parm1, #1 + wrbyte temp1, parm2 + add parm2, #1 + djnz parm3, #:loop + jmp #innerloop + +'******************************************************************************* +' Perform the increment and compare for the loop word +' Changes parm1, parm2, parm3 +'******************************************************************************* +_loopfunc call #pop ' Get increment + sub returnptr, #8 + rdlong parm3, returnptr ' Get upper limit + add returnptr, #4 + rdlong parm2, returnptr ' Get index + add parm1, parm2 ' index + increment + wrlong parm1, returnptr ' Push index back + add returnptr, #4 + cmps parm1, parm3 wc + if_nc neg parm1, #1 + if_c mov parm1, #0 + jmp #push_jmp + +'******************************************************************************* +' The following code implements the basic functions used by the kernel words +'******************************************************************************* + +'******************************************************************************* +' Create a word entry in the dictionary +' Changes parm, parm1, parm2, temp1, temp2 +'******************************************************************************* +create mov parm, #" " + call #word_del + if_z jmp create_ret + rdlong temp1, a_dp ' Align DP + add temp1, #3 + and temp1, minus4 + rdlong temp2, a_last + wrword temp2, temp1 ' Write the link pointer + wrlong temp1, a_last ' Update LAST + add temp1, #2 + + wrbyte parm4, temp1 ' Write the flag + add temp1, #1 + wrbyte parm2, temp1 ' Write the length + add temp1, #1 + cmps parm2, #0 wc, wz + if_c_or_z jmp #create_done +:loop rdbyte temp2, parm1 ' Copy the name + add parm1, #1 + wrbyte temp2, temp1 + add temp1, #1 wz + djnz parm2, #:loop + +create_done mov temp2, #0 ' Pad with 0's to align +:loop1 test temp1, #3 wz + if_z jmp #create_aligned + wrbyte temp2, temp1 + add temp1, #1 + jmp #:loop1 + +create_aligned wrword parm3, temp1 ' Write the code pointer + add temp1, #2 + wrword temp2, temp1 ' Write the DOES> pointer + add temp1, #2 wz ' Clear zero flag + + wrlong temp1, a_dp +create_ret ret + +'******************************************************************************* +' Get one character from the input port. +' Input none +' Changes parm, temp, temp1, temp2 +' Output parm +'******************************************************************************* +getch mov parm, ina + and parm, inbit wz + if_nz jmp #getch + mov temp2, cnt + mov temp, bitcycles + shr temp, #1 + add temp2, temp + mov temp1, #10 +:loop waitcnt temp2, bitcycles + mov temp, ina + and temp, inbit + ror parm, #1 + or parm, temp + djnz temp1, #:loop + ror parm, #31 - 8 + and parm, #255 +getch_ret ret + +inbit long $80000000 +bitcycles long 80_000_000 / 115_200 + +'******************************************************************************* +' Send one character to the output port. +' Input parm +' Changes parm, temp1, temp2 +' Output none +'******************************************************************************* +putch rdlong temp1, a_verbose wz + if_z jmp putch_ret + or parm, #$100 + shl parm, #1 + rol parm, #30 + mov temp1, #10 + mov temp2, bitcycles + add temp2, cnt +:loop mov outa, parm + ror parm, #1 + waitcnt temp2, bitcycles + djnz temp1, #:loop +putch_ret ret + +outbit long $40000000 + +'******************************************************************************* +' Skip the specified character in the input buffer +' Input parm +' Changes temp, temp1 +' Output none +'******************************************************************************* +skipchar cmps temp1, temp2 wc + if_nc jmp skipchar_ret + rdlong temp, a_tib + add temp, temp1 + rdbyte temp, temp + cmp temp, parm wz + if_nz jmp skipchar_ret + add temp1, #1 + jmp #skipchar +skipchar_ret ret + +'******************************************************************************* +' Find the next occurance of the specified character in the input buffer +' Input parm +' Changes temp, temp1 +' Output none +'******************************************************************************* +findchar cmps temp1, temp2 wc + if_nc jmp findchar_ret + rdlong temp, a_tib + add temp, temp1 + rdbyte temp, temp + cmp temp, parm wz + if_z jmp findchar_ret + add temp1, #1 + jmp #findchar +findchar_ret ret + +'******************************************************************************* +' Find the next word in the input buffer delimited by the specified character +' Input parm +' Changes parm1, parm2, temp1, temp2 +' Output none +'******************************************************************************* +word_del + rdlong temp1, a_inputidx + rdlong temp2, a_inputlen + call #skipchar + mov parm1, temp1 + call #findchar + mov parm2, temp1 + sub parm2, parm1 wz + rdlong temp, a_tib + add parm1, temp + cmps temp1, temp2 wc + if_c add temp1, #1 + wrlong temp1, a_inputidx +word_del_ret ret + +'******************************************************************************* +' Find the specified word in the dictionary +' Input parm1, parm2 +' Changes parm, parm3, parm4 +' Output parm +'******************************************************************************* +findword rdlong parm, a_last wz + if_z jmp findword_ret +:loop mov parm3, parm + add parm3, #3 + rdbyte parm4, parm3 + add parm3, #1 + call #compare + if_z jmp findword_ret + rdword parm, parm wz + if_nz jmp #:loop +findword_ret ret + +'******************************************************************************* +' Do a case insensitive comparison of two character strings +' Input parm1, parm2, parm3, parm4 +' Changes parm3, parm4, temp, temp1, temp2 +' Outut Z +'******************************************************************************* +compare cmps parm2, #1 wc, wz + if_c jmp compare_ret + cmp parm2, parm4 wz + if_nz jmp compare_ret + mov temp, parm1 +:loop rdbyte temp1, temp + call #toupper + mov temp2, temp1 + rdbyte temp1, parm3 + call #toupper + cmp temp1, temp2 wz + if_nz jmp compare_ret + add temp, #1 + add parm3, #1 + djnz parm4, #:loop +compare_ret ret + +'******************************************************************************* +' Convert a character to uppercase +' Input temp1 +' Changes temp1 +' Ouput temp1 +'******************************************************************************* +toupper cmp temp1, #"a" wc + if_c jmp toupper_ret + cmp temp1, #"z" wc, wz + if_nc_and_nz jmp toupper_ret + sub temp1, #"a" - "A" +toupper_ret ret + + +'******************************************************************************* +' Print an 8-digit hex value to the output port +' Input parm1 +' Changes parm, parm1, parm2 +' Output none +'******************************************************************************* +printhex mov parm2, #8 +:loop rol parm1, #4 + mov parm, #15 + and parm, parm1 + add parm, a_hexstr + rdbyte parm, parm + call #putch + djnz parm2, #:loop +printhex_ret ret + + +'******************************************************************************* +' Convert a string to a hex number +' Input parm1, parm2 +' Changes parm, temp, temp1, temp2 +' Output parm +'******************************************************************************* +gethex mov parm, #0 + cmps parm2, #0 wc, wz + if_c_or_z jmp gethex_ret + mov temp1, parm1 + mov temp2, parm2 +:loop rdbyte temp, temp1 + add temp1, #1 + sub temp, #"0" + cmps temp, #10 wc + if_nc sub temp, #"a"-"0"-10 + shl parm, #4 + add parm, temp + djnz temp2, #:loop +gethex_ret ret + +'******************************************************************************* +' Push a value onto the data stack +' Input parm1 +' No changes +' Output none +'******************************************************************************* +push wrlong parm1, stackptr + add stackptr, #4 +push_ret ret + +'******************************************************************************* +' Push a value onto the data stack and jump to the innerloop +' Input parm1 +' No changes +' Output none +'******************************************************************************* +push_jmp wrlong parm1, stackptr + add stackptr, #4 + jmp #innerloop + +'******************************************************************************* +' Pop two values off of the data stack +' Input none +' Changes parm1, parm2 +' Output parm1, parm2 +'******************************************************************************* +pop2 sub stackptr, #4 + rdlong parm2, stackptr + +'******************************************************************************* +' Pop one value off of the data stack +' Input none +' Changes parm1 +' Ouput parm1 +'******************************************************************************* +pop sub stackptr, #4 + rdlong parm1, stackptr wz +pop_ret +pop2_ret ret + +'******************************************************************************* +' Read a value on the stack based on an index number +' Changes parm1, temp1 +'******************************************************************************* +indexstack neg temp1, parm1 + shl temp1, #2 + sub temp1, #4 + add temp1, stackptr + rdlong parm1, temp1 +indexstack_ret ret + +'******************************************************************************* +' Compute the XT from the address of the link +' Input: parm1 +' Output: parm1 +' Changes: temp1 +'******************************************************************************* +link2xt mov temp1, parm1 + add temp1, #3 + rdbyte parm1, temp1 ' Get name length + add parm1, temp1 + add parm1, #4 + and parm1, minus4 ' Align +link2xt_ret ret + +'******************************************************************************* +' Multiply two 32-bit numbers +' Changes parm2, temp1, temp2 +'******************************************************************************* +multiply mov temp1, #0 + mov temp2, #32 + shr parm1, #1 wc +mmul if_c add temp1, parm2 wc + rcr temp1, #1 wc + rcr parm1, #1 wc + djnz temp2, #mmul +multiply_ret ret + +'******************************************************************************* +' Divide two 32-bit numbers producing a quotient and a remainder +' Changes parm1, parm2, parm3, temp1, temp2 +'******************************************************************************* +divide mov temp2, #32 + mov temp1, #0 + abs parm1, parm1 wc + muxc parm3, #%11 + abs parm2, parm2 wc,wz + if_c xor parm3, #%01 +' if_nz jmp #mdiv +' mov parm1, #0 +' jmp divide_ret +mdiv shr parm2, #1 wc,wz + rcr temp1, #1 + if_nz djnz temp2, #mdiv +mdiv2 cmpsub parm1, temp1 wc + rcl parm2, #1 + shr temp1, #1 + djnz temp2, #mdiv2 +divide_ret ret + +'******************************************************************************* +' These are working registers. The parm registers are generally used to pass +' parameters from one routine to another, and the temp registers are used as +' temporary storage within a routine. +'******************************************************************************* + +'******************************************************************************* +' Addresses of variables in the dictionary, and the hex table +'******************************************************************************* +a_hexstr long @hexstr+Q +a_last long @last+Q +a_state long @state+Q +a_dp long @dp+Q +a_tib long @tib+Q +a_verbose long @verbose+Q +a_inputidx long @greaterin+Q +a_inputlen long @poundtib+Q + +'******************************************************************************* +' The data and return stack pointers, and their base addresses +'******************************************************************************* +stackptr long 0 +stackptr0 long 0 +returnptr long 0 +returnptr0 long 0 + +'******************************************************************************* +' The input file pointer used during initialization +'******************************************************************************* +infileptr long @infile+Q + +'******************************************************************************* +' Constants +'******************************************************************************* +minus4 long -4 + + fit $1f0 + +'******************************************************************************* +' Input buffer and hex table +'******************************************************************************* +hexstr byte "0123456789abcdef" +inputbuf byte 0[200] + +'******************************************************************************* +' This is the beginning of the dictionary. The kernel words are specified below +'******************************************************************************* +exit_L word 0 + byte FLAG_CORE, 4, "exit" + long +exit_X word exitfunc, 0 + +quit_L word @exit_L+Q + byte FLAG_CORE, 4, "quit" + long +quit_X word quitfunc, 0 + +abort_L word @quit_L+Q + byte FLAG_CORE, 5, "abort" + long +abort_X word abortfunc, 0 + +execute_L word @abort_L+Q + byte FLAG_CORE, 7, "execute" + long +execute_X word executefunc, 0 + +word_L word @execute_L+Q + byte FLAG_CORE, 4, "word" + long +word_X word wordfunc, 0 + +find_L word @word_L+Q + byte FLAG_CORE, 4, "find" + long +find_X word findfunc, 0 + +getchar_L word @find_L+Q + byte FLAG_CORE, 7, "getchar" + long +getchar_X word getcharfunc, 0 + +getfchar_L word @getchar_L+Q + byte FLAG_CORE, 8, "getfchar" + long +getfchar_X word getfcharfunc, 0 + +key_L word @getfchar_L+Q + byte FLAG_CORE, 3, "key" + long +key_X word deferfunc, @key_B+Q +key_B word @getfchar_X+Q, 0 + +create_L word @key_L+Q + byte FLAG_CORE, 6, "create" + long +create_X word createfunc, 0 + +_lit_L word @create_L+Q + byte FLAG_LIT, 4, "_lit" + long +_lit_X word _litfunc, 0 + +_gethex_L word @_lit_L+Q + byte FLAG_CORE, 7, "_gethex" + long +_gethex_X word _gethexfunc, 0 + +emit_L word @_gethex_L+Q + byte FLAG_CORE, 4, "emit" + long +emit_X word emitfunc, 0 + +store_L word @emit_L+Q + byte FLAG_CORE, 1, "!" + long +store_X word storefunc, 0 + +fetch_L word @store_L+Q + byte FLAG_CORE, 1, "@" + long +fetch_X word fetchfunc, 0 + +wstore_L word @fetch_L+Q + byte FLAG_CORE, 2, "w!" + long +wstore_X word wstorefunc, 0 + +wfetch_L word @wstore_L+Q + byte FLAG_CORE, 2, "w@" + long +wfetch_X word wfetchfunc, 0 + +cstore_L word @wfetch_L+Q + byte FLAG_CORE, 2, "c!" + long +cstore_X word cstorefunc, 0 + +cfetch_L word @cstore_L+Q + byte FLAG_CORE, 2, "c@" + long +cfetch_X word cfetchfunc, 0 + +plus_L word @cfetch_L+Q + byte FLAG_CORE, 1, "+" + long +plus_X word plusfunc, 0 + +minus_L word @plus_L+Q + byte FLAG_CORE, 1, "-" + long +minus_X word minusfunc, 0 + +multiply_L word @minus_L+Q + byte FLAG_CORE, 1, "*" + long +multiply_X word multfunc, 0 + +divide_L word @multiply_L+Q + byte FLAG_CORE, 1, "/" + long +divide_X word dividefunc, 0 + +mod_L word @divide_L+Q + byte FLAG_CORE, 3, "mod" + long +mod_X word modfunc, 0 + +and_L word @mod_L+Q + byte FLAG_CORE, 3, "and" + long +and_X word andfunc, 0 + +or_L word @and_L+Q + byte FLAG_CORE, 2, "or" + long +or_X word orfunc, 0 + +xor_L word @or_L+Q + byte FLAG_CORE, 3, "xor" + long +xor_X word xorfunc, 0 + +less_L word @xor_L+Q + byte FLAG_CORE, 1, "<" + long +less_X word lessfunc, 0 + +equal_L word @less_L+Q + byte FLAG_CORE, 1, "=" + long +equal_X word equalfunc, 0 + +greater_L word @equal_L+Q + byte FLAG_CORE, 1, ">" + long +greater_X word greaterfunc, 0 + +rshift_L word @greater_L+Q + byte FLAG_CORE, 6, "rshift" + long +rshift_X word rshiftfunc, 0 + +lshift_L word @rshift_L+Q + byte FLAG_CORE, 6, "lshift" + long +lshift_X word lshiftfunc, 0 + +depth_L word @lshift_L+Q + byte FLAG_CORE, 5, "depth" + long +depth_X word depthfunc, 0 + +tib_L word @depth_L+Q + byte FLAG_VAR, 3, "tib" + long +tib_X word varfunc, @tib+Q+4 +tib long @inputbuf+Q + word @fetch_X+Q, 0 + long + +poundtib_L word @tib_L+Q + byte FLAG_VAR, 4, "#tib" + long +poundtib_X word varfunc, 0 +poundtib long 0 + +greaterin_L word @poundtib_L+Q + byte FLAG_VAR, 3, ">in" + long +greaterin_X word varfunc, 0 +greaterin long 0 + +dp_L word @greaterin_L+Q + byte FLAG_VAR, 2, "dp" + long +dp_X word varfunc, 0 +dp long @_here+Q + +last_L word @dp_L+Q + byte FLAG_VAR, 4, "last" + long +last_X word varfunc, 0 +last long @_last+Q + +state_L word @last_L+Q + byte FLAG_VAR, 5, "state" + long +state_X word varfunc, 0 +state long 0 + +base_L word @state_L+Q + byte FLAG_VAR, 4, "base" + long +base_X word varfunc, 0 +base long 16 + +verbose_L word @base_L+Q + byte FLAG_VAR, 7, "verbose" + long +verbose_X word varfunc, 0 +verbose long 0 + +forth_L word @verbose_L+Q + byte FLAG_VAR, 5, "forth" + long +forth_X word varfunc, 0 + long @forth+Q + +drop_L word @forth_L+Q + byte FLAG_CORE, 4, "drop" + long +drop_X word dropfunc, 0 + +dup_L word @drop_L+Q + byte FLAG_CORE, 3, "dup" + long +dup_X word dupfunc, 0 + +swap_L word @dup_L+Q + byte FLAG_CORE, 4, "swap" + long +swap_X word swapfunc, 0 + +pick_L word @swap_L+Q + byte FLAG_CORE, 4, "pick" + long +pick_X word pickfunc, 0 + +roll_L word @pick_L+Q + byte FLAG_CORE, 4, "roll" + long +roll_X word rollfunc, 0 + +tor_L word @roll_L+Q + byte FLAG_CORE, 2, ">r" + long +tor_X word torfunc, 0 + +fromr_L word @tor_L+Q + byte FLAG_CORE, 2, "r>" + long +fromr_X word fromrfunc, 0 + +colon_L word @fromr_L+Q + byte FLAG_CORE, 1, ":" + long +colon_X word colonfunc, 0 + +semicolon_L word @colon_L+Q + byte FLAG_SEMI, 1, ";" + long +semicolon_X word semicolonfunc, 0 + +cogfetch_L word @semicolon_L+Q + byte FLAG_CORE, 4, "cog@" + long +cogfetch_X word cogfetchfunc, 0 + +cogstore_L word @cogfetch_L+Q + byte FLAG_CORE, 4, "cog!" + long +cogstore_X word cogstorefunc, 0 + +cogx1_L word @cogstore_L+Q + byte FLAG_CORE, 5, "cogx1" + long +cogx1_X word cogx1func, 0 + +_jz_L word @cogx1_L+Q + byte FLAG_JMP, 3, "_jz" + long +_jz_X word _jzfunc, 0 + +cmove_L word @_jz_L+Q + byte FLAG_CORE, 5, "cmove" + long +cmove_X word cmovefunc, 0 + +dotx_L word @cmove_L+Q + byte FLAG_CORE, 2, ".x" + long +dotx_X word dotxfunc, 0 + +'******************************************************************************* +' SPI/SD Variables +'******************************************************************************* +spi_vars_L word @dotx_L+Q + byte FLAG_VAR, 8, "spi_vars" + long +spi_vars_X word varfunc, 0 +spi_vars long 0 ' SPI_engine_cog + long 0 ' SPI_command + long 0 ' SPI_block_index + long 0 ' SPI_buffer_address + long 0 ' SD_rootdir + long 0 ' SD_filesystem + long 0 ' SD_clustershift + long 0 ' SD_dataregion + long 0 ' SD_fat1 + long 0 ' SD_sectorsperfat + long 0 ' SD_currdir + +argc_L word @spi_vars_L+Q + byte FLAG_VAR, 4, "argc" + long +argc_X word confunc, 0 +argc_B long 0 + +argv_L word @argc_L+Q + byte FLAG_VAR, 4, "argv" + long +argv_X word confunc, 0 +argv_B long 0 + +hostcwd_L word @argv_L+Q + byte FLAG_VAR, 7, "hostcwd" + long +hostcwd_X word confunc, 0 +hostcwd_B long 0 + +'******************************************************************************* +' A small number of compiled words follow below. These are used by the boot +' interpreter. +'******************************************************************************* + ' : here dp @ ; +here_L word @hostcwd_L+Q + byte FLAG_DEF, 4, "here" + long +here_X word execlistfunc, 0 + word @dp_X+Q, @fetch_X+Q, 0 + long + + ' : allot dp @ + dp ! ; +allot_L word @here_L+Q + byte FLAG_DEF, 5, "allot" + long +allot_X word execlistfunc, 0 + word @dp_X+Q, @fetch_X+Q, @plus_X+Q, @dp_X+Q, @store_X+Q, 0 + long + + ' : , here ! 4 allot ; +comma_L word @allot_L+Q + byte FLAG_DEF, 1, "," + long +comma_X word execlistfunc, 0 + word @here_X+Q, @store_X+Q, @_lit_X+Q, 4, @allot_X+Q, 0 + long + + ' : _jmp r> @ >r ; +_jmp_L word @comma_L+Q + byte FLAG_JMP, 4, "_jmp" + long +_jmp_X word execlistfunc, 0 + word @fromr_X+Q, @wfetch_X+Q, @tor_X+Q, 0 + long + + ' : count 0 pick 1 + 1 roll c@ ; +count_L word @_jmp_L+Q + byte FLAG_DEF, 5, "count" + long +count_X word execlistfunc, 0 + word @_lit_X+Q, 0, @pick_X+Q, @_lit_X+Q, 1, @plus_X+Q, @_lit_X+Q, 1, @roll_X+Q, @cfetch_X+Q, 0 + long + + ' : accept ( addr size -- num ) \ Accept a string from the input source +accept_L word @count_L+Q + byte FLAG_DEF, 6, "accept" + long +accept_X word execlistfunc, 0 + ' >r dup + word @tor_X+Q, @dup_X+Q + ' r> dup 1 < _jz _accept4 +accept_1 word @fromr_X+Q, @dup_X+Q, @_lit_X+Q, 1, @less_X+Q, @_jz_X+Q, @accept_4+Q + ' drop swap - exit + word @drop_X+Q, @swap_X+Q, @minus_X+Q, @exit_X+Q + ' >r key +accept_4 word @tor_X+Q, @key_X+Q + ' dup 0d = over 0a = or + word @dup_X+Q, @_lit_X+Q, $0d, @equal_X+Q, @_lit_X+Q, 1, @pick_X+Q, @_lit_X+Q, $0a, @equal_X+Q, @or_X+Q + ' _jz _accept2 + word @_jz_X+Q, @accept_2+Q + ' cr drop swap - + word @_lit_X+Q, 13, @emit_X+Q, @_lit_X+Q, 10, @emit_X+Q, @drop_X+Q, @swap_X+Q, @minus_X+Q + ' r> drop exit + word @fromr_X+Q, @drop_X+Q, @exit_X+Q + ' dup 8 = _jz _accept3 +accept_2 word @dup_X+Q, @_lit_X+Q, 8, @equal_X+Q, @_jz_X+Q, @accept_3+Q + ' drop over over - _jz _accept1 + word @drop_X+Q, @_lit_X+Q, 1, @pick_X+Q, @_lit_X+Q, 1, @pick_X+Q, @minus_X+Q, @_jz_X+Q, @accept_1+Q + ' 1 - r> 1 + >r + word @_lit_X+Q, 1, @minus_X+Q, @fromr_X+Q, @_lit_X+Q, 1, @plus_X+Q, @tor_X+Q + ' 8 emit bl emit 8 emit _jmp _accept1 + word @_lit_X+Q, 8, @emit_X+Q, @_lit_X+Q, 32, @emit_X+Q, @_lit_X+Q, 8, @emit_X+Q, @_jmp_X+Q, @accept_1+Q + ' dup emit over c! 1 + +accept_3 word @dup_X+Q, @emit_X+Q, @_lit_X+Q, 1, @pick_X+Q, @cstore_X+Q, @_lit_X+Q, 1, @plus_X+Q + ' r> 1 - >r _jmp _accept1 + word @fromr_X+Q, @_lit_X+Q, 1, @minus_X+Q, @tor_X+Q, @_jmp_X+Q, @accept_1+Q, 0 + long + + ' : refill tib 200 accept #tib ! 0 >in ! ; +refill_L word @accept_L+Q + byte FLAG_DEF, 6, "refill" + long +refill_X word execlistfunc, 0 + word @tib_X+Q, @_lit_X+Q, 200, @accept_X+Q, @poundtib_X+Q, @store_X+Q, @_lit_X+Q, 0, @greaterin_X+Q, @store_X+Q, 0 + long + + ' : compile, here w! 2 allot ; +compcomma_L word @refill_L+Q + byte FLAG_DEF, 8, "compile," + long +compcomma_X word execlistfunc, 0 + word @here_X+Q, @wstore_X+Q, @_lit_X+Q, 2, @allot_X+Q, 0 + long + +'******************************************************************************* +' The boot interpreter follows below. +'******************************************************************************* + ' : xboot ( This word runs a simple interpreter ) +xboot_L word @compcomma_L+Q + byte FLAG_DEF, 5, "xboot" + long +xboot_X word execlistfunc, 0 + + ' 20 word 0 pick c@ _jz _xboot2 ( Get word, refill if empty ) +xboot_1 word @_lit_X+Q, $20, @word_X+Q, @_lit_X+Q, 0, @pick_X+Q, @cfetch_X+Q, @_jz_X+Q, @xboot_2+Q + + ' find 0 pick _jz _xboot3 ( Find word, get number if not found ) + word @find_X+Q, @_lit_X+Q, 0, @pick_X+Q, @_jz_X+Q, @xboot_3+Q + + ' state @ = _jz _xboot4 ( Go execute if not compile mode or immediate ) + word @state_X+Q, @fetch_X+Q, @equal_X+Q, @_jz_X+Q, @xboot_4+Q + + ' compile, _jmp _xboot1 ( Otherwise, compile and loop again ) + word @compcomma_X+Q, @_jmp_X+Q, @xboot_1+Q + + ' execute _jmp _xboot1 ( Execute and loop again ) +xboot_4 word @execute_X+Q, @_jmp_X+Q, @xboot_1+Q + + ' drop count _gethex ( Get number ) +xboot_3 word @drop_X+Q, @count_X+Q, @_gethex_X+Q + + ' state @ _jz _xboot1 ( Loop again if not compile mode ) + word @state_X+Q, @fetch_X+Q, @_jz_X+Q, @xboot_1+Q + + ' ['] _lit , , _jmp _xboot1 ( Otherwise, compile number and loop again ) + word @_lit_X+Q, @_lit_X+Q, @compcomma_X+Q, @compcomma_X+Q, @_jmp_X+Q, @xboot_1+Q + + ' drop refill _jmp _xboot1 ( Refill and loop again ) +xboot_2 word @drop_X+Q, @refill_X+Q, @_lit_X+Q, 13, @emit_X+Q, @_jmp_X+Q, @xboot_1+Q, 0 + long + +switch_L word @xboot_L+Q + byte FLAG_DEF, 6, "switch" + long +switch_X word execlistfunc, 0 + word @_lit_X+Q, @getchar_X+Q, @_lit_X+Q, @key_B+Q, @store_X+Q, 0 + long + +_last long + +_loop_L word @switch_L+Q + byte FLAG_CORE, 5, "_loop" + long +_loop_X word _loopfunc, 0 + +_here long + +'******************************************************************************* +' The Forth source files follow below. They will be compiled into the +' dictionary, which will over-write the source data. Some padding space is +' included to ensure that we don't over-write the source data before it is +' compiled. +'******************************************************************************* + long 0[100] +infile file "init.fth" + file "comus.fth" + 'file "see.fth" + file "propwords.fth" + file "sd.fth" + 'file "sdutils.fth" + file "linux.fth" + 'file "cd.fth" + 'file "ted.fth" + 'file "bufser.fth" + 'file "i2c.fth" + 'file "fds.fth" + 'file "time.fth" + 'file "toggle.fth" + 'file "primes.fth" + 'file "chess.fth" + +'******************************************************************************* +' Enable serial output, print version string and switch to serial input +'******************************************************************************* + byte 13 + byte " 1 verbose !" + byte " pfthversion type cr" + byte " switch", 13 + + +{ ++-------------------------------------------------------------------- +| TERMS OF USE: MIT License ++-------------------------------------------------------------------- +Permission is hereby granted, free of charge, to any person obtaining +a copy of this software and associated documentation files +(the "Software"), to deal in the Software without restriction, +including without limitation the rights to use, copy, modify, merge, +publish, distribute, sublicense, and/or sell copies of the Software, +and to permit persons to whom the Software is furnished to do so, +subject to the following conditions: + +The above copyright notice and this permission notice shall be +included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. ++------------------------------------------------------------------ +} \ No newline at end of file diff --git a/pfth/sdutils.fth b/pfth/sdutils.fth new file mode 100644 index 0000000..a238cb5 --- /dev/null +++ b/pfth/sdutils.fth @@ -0,0 +1,58 @@ +: print_allocated_clusters + spi_fat1 @ spi_sectorsperfat @ 0 ?do + dup spi_buf spi_readblock drop + 1+ + 512 0 do + spi_filesystem @ 2 = + if + spi_buf i + @ dup + if + j 128 * i 4 / + . . cr + else + drop + then + 4 + else + spi_buf i + w@ dup + if + j 256 * i 2 / + . . cr + else + drop + then + 2 + then + +loop + loop + drop +; + +: create_file + 32 word count 0 create-file + swap . . cr +; + +: open_file + 32 word count 0 open-file + swap . . cr +; + +: _dump_file_info + ." file_sector0 " file_sector0 ? cr + ." file_sector " file_sector ? cr + ." file_length " file_length ? cr + ." file_position " file_position ? cr + ." file_remaining " file_remaining ? cr + ." file_dsector " file_dsector ? cr + ." file_doffset " file_doffset ? cr + ." file_dirty " file_dirty ? cr + ." file_opened " file_opened ? cr +; + +: dump_file_info + file_info_1 to file_info + ." file_info_1" cr + _dump_file_info + file_info_2 to file_info + ." file_info_2" cr + _dump_file_info +; diff --git a/pfth/see.fth b/pfth/see.fth new file mode 100644 index 0000000..ff9fbac --- /dev/null +++ b/pfth/see.fth @@ -0,0 +1,65 @@ +: cond.name ( link ) + dup link>flags c@ dup 2 and ( link flag literal ) + if + 8 and + if + .name + then + else + 4 and + if + [char] s emit [char] " emit bl emit + else + .name + then + then +; + +: seefunc ( xt ) + >body ( listptr ) + begin + dup w@ ( listptr xt ) + while + dup w@ ( listptr xt ) + >link .name link>flags c@ dup 2 and ( listptr flags literal ) + if + 8 and ( listptr flags jump ) + if + 2 + dup dup w@ swap - 2 - 2 / . + else + 2 + dup w@ . + then + else + 4 and ( listptr string ) + if + 2 + dup count type [char] " emit space + dup c@ + 0 2 - and + then + then + compsize + ( listptr+=compsize) + repeat + drop +; + +: see + ' dup ( xt xt ) + if + dup >flags c@ dup 16 and ( xt flags kernel ) + if + drop + drop + ." Kernel Word" + else + 32 and + if + drop + ." Variable" + else + seefunc + then + then + else + drop + ." ?" + then +; diff --git a/pfth/serial.fth b/pfth/serial.fth new file mode 100644 index 0000000..a39b145 --- /dev/null +++ b/pfth/serial.fth @@ -0,0 +1,20 @@ +: waitcnt begin dup cnt@ - 0< until ; + +: putch 256 or dup + clkfreq@ 9600 / + 11 >r + cnt@ + begin + r> 1- dup >r + while + rot dup + 30 lshift + outa! + 1 rshift + swap rot dup rot + + waitcnt + repeat + r> 2drop 2drop +; + +: test cnt@ 65 putch cnt@ swap - 80 / . ; + diff --git a/pfth/starting.fth b/pfth/starting.fth new file mode 100644 index 0000000..d2cc9c3 --- /dev/null +++ b/pfth/starting.fth @@ -0,0 +1,65 @@ +: STAR 42 EMIT ; +: MARGIN CR 30 SPACES ; +: BLIP MARGIN STAR ; +: STARS 0 DO STAR LOOP ; +: BAR MARGIN 5 STARS ; +: F BAR BLIP BAR BLIP BLIP CR ; +: MULT CR 11 1 DO DUP I * . LOOP DROP ; +: TABLE CR 11 1 DO I MULT LOOP ; +: TABLE1 CR 11 1 DO 11 1 DO I J * . LOOP CR LOOP ; +: DUB 32767 1 DO I . I +LOOP ; +: GREET ." Hello, I speak Forth " ; +: GIFT ." chocolate" ; +: GIVER ." Mum" ; +: THANKS CR ." Dear " GIVER ." ," + CR ." Thanks for the " GIFT ." . " ; +: EGGSIZE DUP 18 < IF ." reject " ELSE + DUP 21 < IF ." small " ELSE + DUP 24 < IF ." medium " ELSE + DUP 27 < IF ." large " ELSE + DUP 30 < IF ." extra large " ELSE + ." error " + THEN THEN THEN THEN THEN DROP ; +: FALSE 0 ; +: TRUE -1 ; +: TEST IF ." non-" THEN ." zero " ; +: /CHECK ?DUP IF / THEN ; +: UNCOUNT DROP 1 - ; +: max-int -1 1 rshift ; +: min-int max-int negate 1 - ; +: max-uint -1 ; +: OUTPUT-TEST + ." YOU SHOULD SEE THE STANDARD GRAPHIC CHARACTERS:" CR + 41 BL DO I EMIT LOOP CR + 61 41 DO I EMIT LOOP CR + 127 61 DO I EMIT LOOP CR + ." YOU SHOULD SEE 0-9 SEPARATED BY A SPACE:" CR + 9 1+ 0 DO I . LOOP CR + ." YOU SHOULD SEE 0-9 (WITH NO SPACES):" CR + 57 1+ 48 DO I 0 SPACES EMIT LOOP CR + ." YOU SHOULD SEE A-G SEPARATED BY A SPACE:" CR + 71 1+ 65 DO I EMIT SPACE LOOP CR + ." YOU SHOULD SEE 0-5 SEPARATED BY TWO SPACES:" CR + 5 1+ 0 DO I 48 + EMIT 2 SPACES LOOP CR + ." YOU SHOULD SEE TWO SEPARATE LINES:" CR + ." LINE 1" CR ." LINE 2" CR + ." YOU SHOULD SEE THE NUMBER RANGES OF SIGNED AND UNSIGNED NUMBERS:" CR + ." SIGNED: " MIN-INT . MAX-INT . CR + ." UNSIGNED: " 0 . MAX-UINT U. CR + ; + +output-test +f +10 mult +table +table1 +dub +greet +thanks cr +17 eggsize cr +22 eggsize cr +25 eggsize cr +28 eggsize cr +32 eggsize cr +0 test cr +1 test cr diff --git a/pfth/sysdefs.spin b/pfth/sysdefs.spin new file mode 100644 index 0000000..d90920a --- /dev/null +++ b/pfth/sysdefs.spin @@ -0,0 +1,112 @@ +'****************************************************************************** +' Copyright (c) 2011, 2012, Dave Hein +' See end of file for terms of use. +'****************************************************************************** +CON + 'start = $7e50 ' Extra space for the stand-alone loader + start = $7c00 ' Extra space for the stand-alone loader + + ' SD CLIB data + rendezvous = $7e50 + + ' Exported variables + environ_vars = $7e50 + environ_vars_end = $7ed3 + + ' Argv parameter area + argv_parms = $7ed4 + + ' Additional system parameters + return_value = $7f94 + vga_cog = $7f98 + vga_handle = $7f9c + sd_pins = $7fa0 + config = $7fa4 + focus = $7fa8 + + ' System time + unixtime = $7fac + cycle0 = $7fb0 + timezone = $7fb4 +{ + ' I2C Driver + i2c_cog = $7fb8 + i2c_cmd = $7fbc + i2c_parm = $7fc0 + + ' Kernel data + filelock = $7fc4 + filecmd = $7fc8 + fileparm = $7fcc +} + 'Shell variables + scriptline = $7fb8 + ifflag = $7fbc + whileflag = $7fc0 + skipflag = $7fc4 + shell_level = $7fc8 + bootflag = $7fcc + + 'File I/O + spi_engine_cog = $7fd0 + spi_command = $7fd4 + spi_block_index = $7fd8 + spi_buffer_address = $7fdc + + ' Basic CLIB data + ' Serial data + serial = $7fe0 + stdio = $7fe4 + stdin = $7fe8 + stdout = $7fec + + ' Malloc data + memlocknum = $7ff0 + memfreelist = $7ff4 + malloclist = $7ff8 + laststackaddr = $7ffc + + ' Stack check word + checkword = $dead1eaf + + ' Process types + proc_type_spin = 1 + proc_type_pasm = 2 + proc_type_capp = 3 + proc_type_driver = $80 + + ' Run modes + run_shell_wait = $00 + run_shell_nowait = $08 + run_kill_caller = $10 + run_at_address0 = $20 + run_c_program = $40 + run_spin_program = $80 + run_stand_alone = $100 + +PUB main + return + +{{ ++-----------------------------------------------------------------------------+ +| TERMS OF USE: MIT License | ++-----------------------------------------------------------------------------+ +|Permission is hereby granted, free of charge, to any person obtaining a copy | +|of this software and associated documentation files (the "Software"), to deal| +|in the Software without restriction, including without limitation the rights | +|to use, copy, modify, merge, publish, distribute, sublicense, and/or sell | +|copies of the Software, and to permit persons to whom the Software is | +|furnished to do so, subject to the following conditions: | +| | +|The above copyright notice and this permission notice shall be included in | +|all copies or substantial portions of the Software. | +| | +|THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | +|IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | +|FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE | +|AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | +|LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,| +|OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE| +|SOFTWARE. | ++-----------------------------------------------------------------------------+ +}} diff --git a/pfth/ted.fth b/pfth/ted.fth new file mode 100644 index 0000000..9b287a2 --- /dev/null +++ b/pfth/ted.fth @@ -0,0 +1,278 @@ +create ted_linenum 0 , +create ted_first 0 , +create ted_last 0 , +create ted_numline 0 , +create ted_cmd 0 , +create ted_changed 0 , +create ted_numchar 0 , +create ted_bigptr 0 , +create ted_fileid 0 , +0 value ted_instr +0 value ted_lineptr + +\ Check if a decimal digit +: ted_IsDigit ( value -- digit ) + dup [char] 0 >= swap [char] 9 <= and +; + +\ Get a decimal number +: ted_GetNumber ( str -- str number ) + 0 + begin + over c@ dup ted_IsDigit + while + [char] 0 - swap 10 * + + swap 1+ swap + repeat + drop +; + +\ Get a line number +: ted_GetLineNumber ( str -- str number ) + dup c@ ( str byte[str] ) + dup [char] . = if drop 1+ ted_linenum @ else + dup [char] - = if drop 1+ ted_linenum @ 1- else + dup [char] + = if drop 1+ ted_linenum @ 1+ else + dup [char] $ = if drop 1+ ted_numline @ 1- else + ted_IsDigit if ted_GetNumber 1- else + -2 + then then then then then + ( num str value ) + \ ( value < 0 | value >= ted_numline ) & value <> -2 + dup 0 < + over ted_numline @ >= or + over -2 <> and + if drop -3 then +; + +\ Get the first and last line numbers and the command +: ted_ParseCommand ( str -- str retval ) + ted_GetLineNumber dup ted_first ! -3 = if 0 exit then + ted_first @ -2 = if ted_linenum @ ted_first ! then + dup c@ [char] , = + if + 1+ + ted_GetLineNumber dup ted_last ! -3 = if 0 exit then + else + -2 ted_last ! + then + ted_last @ -2 = if ted_first @ ted_last ! then + dup c@ ted_cmd ! + ted_first @ ted_last @ > if 0 else 1 then +; + +\ Print the help information +: ted_help + ." TED Commands" cr + ." q - quit" cr + ." w - write" cr + ." d - delete line" cr + ." i - insert lines" cr + ." a - append lines" cr + ." p - print lines" cr + ." j - join line (not implemented)" cr + ." # - move to line #" cr + ." . - current line" cr + ." $ - move to last line" cr + ." - - move down one line" cr + ." + - move up one line" cr + ." h - print help information" cr +; + +: ted_InsertLine ( str linenum ) + ted_numline @ + begin + 2dup < ( ted_linenum < ted_numline ) + while + dup 1- 2* ted_lineptr + w@ + over 2* ted_lineptr + w! + 1- + repeat + drop 2* ted_lineptr + w! + ted_numline @ 1+ ted_numline ! +; + +: ted_DeleteLine ( linenum ) + ted_numline @ swap + begin + 2dup > + while + dup 1+ 2* ted_lineptr + w@ + over 2* ted_lineptr + w! + 1+ + repeat + drop drop + ted_numline @ 1- ted_numline ! +; + +: ted_ReadLines + begin + ted_bigptr @ 1+ dup 79 accept + dup 1 = rot c@ [char] . = and 0= + while + ted_bigptr @ c! + ted_bigptr @ + ted_linenum @ ted_InsertLine + ted_bigptr @ dup c@ + 1+ ted_bigptr ! + ted_linenum @ 1+ ted_linenum ! + repeat + drop + ted_linenum @ 1- ted_linenum ! + 1 ted_changed ! +; + +: ted_readfile + bl word + dup count 0 open-file + 0 ted_linenum ! + if + drop + count 0 create-file drop + ted_fileid ! + else + swap drop + ted_fileid ! + begin + ted_bigptr @ 1+ 100 ted_fileid @ read-line drop + 0 = if drop ted_linenum @ 1- ted_linenum ! exit then + ted_bigptr @ c! + ted_bigptr @ ted_linenum @ ted_InsertLine + ted_bigptr @ c@ ted_numchar @ + ted_numchar ! + ted_bigptr @ dup c@ + 1+ ted_bigptr ! + ted_linenum @ 1+ ted_linenum ! + again + then +; + +: ted_error ." ?" cr ; + +: ted_writefile + 0 ted_fileid @ reposition-file drop + 0 + ted_numline @ 0 ?do + i 2* ted_lineptr + w@ + dup count ted_fileid @ write-line drop + c@ + 1+ + loop + ted_fileid @ flush-file drop + . cr +; + +: ted + 0 ted_numchar ! + 0 ted_numline ! + here dup to ted_instr + 80 + dup to ted_lineptr + 800 + ted_bigptr ! + ted_readfile + ted_numline @ 1 - ted_linenum ! + ted_numchar @ . cr + begin + ted_instr 79 accept + ted_instr + 0 swap c! + ted_instr ted_ParseCommand swap drop +\ ted_first ? ted_last ? ted_cmd ? cr + + ted_cmd @ swap 0 = + if + ted_error + else + + \ No command + dup 0 = + if + ted_last @ dup 0 < + if + drop + ted_error + else + dup ted_linenum ! dup 1+ . + 2* ted_lineptr + w@ count type cr + then + else + + \ Quit + dup [char] q = + if + ted_changed @ + if + ted_error + 0 ted_changed ! + else + drop + ted_fileid @ close-file drop + quit + then + else + + \ Help + dup [char] h = + if + ted_help + else + + dup [char] w = + if + ted_writefile + 0 ted_changed ! + else + + \ Delete Lines + dup [char] d = + if + ted_first @ 0 < + if + ted_error + else + ted_first @ 1- ted_linenum ! + ted_last @ 1+ ted_first @ ?do + ted_first @ ted_DeleteLine + loop + 1 ted_changed ! + then + ted_first @ ted_numline @ 1- min ted_linenum ! + else + + \ Insert Lines + dup [char] i = + if + ted_first @ 0 max ted_linenum ! + ted_ReadLines + else + + \ Append Lines + dup [char] a = + if + ted_first @ 1+ ted_linenum ! + ted_ReadLines + else + + \ Print Lines + dup [char] p = + if + ted_first @ 0 < + if + ted_error + else + ted_last @ 1+ ted_first @ do + i dup 1+ . + 2* ted_lineptr + w@ count type cr + loop + then + ted_last @ ted_linenum ! + else + + \ Join Lines + dup [char] j = + if + ." Not implemented" cr + + \ Invalid Command + else + ted_error + + then then then then then then then then then then + drop + again +; + diff --git a/pfth/ted.txt b/pfth/ted.txt new file mode 100644 index 0000000..60093a2 --- /dev/null +++ b/pfth/ted.txt @@ -0,0 +1,46 @@ +TED(1) User Commands TED(1) + +NAME + ted - tiny text editor + +SYNOPSIS + ted - [FILE] + +DESCRIPTION + Edits the file named FILE. ted is a subset of the ed text editor It + uses single letter commands that may be prefixed by a line number or a + range of line numbers. The "." character represents the current line, + and the "$" character is the last line number. A range is specifed by + two line numbers seperated by a comma, such as "1,10" or ".,$". + + The commands are: + q - quit + w - write edit buffer to FILE + d - delete the current line + i - insert lines after the current line + a - append lines before the current line + p - print lines + n - print lines with line number + j - join the current line with the next line + # - move to line # + . - current line + $ - move to last line + - - move down one line + + - move up one line + h - print help information + +LIMITATIONS + A file name must be specified when starting ted or it will write a + blank file name when using the "w" command. + +AUTHOR + Dave Hein + +COPYRIGHT + Copyright (c) 2011, 2012, Dave Hein + MIT License (See license.txt in the root directory) + This is free software: you are free to change and redistribute it. + There is no warranty, to the extent permitted by law. + + +SPINIX utility March 2012 TED(1) diff --git a/pfth/time.fth b/pfth/time.fth new file mode 100644 index 0000000..ac3f498 --- /dev/null +++ b/pfth/time.fth @@ -0,0 +1,2 @@ +: TIME CNT@ 100000 BEGIN 1 - DUP 0 = UNTIL DROP CNT@ SWAP - 8000 / . ; + diff --git a/pfth/toggle.fth b/pfth/toggle.fth new file mode 100644 index 0000000..ceccbf5 --- /dev/null +++ b/pfth/toggle.fth @@ -0,0 +1,33 @@ +\ ############################################################################ +\ # toggle.fth - This program starts up a Forth cog that toggles P15 +\ # +\ # Copyright (c) 2012 Dave Hein +\ # MIT Licensed +\ ############################################################################ + +create cogstack 80 allot \ Allocate data stack space +create cogreturn 80 allot \ Allocate return stack space +create delaycnt 80000000 , \ This variable controls the blink rate + +hex +\ This word toggles bit P15 every "delaycnt" cycles +: toggle + 8000 dirasetbit cnt@ \ Set P15 for output and get CNT + begin + delaycnt @ + waitcnt \ Wait "delaycnt" cycles + 8000 outasetbit \ Set P15 + delaycnt @ + waitcnt \ Wait "delaycnt" cycles + 8000 outaclrbit \ Clear P15 + again ; \ Repeat forever +decimal + +create cogconfig \ Forth cog config structure + ' toggle >body , \ Get execution token for TOGGLE + cogstack , \ Initial value of stack ptr + cogstack , \ Empty value for stack ptr + cogreturn , \ Initial value of return ptr + cogreturn , \ Empty value for return ptr + +\ This word starts a cog running the TOGGLE word +: starttoggle forth @ cogconfig cognew ; + diff --git a/readme.txt b/readme.txt new file mode 100644 index 0000000..a878d5b --- /dev/null +++ b/readme.txt @@ -0,0 +1,23 @@ +Spinix is an operating system that provides a linux-like look and feel +to a propeller based board with an SD card. It takes advantage of the +relocatable nature of Spin programs to run multiple programs at the +same time. Each program runs on a dedicated propeller cog. + +You can install Spinix by writing the boot.binary file to the EEPROM +and copying the files in the root directory to an SD card. + +When Spinix boots up for the first time it will request the four pin +numbers of the pins that interface to the SD card. For a C3 card, +use the pin numbers 10, 11, 9 and 25. + +Spinix can be rebuilt by running the build_spinix script. It uses +BSTC to build Spin programs. + +The subdirectories in the file system contain the following programs: + +bin - Spinix utilities +demos - C and Spin demo programs +devel - Sample Spin code that can be built under spinix +manpages - Documentation files displayed by man +tmp - Temporary file storage +scripts - Sample shell script files diff --git a/scripts/fact.sh b/scripts/fact.sh new file mode 100644 index 0000000..0f05805 --- /dev/null +++ b/scripts/fact.sh @@ -0,0 +1,22 @@ +#shell +#This script computes the factorial +if [ $# -ne 1 ] +then +echo usage: fact.sh number +exit +fi +if [ -z $factorial ] +then +export factorial=$1 +else +let factorial=factorial*$1 +fi +echo . +if [ $1 -le 1 ] +then +echo $factorial +unset factorial +else +let a=$1-1 +./fact.sh $a +fi diff --git a/scripts/test1.sh b/scripts/test1.sh new file mode 100644 index 0000000..063190c --- /dev/null +++ b/scripts/test1.sh @@ -0,0 +1,8 @@ +#shell +#This script loops from 1 to 10 +a=1 +while [ $a -le 10 ] +do +echo $a +let a=a+1 +done diff --git a/scripts/test2.sh b/scripts/test2.sh new file mode 100644 index 0000000..03ca7d7 --- /dev/null +++ b/scripts/test2.sh @@ -0,0 +1,13 @@ +#shell +#This script counts to 5 +if [ $# -ne 1 ] +then +echo usage: test2.sh number +exit +fi +echo $1 +if [ $1 -lt 5 ] +then +let a=$1+1 +./test2.sh $a +fi diff --git a/src/FIBO.SH b/src/FIBO.SH new file mode 100644 index 0000000..222895d --- /dev/null +++ b/src/FIBO.SH @@ -0,0 +1,27 @@ +#shell +#echo first num = $num +if [ $# -ne 1 ] +then +echo $# +echo usage: fibo num +exit +fi +if [ -z $num ] +then +export num=$1 +export total=1 +#echo num = $num, total = $total +fibo.sh 1 +exit +fi +if [ $1 -ge $num ] +then +#echo $1 -ge $num +unset num +unset total +exit +fi +echo $total +let total=total+$1 +let a=$1+1 +fibo.sh $a diff --git a/src/FileDriver.c b/src/FileDriver.c new file mode 100644 index 0000000..f595358 --- /dev/null +++ b/src/FileDriver.c @@ -0,0 +1,363 @@ +/* +# ######################################################### +# This file contains the standard file I/O driver that is +# uses the DOSFS FAT file I/O routines. +# +# Written by Dave Hein with contributions from other GCC +# development team members. +# Copyright (c) 2011 Parallax, Inc. +# MIT Licensed +# ######################################################### +*/ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "dosfs.h" + +#ifndef __PROPELLER_USE_XMM__ +#define FILE_BUFFER_SIZE 16 // Use a small file buffer for CMM/LMM +#else +#define FILE_BUFFER_SIZE 512 // Use a 512-byte file buffer for XMM/XMMC +#endif + +// Global variables +VOLINFO dfs_volinfo; +int dfs_mountflag = 0; +char dfs_currdir[MAX_PATH]; +__attribute__((section(".hub"))) uint8_t dfs_scratch[512]; + +// Function prototypes +extern void LoadSDDriver(uint32_t configwords[2]); +int DFS_InitFileIO(void); +int dfs_stdio_errno(int errnum); +void dfs_resolve_path(const char *fname, char *path); +uint32_t mount_complete(void); + +// Define the the file prefix +static const char File_prefix[] = ""; + +// function called by fopen +static int File_fopen(FILE *fp, const char *fname1, const char *mode) +{ + int retval; + PFILEINFO fileinfo; + char fname[MAX_PATH]; + uint8_t dfs_mode; + + if (!dfs_mountflag && dfs_mount_defaults() != DFS_OK) + { + errno = EIO; + return -1; + } + + dfs_resolve_path((char *)fname1, fname); + + fileinfo = malloc(sizeof(FILEINFO) + FILE_BUFFER_SIZE); + + if (!fileinfo) + { + errno = ENOMEM; + return -1; + } + + if (mode[1] == '+') + dfs_mode = DFS_READ | DFS_WRITE; + else if (mode[0] == 'r') + dfs_mode = DFS_READ; + else + dfs_mode = DFS_WRITE; + + if (mode[0] == 'w') + { + retval = DFS_OpenFile(&dfs_volinfo, (uint8_t *)fname, DFS_READ, dfs_scratch, fileinfo); + if (retval == DFS_OK) + DFS_UnlinkFile(&dfs_volinfo, (uint8_t *)fname, dfs_scratch); + retval = DFS_OpenFile(&dfs_volinfo, (uint8_t *)fname, dfs_mode, dfs_scratch, fileinfo); + } + else if (mode[0] == 'a') + { + retval = DFS_OpenFile(&dfs_volinfo, (uint8_t *)fname, dfs_mode, dfs_scratch, fileinfo); + } + else if (mode[0] == 'r') + { + retval = DFS_OpenFile(&dfs_volinfo, (uint8_t *)fname, DFS_READ, dfs_scratch, fileinfo); + if (retval == DFS_OK && (dfs_mode & DFS_WRITE)) + retval = DFS_OpenFile(&dfs_volinfo, (uint8_t *)fname, dfs_mode, dfs_scratch, fileinfo); + } + else + { + errno = EINVAL; + free(fileinfo); + return -1; + } + + if (retval != DFS_OK) + { + errno = dfs_stdio_errno(retval); + free(fileinfo); + return -1; + } + + fp->drvarg[0] = (int)fileinfo; + + // Set up file buffer in the FILE struct + fp->_ptr = fp->_base = ((void *)fileinfo) + sizeof(FILEINFO); + fp->_bsiz = FILE_BUFFER_SIZE; + fp->_flag |= _IOFREEBUF; + + return 0; +} + +// function called by fclose +static int File_fclose(FILE *fp) +{ + PFILEINFO dfs_fp = (PFILEINFO)fp->drvarg[0]; + free(dfs_fp); + return 0; +} + +// function called by fread +static int File_read(FILE *fp, unsigned char *buf, int count) +{ + int retval; + uint32_t successcount; + PFILEINFO fileinfo = (PFILEINFO)fp->drvarg[0]; + + retval = DFS_ReadFile(fileinfo, dfs_scratch, buf, &successcount, count); + if (retval != DFS_OK) + { + successcount = 0; + errno = dfs_stdio_errno(retval); + } + return successcount; +} + +// function called by fwrite +static int File_write(FILE *fp, unsigned char *buf, int count) +{ + int retval; + uint32_t successcount; + PFILEINFO fileinfo = (PFILEINFO)fp->drvarg[0]; + + retval = DFS_WriteFile(fileinfo, dfs_scratch, buf, &successcount, count); + if (retval != DFS_OK) + { + successcount = 0; + errno = dfs_stdio_errno(retval); + } + return successcount; +} + +// function called by fseek +static int File_fseek(FILE *fp, long int offset, int origin) +{ + // This code needs to be enabled and tested + PFILEINFO dfs_fp = (PFILEINFO)fp->drvarg[0]; + if (origin == SEEK_CUR) + offset += dfs_fp->pointer; + else if (origin == SEEK_END) + offset += dfs_fp->filelen; + else if (origin != SEEK_SET) + { + errno = EIO; + return -1; + } + if (offset < 0) + offset = 0; + DFS_Seek(dfs_fp, offset, dfs_scratch); + return 0; +} + +// function called by remove +static int File_remove(const char *fname) +{ + int errnum; + int retval = -1; + //DIRINFO dirinfo; + char path[MAX_PATH]; + + dfs_resolve_path(fname, path); + + if (!(errnum = DFS_UnlinkFile(&dfs_volinfo, (uint8_t *)path, dfs_scratch))) + retval = 0; + // OpenDir seems to be causing a problem, so it's commented out for now + //else if (!DFS_OpenDir(&dfs_volinfo, (uint8_t *)path, &dirinfo)) + // errno = EISDIR; + else + errno = dfs_stdio_errno(errnum); + + return retval; +} + +// Define the driver list +_Driver _FileDriver = +{ + File_prefix, + File_fopen, + File_fclose, + File_read, + File_write, + File_fseek, + File_remove, +}; + +// Convert the dosfs errno to the stdio errno +int dfs_stdio_errno(int errnum) +{ + switch(errnum) + { + case DFS_OK: + break; + case DFS_EOF: + errnum = EOK; + break; + case DFS_WRITEPROT: + errnum = EROFS; + break; + case DFS_NOTFOUND: + errnum = ENOENT; + break; + case DFS_PATHLEN: + errnum = ENAMETOOLONG; + break; + default: + errnum = EIO; + } + return errnum; +} + +/* these variables are patched by the loader */ +int _cfg_sdspi_config1 = -1; +int _cfg_sdspi_config2 = -1; + +uint32_t dfs_mount_defaults(void) +{ + uint32_t configwords[2] = { _cfg_sdspi_config1, _cfg_sdspi_config2 }; + LoadSDDriver(configwords); + return mount_complete(); +} + +uint32_t mount_complete(void) +{ + int retval, sector; + + strcpy(dfs_currdir, "/"); + + // Start up the low-level file I/O driver + if ((retval = DFS_InitFileIO()) != DFS_OK) + return retval; + + // Find the first sector of the volume + if ((retval = DFS_ReadSector(0, dfs_scratch, 0, 1)) != DFS_OK) + return retval; + if (!strncmp((char *)dfs_scratch+0x36, "FAT16", 5) || + !strncmp((char *)dfs_scratch+0x52, "FAT32", 5)) + sector = 0; + else + memcpy(§or, &dfs_scratch[0x1c6], 4); + + // Get the volume information + retval = DFS_GetVolInfo(0, dfs_scratch, sector, &dfs_volinfo); + dfs_mountflag = (retval == DFS_OK); + + return retval; +} + +static void movestr(char *ptr1, char *ptr2) +{ + while (*ptr2) *ptr1++ = *ptr2++; + *ptr1 = 0; +} + +void dfs_resolve_path(const char *fname, char *path) +{ + char *ptr; + char *ptr1; + + if (!strcmp(fname, ".")) fname++; + else if (!strncmp(fname, "./",2) || !strncmp(fname, ".\\",2)) fname += 2; + + if (fname[0] == '/' || fname[0] == '\\') + strcpy(path, fname); + else if (fname[0] == 0) + strcpy(path, dfs_currdir); + else + { + strcpy(path, dfs_currdir); + int pathLen = strlen(path); + if (path[pathLen-1] != '/' && path[pathLen-1] != '\\') strcat(path, "/"); + strcat(path, fname); + } + + // Whack DOS paths to UNIX paths + for (ptr = path; *ptr; ptr++) + if (*ptr == '\\') + *ptr = '/'; + + // Process .. + ptr = path; + while (*ptr) + { + if (!strncmp(ptr, "/..", 3) && (ptr[3] == 0 || ptr[3] == '/')) + { + if (ptr == path) + { + movestr(ptr, ptr+3); + } + else + { + ptr1 = ptr - 1; + while (ptr1 != path) + { + if (*ptr1 == '/') break; + ptr1--; + } + movestr(ptr1, ptr+3); + ptr = ptr1; + } + } + else + { + ptr++; + while (*ptr) + { + if (*ptr == '/') break; + ptr++; + } + } + } + + if (path[0] == 0) strcpy(path, "/"); +} + +/* ++-------------------------------------------------------------------- +| TERMS OF USE: MIT License ++-------------------------------------------------------------------- +Permission is hereby granted, free of charge, to any person obtaining +a copy of this software and associated documentation files +(the "Software"), to deal in the Software without restriction, +including without limitation the rights to use, copy, modify, merge, +publish, distribute, sublicense, and/or sell copies of the Software, +and to permit persons to whom the Software is furnished to do so, +subject to the following conditions: + +The above copyright notice and this permission notice shall be +included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. ++------------------------------------------------------------------ +*/ diff --git a/src/_shellrc b/src/_shellrc new file mode 100644 index 0000000..4b63305 --- /dev/null +++ b/src/_shellrc @@ -0,0 +1,3 @@ +#shell +alias ll="ls -l" +config 3012 diff --git a/src/_sysparm b/src/_sysparm new file mode 100644 index 0000000..763a49d --- /dev/null +++ b/src/_sysparm @@ -0,0 +1,6 @@ +1384486575 +-6 3 0 +0 0 0 +GPWD / +LSCRIPT_FILE /bin/_shellrc +P# 0 diff --git a/src/alias.spin b/src/alias.spin new file mode 100644 index 0000000..14f5713 --- /dev/null +++ b/src/alias.spin @@ -0,0 +1,104 @@ +'****************************************************************************** +' Copyright (c) 2013 Dave Hein +' See end of file for terms of use. +'****************************************************************************** +CON + _clkmode = xinput + _xinfreq = $4e495053 ' SPIN + + DOUBLE_QUOTE = $22 + +OBJ + c : "clibsd" + sys : "sysdefs" + vs : "varsubs" + +PUB main(argc, argv) | argi + c.enter + + ' Print if no parms + if argc < 2 + PrintAliases + c.exit(0) + + repeat argi from 1 to argc - 1 + ProcessParm(long[argv][argi]) + + c.exit(0) + +PUB ProcessParm(name) | ptr, len1, len2, space, value + + ' Check for "=" and get value + value := vs.FindChar(name, "=") + ifnot byte[value] + return + byte[value++] := 0 + + ' Check if variable already exists + if (ptr := vs.FindAlias(name)) + ptr := vs.RemoveEntry(ptr) + else + ptr := vs.FindEnd + + ' Check if space is available + space := sys#environ_vars_end - ptr + 1 + len1 := strsize(name) + len2 := strsize(value) + if space - len1 - len2 - 4 < 50 + c.printf2(string("space = %d, need = %d\n"), space, len1 + len2 + 4) + if (space < len1 + len2 + 4) + c.printf0(string("Not enough variable space\n")) + c.exit(1) + + ' Add alias + byte[ptr++] := VS#ALIAS_FLAG + c.strcpy(ptr, name) + ptr += len1 + 1 + c.strcpy(ptr, value) + ptr += len2 + 1 + byte[ptr] := 0 + +{ +PUB usage + c.printf0(string("usage: alias [var[=[value]]...]\n")) + c.exit(1) +} + +PUB PrintAliases | ptr + ptr := sys#environ_vars + repeat while byte[ptr] + ptr := PrintAlias(ptr) + +PUB PrintAlias(ptr) | scope, name, value + scope := byte[ptr++] + name := ptr + ptr += strsize(ptr) + 1 + value := ptr + ptr += strsize(ptr) + 1 + if scope == "A" + c.printf2(string("alias %s=", DOUBLE_QUOTE, "%s", DOUBLE_QUOTE, "\n"), name, value) + return ptr + +{{ ++-----------------------------------------------------------------------------+ +| TERMS OF USE: MIT License | ++-----------------------------------------------------------------------------+ +|Permission is hereby granted, free of charge, to any person obtaining a copy | +|of this software and associated documentation files (the "Software"), to deal| +|in the Software without restriction, including without limitation the rights | +|to use, copy, modify, merge, publish, distribute, sublicense, and/or sell | +|copies of the Software, and to permit persons to whom the Software is | +|furnished to do so, subject to the following conditions: | +| | +|The above copyright notice and this permission notice shall be included in | +|all copies or substantial portions of the Software. | +| | +|THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | +|IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | +|FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE | +|AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | +|LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,| +|OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE| +|SOFTWARE. | ++-----------------------------------------------------------------------------+ +}} diff --git a/src/boot.spin b/src/boot.spin new file mode 100644 index 0000000..a188abf --- /dev/null +++ b/src/boot.spin @@ -0,0 +1,170 @@ +CON + _clkmode = xtal1 + pll16x + _xinfreq = 5_000_000 + + CYCLE0 = sys#cycle0 + SECONDS = sys#unixtime + TIMEZONE = sys#timezone + + eeprom_pin = 28 + +OBJ + c : "clibsd" + sys : "sysdefs" + ut : "unixtime" + vs : "varsubs" + spi : "safe_spi_p" + ser : "cserial" + fsrwx : "fsrwx" + +VAR + byte buffer[100] + +DAT + pins byte 0[4] + sd_names byte "DO ", 0, "CLK", 0, "DI ", 0, "CS ", 0 + +PUB main | i, ptr, infile, handle + longfill(sys#rendezvous, 0, ($8000 - sys#rendezvous)/4) + c.start + ifnot pins + repeat + c.printf0(string("Press any key to start\n")) + if ser.rxtime(1000) <> -1 + quit + ptr := @sd_names + repeat i from 0 to 3 + c.printf1(string("Enter SD %s pin number: "), ptr) + c.scanf1(string("%d"), @result) + pins[i] := result + ptr += 4 + WriteEEPromLong(eeprom_pin, @pins) + spi.start_explicit(pins, pins[1], pins[2], pins[3]) + c.mount_explicit(pins, pins[1], pins[2], pins[3]) + 'c.chdir(@long[ptr][2]) + load_sysparm + 'vs.SaveVar(string("#"), string("0"), vs#PARM_FLAG) + 'vs.SaveVar(string("SCRIPT_FILE"), string("/bin/_shellrc"), vs#LOCAL_FLAG) + 'vs.SaveVar(string("PWD"), string("/"), vs#GLOBAL_FLAG) + splash + infile := c.fopen(string("/bin/shell"), string("r")) + ifnot infile + c.printf0(string("Could not open /bin/shell\n")) + abort + handle := long[infile][c#stream_handle] + long[sys#shell_sector] := fsrwx.hget_first_sector(handle) + long[sys#shell_size] := fsrwx.hget_filesize(handle) + 'c.printf2(string("sector = %d, size = %d\n"), long[sys#shell_sector], long[sys#shell_size]) + c.exit(0) + +pub load_sysparm | infile, ptr + infile := c.fopen(string("/_sysparm"), string("r")) + long[sys#cycle0] := cnt + ifnot infile + c.printf0(string("Could not open _sysparm\n")) + return + ifnot c.fgets(@buffer, 100, infile) + c.fclose(infile) + return + c.sscanf1(@buffer, string("%d"), sys#unixtime) + ifnot c.fgets(@buffer, 100, infile) + c.fclose(infile) + return + c.sscanf3(@buffer, string("%d %d %d"), sys#timezone, sys#config, sys#shell_level) + ifnot c.fgets(@buffer, 100, infile) + c.fclose(infile) + return + c.sscanf3(@buffer, string("%d %d %d"), sys#ifflag, sys#whileflag, sys#scriptline) + ptr := sys#environ_vars + repeat while c.fgets(ptr, 100, infile) + ptr := vs.FindChar(ptr, " ") + byte[ptr++] := 0 + ptr += strsize(ptr) - 1 + byte[ptr++] := 0 + byte[ptr] := 0 + c.fclose(infile) + +pub splash | unixtime, date, time, hour, minute, second, year, month, day, cycle, temp, tz + tz := long[TIMEZONE] + unixtime := long[SECONDS] + date := ut.date(unixtime, tz) + time := ut.time(unixtime, tz) + year := word[@date][1] + month := byte[@date][1] + day := byte[@date][0] + hour := word[@time][1] + minute := byte[@time][1] + second := byte[@time][0] + c.printf0(string("******************************************************\n")) + c.printf0(string("* *\n")) + c.printf0(string("* Spinix 1.33 November 14, 2013 *\n")) + c.printf0(string("* *\n")) + c.printf0(string("******************************************************\n")) + c.printf0(string("\n")) + c.printf0(string("Current date and time: ")) + c.printf3(string("%2.2d/%2.2d/%2.2d "), year, month, day) + c.printf3(string("%2.2d:%2.2d:%2.2d\n"),hour, minute, second) + c.printf0(string("\n")) + +' I2C EEProm methods follow below +PUB WriteEEPromLong(SCL, addr) | devSel, ackbit + ' Make sure EEProm is in a known state + Initialize(SCL) + ' Write three address bytes + devSel := $A0 | ((addr >> 15) & %1110) + Start(SCL) + Write(SCL, devSel) + Write(SCL, addr >> 8 & $FF) + Write(SCL, addr & $FF) + ' Write four data bytes + repeat 4 + Write(SCL, byte[addr++]) + Stop(SCL) + ' Wait till the write is done + repeat + Start(SCL) + ackbit := Write(SCL, devSel) + Stop(SCL) + while ackbit + +PUB Initialize(SCL) | SDA ' An I2C device may be left in an + SDA := SCL + 1 ' invalid state and may need to be + outa[SCL] := 1 ' reinitialized. Drive SCL high. + dira[SCL] := 1 + dira[SDA] := 0 ' Set SDA as input + repeat 9 + outa[SCL] := 0 ' Put out up to 9 clock pulses + outa[SCL] := 1 + if ina[SDA] ' Repeat if SDA not driven high + quit ' by the EEPROM + +PUB Start(SCL) | SDA ' SDA goes HIGH to LOW with SCL HIGH + SDA := SCL + 1 + outa[SCL]~~ ' Initially drive SCL HIGH + dira[SCL]~~ + outa[SDA]~~ ' Initially drive SDA HIGH + dira[SDA]~~ + outa[SDA]~ ' Now drive SDA LOW + outa[SCL]~ ' Leave SCL LOW + +PUB Stop(SCL) | SDA ' SDA goes LOW to HIGH with SCL High + SDA := SCL + 1 + outa[SCL]~~ ' Drive SCL HIGH + outa[SDA]~~ ' then SDA HIGH + dira[SCL]~ ' Now let them float + dira[SDA]~ ' If pullups present, they'll stay HIGH + +PUB Write(SCL, data) : ackbit | SDA + SDA := SCL + 1 + ackbit := 0 + data <<= 24 + repeat 8 ' Output data to SDA + outa[SDA] := (data <-= 1) & 1 + outa[SCL]~~ ' Toggle SCL from LOW to HIGH to LOW + outa[SCL]~ + dira[SDA]~ ' Set SDA to input for ACK/NAK + outa[SCL]~~ + ackbit := ina[SDA] ' Sample SDA when SCL is HIGH + outa[SCL]~ + outa[SDA]~ ' Leave SDA driven LOW + dira[SDA]~~ diff --git a/src/bye.fth b/src/bye.fth new file mode 100644 index 0000000..39ca508 --- /dev/null +++ b/src/bye.fth @@ -0,0 +1,59 @@ +hex +create bye_argv 7ed4 , +create bye_start 7c00 , +variable bye_size +variable bye_sector + +: lockset ( locknum ... ) 0cfc0006 cogx1 drop ; +: lockclr ( locknum ... ) 0cfc0007 cogx1 drop ; + +: bye + \ Close files + fb close-file drop + fc close-file drop + + \ Set up the arg list + bye_argv @ 10 + c" /bin/shell" 0a cmove + bye_argv @ 1a + 0 c! + bye_argv @ 10 + bye_argv @ ! + bye_argv @ 1a + bye_argv @ 4 + ! + bye_argv @ 1a + bye_argv @ 8 + ! + [char] / bye_argv @ 1b + c! + 0 bye_argv @ 1c + c! + + \ Open pshell and get sector and length + s" /bin" cd-path + s" pshell" 0 open-file drop + file_sector0 @ bye_sector ! + file_length @ bye_length ! + close-file drop + + \ Open _ploader + s" _ploader" 0 open-file drop + + \ Stop all the cogs except this one and the SD SPI cog + SPI_engine_cog @ 1 - 8 0 do + dup i <> i cogid <> and if i cogstop then loop drop + + \ Clear and return all the locks + 8 0 do i lockclr i lockret loop + + \ Read the loader into memory + bye_start @ 200 2 pick read-file drop drop drop + close-file drop + + \ Add the memory offset to the header + bye_start @ + 8 3 do dup dup w@ bye_start @ + swap w! 2+ loop drop + + \ Add the parms to the stack + bye_start @ 10 + 0 over ! + 4 + 1 over ! + 4 + bye_argv @ over ! + 4 + 0 over ! + 4 + bye_sector over ! + 4 + bye_length over ! + f004 bye_start @ 4 + cogid coginit +; + +decimal diff --git a/src/cat.spin b/src/cat.spin new file mode 100644 index 0000000..e7ae913 --- /dev/null +++ b/src/cat.spin @@ -0,0 +1,75 @@ +'****************************************************************************** +' Copyright (c) 2011, 2012, Dave Hein +' See end of file for terms of use. +'****************************************************************************** +CON + _clkmode = xinput + _xinfreq = $4e495053 ' SPIN + +OBJ + c : "clibsd" + g : "glob" + +PUB main(argc, argv) | i, infile, value, buffer[5], fname, globdata, str[25] + c.enter + i := 1 + repeat + ' Check for stdin + if (argc < 2 OR c.strcmp(long[argv][i], string("-")) == 0) + infile := c.getstdin + ' Read one character at a time if stdin + repeat while ((value := c.fgetc(infile)) => 0) + if (value == 0) + c.printf0(string("\n")) + elseif (value == 4) + quit + else + c.printf1(string("%c"), value) + + ' Open a file + else + globdata := 0 + repeat while (fname := g.glob(long[argv][i], @globdata, @str)) + infile := c.fopen(fname, string("r")) + if (infile == 0) + c.printf1(string("Error opening %s\n"), fname) + c.exit(1) + if c.getmod(fname) & $10 ' Check if it's a directory + c.printf1(string("%s is a directory\n"), fname) + c.fclose(infile) + next + + ' Read 20 characters at a time if it's a file + repeat while ((value := c.fread(@buffer, 1, 20, infile)) > 0) + c.fwrite(@buffer, 1, value, c.getstdout) + + ' Close the file if it's not stdin + c.fclose(infile) + + while (++i < argc) + + c.exit(1) + +{{ ++-----------------------------------------------------------------------------+ +| TERMS OF USE: MIT License | ++-----------------------------------------------------------------------------+ +|Permission is hereby granted, free of charge, to any person obtaining a copy | +|of this software and associated documentation files (the "Software"), to deal| +|in the Software without restriction, including without limitation the rights | +|to use, copy, modify, merge, publish, distribute, sublicense, and/or sell | +|copies of the Software, and to permit persons to whom the Software is | +|furnished to do so, subject to the following conditions: | +| | +|The above copyright notice and this permission notice shall be included in | +|all copies or substantial portions of the Software. | +| | +|THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | +|IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | +|FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE | +|AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | +|LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,| +|OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE| +|SOFTWARE. | ++-----------------------------------------------------------------------------+ +}} diff --git a/src/cd.spin b/src/cd.spin new file mode 100644 index 0000000..17bdda4 --- /dev/null +++ b/src/cd.spin @@ -0,0 +1,53 @@ +'****************************************************************************** +' Copyright (c) 2011, 2012, Dave Hein +' See end of file for terms of use. +'****************************************************************************** +CON + _clkmode = xinput + _xinfreq = $4E505350 ' PSPN + +OBJ + c : "clibsd" + r : "resolve" + +PUB main(argc, argv) | path[25] + if argc > 2 + c.printf0(string("cd [path]\n")) + return 1 + if argc == 1 + c.strcpy(@path, string("/")) + else + r.resolve_path(long[argv][1], @path) + ifnot strcomp(@path, string("/")) + ifnot c.getmod(@path) & %10000 + c.printf1(string("%s is not a directory\n"), @path) + c.exit(1) + if c.chdir(@path) + c.printf1(string("Could not find path %s\n"), @path) + c.exit(2) + + c.exit(0) + +{{ ++-----------------------------------------------------------------------------+ +| TERMS OF USE: MIT License | ++-----------------------------------------------------------------------------+ +|Permission is hereby granted, free of charge, to any person obtaining a copy | +|of this software and associated documentation files (the "Software"), to deal| +|in the Software without restriction, including without limitation the rights | +|to use, copy, modify, merge, publish, distribute, sublicense, and/or sell | +|copies of the Software, and to permit persons to whom the Software is | +|furnished to do so, subject to the following conditions: | +| | +|The above copyright notice and this permission notice shall be included in | +|all copies or substantial portions of the Software. | +| | +|THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | +|IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | +|FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE | +|AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | +|LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,| +|OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE| +|SOFTWARE. | ++-----------------------------------------------------------------------------+ +}} diff --git a/src/cfileio.spin b/src/cfileio.spin new file mode 100644 index 0000000..55e8c72 --- /dev/null +++ b/src/cfileio.spin @@ -0,0 +1,383 @@ +'****************************************************************************** +' C Function Library in Spin +' Author: Dave Hein +' Copyright (c) 2010 +' See end of file for terms of use. +'****************************************************************************** +'****************************************************************************** +' Revison History +' v1.0 - 4/2/2010 First official release +'****************************************************************************** +{{ + This object provides some standard C library functions written in Spin. + There are three main types of functions provided -- string manipulation, + formatted I/O and memory allocation. C functions that use a variable + number of arguments are supported in two ways. The first way uses + unique function names for each number of arguments that is used, such + as printf3 for a printf with three arguments. The second way uses an + argument list, such as vprintf. The argument list consists of an array + of longs that holds the values of the arguments. + + The start function must be called before using any of the serial I/O or + memory allocation functions. It will setup a a serial port that transmits on + pin 30 and receives on pin 31 at 57600 baud. It will also establish a stack + space of 200 longs for the top object. The malloc heap begins immediately + after the stack space, and extends to the end of the RAM. + + Serial I/O and stack space parameters can be specified by calling start1 + instead of start. The first four parameters of start1 are the same as those + used in a call to the FullDuplexSerial start function. The fifth parameter, + stacksize, defines the size in longs of the stack space. + + Additional serial ports can be created by calling the openserial routine. + In additional to rxpin, txpin, mode and baudrate the receive and transmit + buffer sizes are also specified. A fileinfo pointer is returned, which + is used when calling fputc, fgetc and all of the other f... routines. + + The functions puts, putchar and getchar use the str1, tx1 and rx1 methods + of a modified FullDuplexSerial object. This object, cserial, allows for multiple + serial ports through the use of a structer pointer called a handle. See + cserial.spin for more information. + + clib uses a file info structure to access I/O devices, such as the serial port. + The I/O routines that start with the letter "f" require a pointer to the file info + struction, wich is normally named stream. Currently, only serial I/O and + string I/O are supported. Future releases will also support file I/O. +}} + +OBJ + mem : "cmalloc" + fileio : "fsrwx" + sys : "sysdefs" + 'debug : "cserial" + +CON + NEW_LINE = 10 + SEEK_SET = 0 + SEEK_CUR = 1 + SEEK_END = 2 + + stream_fileio = 2 + + stream_type = 0 + stream_data = 1 + stream_size = 8 + +DAT + currentwd long 0[25] + +'****************************************************************************** +' String Routines +'****************************************************************************** +PRI strcpy(dst, src) | slen +{{ + This routine copies the string pointed to by "src" to the location pointed + to by "dst". It returns the value of "dst". +}} + slen := strsize(src) + 1 + bytemove(dst, src, slen) + return dst + +PRI strncpy(dst, src, num) | slen +{{ + This routine copies the first num bytes from the src string to the dst string. + If src contains less than num bytes, then only the bytes contained in the src + string are copied, and the remaining bytes are set to zero. Note that if num + is less than or equal to the string length of src the dst string will not be + terminated with a NULL. This routine returns the value of the dst pointer. +}} + if (num < 1) + return dst + slen := strsize(src) + if (slen > num) + slen := num + bytemove(dst, src, slen) + bytefill(dst + slen, 0, num - slen) + return dst + +PUB fgetc(stream) | char, handle, num +{{ + This routine returns a single character from the input device defined by "stream". + It will not return until a character has been received. +}} + char := 0 + handle := long[stream][stream_data] + num := fileio.hread(handle, @char, 1) + if (num < 1) + return -1 + return char + +PUB fgets(str, num, stream) | handle, i, value +{{ + This routine returns a string from the input device defined by "stream". It will + return until it has either received a carriage return or "size" number of characters. +}} + handle := long[stream][stream_data] + result := str + repeat i from 0 to num - 2 + value := fileio.hread(handle, str, 1) + IF (value < 1) + IF (i == 0) + RETURN 0 + QUIT + IF (BYTE[str] == 0) + QUIT + if byte[str++] == NEW_LINE + quit + BYTE[str] := 0 + +PUB fopen(fname, mode) | stream, handle, handlesize, value, pathstr[20], fname1 +{{ + This routine opens a file with the name pointed to by "fname". The mode is + determined by the first character of the string pointed to by "mode". The first + character must be "r", "w" or "a" for read, write or append. + It returns a pointer to the file info structure if successful, or a NULL value + if not successful. +}} + if byte[mode] == "w" or byte[mode] == "a" + UpdateDate + + ifnot (fname1 := SplitPathFileCd(fname, @pathstr)) + return 0 + + ' Change directory if directory path is specified + handlesize := fileio.handlesize + + ifnot (stream := mem.malloc(stream_size + handlesize)) + return 0 + handle := stream + stream_size + long[stream][stream_data] := handle + + long[stream][stream_type] := stream_fileio + value := fileio.hopen(handle, fname1, byte[mode]) + + if value + mem.free(stream) + return 0 + return stream + +PRI SplitPathFileCd(fname, pathstr) | fname1 + strncpy(pathstr, fname, 80) + fname1 := SplitPathFile(pathstr) + if chdir(pathstr) + return 0 + return fname1 + +PRI SplitPathFile(pathstr) | fname, len, len1 + ' Search backwards for a "/" + len1 := len := strsize(pathstr) + ifnot len + return pathstr + if len == 1 and byte[pathstr] == "/" + return pathstr + 1 + repeat while --len > 0 + if byte[pathstr][len] == "/" + byte[pathstr][len] := 0 + return pathstr + len + 1 + if byte[pathstr] == "/" + bytemove(pathstr+2, pathstr+1, len1) + byte[pathstr][1] := 0 + return pathstr + 2 + else + bytemove(pathstr+1, pathstr, len1+1) + byte[pathstr][0] := 0 + return pathstr + 1 + +PUB fread(buffer, elem_size, num, stream) | handle +{{ + This routine reads a block of data from the input device defined by "stream". + It returns the number of bytes read, or a -1 if the end of file is + encountered. +}} + handle := long[stream][stream_data] + result := fileio.hread(handle, buffer, elem_size * num) + +PUB fwrite(buffer, elem_size, num, stream) | handle +{{ + This routine writes a block of data to the output device defined by "stream". + It returns the number of bytes written, or a -1 if there is an error. +}} + handle := long[stream][stream_data] + result := fileio.hwrite(handle, buffer, elem_size * num) + +PUB fclose(stream) | handle + UpdateDate + handle := long[stream][stream_data] + result := fileio.hclose(handle) + ifnot result + result := 1 + mem.free(stream) + +PUB remove(fname) | pathstr[20], fname1 + ifnot (fname1 := SplitPathFileCd(fname, @pathstr)) + return -1 + result := fileio.hopen(0, fname1, "d") + ifnot result + result := 1 + +PUB rename(fname1, fname2) + result := fileio.rename(fname1, fname2) + +PUB create(fname) | handle, pathstr[20], fname1 + UpdateDate + ifnot (fname1 := SplitPathFileCd(fname, @pathstr)) + return -1 + handle := mem.malloc(fileio.handlesize) + ifnot handle + return -1 + ifnot (result := fileio.hopen(handle, fname1, "r")) + fileio.hclose(handle) + result := 1 + elseif result == -1 + ifnot (result := fileio.hopen(handle, fname1, "w")) + fileio.hclose(handle) + result := 1 + mem.free(handle) + +PUB getfilesize(stream) | handle + handle := long[stream][stream_data] + result := fileio.hget_filesize(handle) + +PUB mount_explicit(DO, CLK, DI, CS) + result := fileio.mount_explicit(DO, CLK, DI, CS) + bytemove(@currentwd, string("/"), 2) + ifnot result + result := 1 + +PUB mount(basepin) + return mount_explicit(basepin, basepin + 1, basepin + 2, basepin + 3) + +PUB getcwd(str, num) + strncpy(str, @currentwd, num) + +PUB FindChar(str, value_0) + REPEAT WHILE (BYTE[str]) + IF (BYTE[str] == value_0) + QUIT + str++ + RETURN str + +PUB chdir(path) + 'debug.dbprintf2(string("chdir: %s -> %s\n"), @currentwd, path) + ifnot strcomp(@currentwd, path) + ifnot (result := fileio.chdir(path)) + 'debug.dbprintf0(string("Success\n")) + strcpy(@currentwd, path) + else + 'debug.dbprintf0(string("Failed\n")) + strcpy(@currentwd, string("/")) + 'else + 'debug.dbprintf0(string("No change\n")) + +PUB opendir(path) | handlesize, stream, handle, value + 'debug.dbprintf1(string("opendir: %s\n"), path) + if chdir(path) + 'debug.dbprintf0(string("opendir: chdir failed\n")) + return 0 + handlesize := fileio.handlesize + ifnot (stream := mem.malloc(stream_size + handlesize)) + 'debug.dbprintf0(string("opendir: malloc failed\n")) + return 0 + handle := stream + stream_size + long[stream][stream_data] := handle + long[stream][stream_type] := stream_fileio + value := fileio.hopendir(handle) + if value + 'debug.dbprintf0(string("opendir: hopendir failed\n")) + mem.free(stream) + return 0 + return stream + +PUB readdir(stream) | handle + handle := long[stream][stream_data] + result := fileio.hreaddir(handle) + +PUB closedir(stream) | handle + handle := long[stream][stream_data] + result := fileio.hclosedir(handle) + mem.free(stream) + +PUB ftell(stream) | handle + handle := long[stream][stream_data] + result := fileio.htell(handle) + +PUB fseek(stream, position, origin) | handle +{{ + This routine moves to the location in the file determined by + the "newpos" and the "origin". For origin values of 0, 1 or 2 + the base position is the start of the file, the current position + or the end of the file respectively. +}} + handle := long[stream][stream_data] + if origin == SEEK_CUR + position += fileio.htell(handle) + elseif origin == SEEK_END + position += fileio.hget_filesize(handle) + result := fileio.hseek(handle, position) + +PUB fflush(stream) | handle + handle := long[stream][stream_data] + result := fileio.hflush(handle) + +PUB mkdir(fname) | fname1, pathstr[20] + UpdateDate + ifnot (fname1 := SplitPathFileCd(fname, @pathstr)) + return -1 + result := fileio.mkdir(fname1) + +PUB chmod(fname, modebits) | fname1, pathstr[20] + ifnot (fname1 := SplitPathFileCd(fname, @pathstr)) + return -1 + result := fileio.chmod(fname1, modebits) + +PUB getmod(fname) | fname1, pathstr[20] + ifnot (fname1 := SplitPathFileCd(fname, @pathstr)) + return -1 + result := fileio.getmod(fname1) + +DAT + year long 0 + month long 0 + day long 0 + days long 0 + +PUB UpdateDate | unixtime, hour, minute, second, leap + unixtime := long[sys#timezone] * 3600 + unixtime += long[sys#unixtime] + second := unixtime // 60 + unixtime /= 60 + minute := unixtime // 60 + unixtime /= 60 + hour := unixtime // 24 + unixtime /= 24 + if unixtime <> days + 'ser.dbprintf0(string("UpdateDate: Compute year, month and day\n")) + days := unixtime + year := (unixtime << 2 + 2) / 1461 + leap := (year & 3) == 2 + unixtime -= (year * 1461 + 1) ~> 2 + if (unixtime > 58 - leap) + unixtime += leap + 2 + month := (unixtime * 12 + 6) / 367 + day := unixtime + 1 - (month++ * 367 + 5) / 12 + year += 1970 + 'ser.dbprintf6(string("UpdateDate: %d/%d/%d %d:%d:%d\n"), year, month, day, hour, minute, second) + fileio.setdate(year, month, day, hour, minute, second) + +{{ +┌──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┐ +│ TERMS OF USE: MIT License │ +├──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┤ +│Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation │ +│files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, │ +│modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software│ +│is furnished to do so, subject to the following conditions: │ +│ │ +│The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.│ +│ │ +│THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE │ +│WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR │ +│COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, │ +│ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. │ +└──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┘ +}} diff --git a/src/cfloatstr.spin b/src/cfloatstr.spin new file mode 100644 index 0000000..7c65c62 --- /dev/null +++ b/src/cfloatstr.spin @@ -0,0 +1,315 @@ +'****************************************************************************** +' Floating Point I/O Routines +' Author: Dave Hein +' Copyright (c) 2010 +' See end of file for terms of use. +'****************************************************************************** +'****************************************************************************** +' Revison History +' v1.0 - 4/2/2010 First official release +'****************************************************************************** +{ + These routines are used by the C library to perform formatted floating point + I/O. The two output routines, putfloate and putfloatf write to string. + + Input formatting is performed by the strtofloat routine. It uses a pointer + to a string pointer and returns the resulting floating point value. + + If floating point I/O is not required for an application this file can be + removed by deleting the references to the object and the three routines in + clib.spin. +} + +'****************************************************************************** +' Floating Point Routines +'****************************************************************************** +PUB putfloate(str, x, width, digits) | man, exp10, signbit +{{ + Convert the floating point value in x to a string of characters in scientific + notation in str. digits determines the number of fractional digits used and + width determines the minimum length of the output string. Leading blanks are + added to achieve the minimum width. + }} + if (digits < 0) + digits := 6 + signbit := tofloat10(x, @man, @exp10) + exp10 += round10(digits + 1, @man) + digits + width -= 5 + signbit - (digits <> 0) + repeat while (width-- > digits) + byte[str++] := " " + if (signbit) + byte[str++] := "-" + str := utoa10(man, str, 1) + byte[str++] := "e" + if (exp10 => 0) + byte[str++] := "+" + else + byte[str++] := "-" + exp10 := -exp10 + if (exp10 < 10) + byte[str++] := "0" + str := utoa10(exp10, str, -1) + byte[str] := 0 + return str + +PUB putfloatf(str, x, width, digits) | lead0, trail0, man, exp10, signbit, digits0 +{{ + Convert the floating point value in x to a string of character in standard + notation in str. digits determines the number of fractional digits used and + width determines the minimum length of the output string. Leading blanks are + added to achieve the minimum width. +}} + if (digits < 0) + digits := 6 + signbit := tofloat10(x, @man, @exp10) + digits0 := numdigits(man, @lead0) + exp10 + if (digits0 > 0) + width -= digits0 + digits0 += digits + if (digits0 > 8) + digits0 := 8 + exp10 += round10(digits0, @man) + digits0 - 1 + if (digits0 < 0) + digits0 := 0 + elseif (digits0 == 0 and man == 1 and digits > 0) + digits0 := 1 + lead0 := digits - digits0 + trail0 := digits - digits0 + exp10 + 1 + width -= signbit + digits - (lead0 => 0) + 1 + repeat while (width-- > 0) + byte[str++] := " " + if (signbit) + byte[str++] := "-" + if (lead0 => 0) + byte[str++] := "0" + if (lead0 > 0) + byte[str++] := "." + repeat while (lead0-- > 0) + byte[str++] := "0" + if (digits0 > 0) + str := utoa10(man, str, exp10 + 1) + exp10 -= digits0 - 1 + repeat while (trail0-- > 0) + if (exp10-- == 0) + byte[str++] := "." + byte[str++] := "0" + byte[str] := 0 + return str + +PUB strtofloat(pstr) | value, exp10, exp10a, signbit, mode, char, esignbit, str +{{ + Convert the string of characters pointer to by "pstr" into a floating point + value. The input can be in either standard or scientific notation. Leading + blanks are ignored. The string pointed to by "pstr" is updated to the last + character postioned that caused processing to be completed. +}} + str := long[pstr] + esignbit := 0 + mode := 0 + value := 0 + exp10 := 0 + exp10a := 0 + signbit := 0 + repeat + char := byte[str++] + if (char == 0) + quit + case mode + 0: + case char + "0".."9": value := char - "0" + "-" : signbit := 1 + " " : next + "+": mode := 1 + other:quit + mode := 1 + 1 : + case char + "0".."9": + if (value =< 200_000_000) + value := (value * 10) + char - "0" + else + exp10++ + ".": mode := 2 + "e", "E": mode := 3 + other: quit + 2: + case char + "0".."9": + if (value =< 200_000_000) + value := (value * 10) + char - "0" + exp10-- + "e", "E": mode := 3 + other: quit + 3: + case char + "0".."9": exp10a := char - "0" + "-" : esignbit := 1 + "+": mode := 4 + other:quit + mode := 4 + 4: + case char + "0".."9":exp10a := (exp10a * 10) + char - "0" + other: quit + if (esignbit) + exp10 -= exp10a + else + exp10 += exp10a + long[pstr] := str + return fromfloat10(value, exp10, signbit) + +DAT +{{ + +________________________ + These tables of scalers are used to scale a floating point number by a ratio of a + power of 10 versus a power of 2. +}} +''SCALE1 10/16 100/128 1000/1024 10^6/2^20 10^12/2^40 10^24/2^80 + scale1 long 1342177280, 1677721600, 2097152000, 2048000000, 1953125000, 1776356839 +''SCALE2 8/10 64/100 512/1000 2^19/10^6 2^39/10^12 2^79/10^24 + scale2 long 1717986918, 1374389535, 1099511628, 1125899907, 1180591621, 1298074215 + nbits1 byte 4, 7, 10, 20, 40, 80 + nbits2 byte 3, 6, 9, 19, 39, 79 + ndecs byte 1, 2, 3, 6, 12, 24 + +PRI floatloop(man, pexp0, pexp1, step0, step1, scale, pexp2, step2) | i +{{ + This private routine reduces the value of exp0 toward 0 while increasing the value + of exp1. This is done in a successive approximation method using the scaling + table passed in "scale". This routine is used here to convert between a mantissa + times a power of 2 or 10 to a mantissa times a power of 10 or 2. +}} + repeat i from 5 to 0 + if (long[pexp0] => byte[step0][i]) + man := (man ** long[scale][i]) << 1 + long[pexp0] -= byte[step0][i] + long[pexp1] += byte[step1][i] + if ((man & $40000000) == 0) + man <<= 1 + long[pexp2] -= step2 + return man + +PRI tofloat10(value, pman, pexp10) | exp2, exp10, man +{{ + This private routine converts from a mantissa times a power of 2 to a mantissa + times a power of 10. +}} + result := value >> 31 + exp2 := ((value >> 23) & 255) - 157 + man := ((value & $007fffff) | $00800000) << 7 + exp10 := 0 + if (exp2 =< 0) + exp2 := -exp2 + man := floatloop(man, @exp2, @exp10, @nbits1, @ndecs, @scale1, @exp2, -1) + man >>= exp2 + exp10 := -exp10 + else + exp2 += 2 + man := floatloop(man, @exp2, @exp10, @nbits2, @ndecs, @scale2, @exp2, 1) + man >>= 2 - exp2 + long[pman] := man + long[pexp10] := exp10 + +PRI fromfloat10(man, exp10, signbit) | exp2 +{{ + This private routine converts from a mantissa times a power of 10 to a mantissa + times a power of two. +}} + if (man == 0) + return 0 + exp2 := 0 + repeat while(man & $40000000) == 0 + man <<= 1 + exp2-- + if (exp10 =< 0) + exp10 := -exp10 + exp2 := -exp2 + man := floatloop(man, @exp10, @exp2, @ndecs, @nbits2, @scale2, @exp2, -1) + exp2 := -exp2 + else + man := floatloop(man, @exp10, @exp2, @ndecs, @nbits1, @scale1, @exp2, 1) + repeat while(man & $ff000000) + man >>= 1 + exp2++ + return (signbit << 31) | ((exp2 + 150) << 23) | (man & $007fffff) + +PRI numdigits(man, pdiv) : numdig | divisor +{{ + This routine determines the number of decimal digits in the number in man. +}} + numdig := 10 + divisor := 1000000000 + repeat while (divisor > man) + numdig-- + divisor /= 10 + long[pdiv] := divisor + +PRI round10(digits, pman) : exp10 | numdig, divisor, rounder, man +{{ + This routine round the number poiinted to by pman to the number of decimal + digits specified by "digits". +}} + man := long[pman] + exp10 := numdigits(man, @divisor) - digits + if (digits < 0) + man := 0 + elseif (digits == 0) + if (man / divisor => 5) + man := 1 + exp10++ + elseif (exp10 > 0) + rounder := 1 + repeat exp10 + rounder *= 10 + man :=(man + (rounder >> 1)) / rounder + divisor /= rounder + if (man / divisor > 9) + man /= 10 + exp10++ + elseif (exp10 < 0) + repeat 0-exp10 + man *= 10 + long[pman] := man + +PRI utoa10(number, str, point) | divisor, temp +{{ + This routine converts the value in "number" to a string of decimal characters + in "str". A decimal point is added after the character position specified by + the value in "point". +}} + if (number == 0) + byte[str++] := "0" + byte[str] := 0 + return str + divisor := 1_000_000_000 + repeat while (divisor > number) + divisor /= 10 + repeat while (divisor > 0) + if (point-- == 0) + byte[str++] := "." + temp := number / divisor + byte[str++] := temp + "0" + number -= temp * divisor + divisor /= 10 + byte[str] := 0 + return str + +{{ +┌──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┐ +│ TERMS OF USE: MIT License │ +├──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┤ +│Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation │ +│files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, │ +│modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software│ +│is furnished to do so, subject to the following conditions: │ +│ │ +│The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.│ +│ │ +│THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE │ +│WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR │ +│COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, │ +│ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. │ +└──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┘ +}} diff --git a/src/chmod.spin b/src/chmod.spin new file mode 100644 index 0000000..cecf774 --- /dev/null +++ b/src/chmod.spin @@ -0,0 +1,111 @@ +'****************************************************************************** +' Copyright (c) 2011, 2012, Dave Hein +' See end of file for terms of use. +'****************************************************************************** +CON + _clkmode = xinput + _xinfreq = $4e495053 ' SPIN + +OBJ + c : "clibsd" + +PUB main(argc, argv) | ptr, fname, value, setmask, setflag, bitpos + c.enter + + if argc <> 3 + usage + + ptr := long[argv][1] + fname := long[argv][2] + + setmask := c.getmod(fname) + + if (setmask == -1) + nofile(fname) + + 'Convert from FAT to UNIX format + setmask := ((setmask&16)>>1) | 4 | (((setmask&1)^1)<<1) | ((setmask&32)>>5) + + if byte[ptr] => "0" AND byte[ptr] =< "7" + c.sscanf1(ptr, string("%o"), @value) + + ' Check for undocumented 76543210XX code to include directory bit + if (value & !15) == %111_110_101_100_011_010_001_000_000_000 + setmask := value + elseifnot value & !7 + setmask := (setmask & 8) | value + else + usage + + 'Convert from UNIX to FAT format + setmask := ((setmask&8)<<1) | ((setmask&1)<<5) | (((setmask&2)>>1)^1) + c.chmod(fname, setmask) + c.exit(0) + + if byte[ptr] <> "+" AND byte[ptr] <> "-" + usage + + setflag := 1 + repeat while byte[ptr] + value := byte[ptr++] + if value == "+" + setflag := 1 + next + elseif value == "-" + setflag := 0 + next + elseif value == "x" + bitpos := 1 + elseif value == "w" + bitpos := 2 + elseif value == "r" + bitpos := 4 + 'elseif value == "d" + 'bitpos := 8 + else + usage + + if setflag + setmask |= bitpos + else + setmask &= (bitpos ^ %1111) + + 'Convert from UNIX to FAT format + setmask := ((setmask&8)<<1) | ((setmask&1)<<5) | (((setmask&2)>>1)^1) + + c.chmod(fname, setmask) + + c.exit(0) + +PUB nofile(fname) + c.printf1(string("Could not find %s\n"), fname) + c.exit(0) + +PUB usage + c.printf0(string("usage: chmod mode file\n")) + c.printf0(string(" mode is either an octal digit or\n")) + c.printf0(string(" contains the characters +-rwx\n")) + c.exit(0) +{{ ++-----------------------------------------------------------------------------+ +| TERMS OF USE: MIT License | ++-----------------------------------------------------------------------------+ +|Permission is hereby granted, free of charge, to any person obtaining a copy | +|of this software and associated documentation files (the "Software"), to deal| +|in the Software without restriction, including without limitation the rights | +|to use, copy, modify, merge, publish, distribute, sublicense, and/or sell | +|copies of the Software, and to permit persons to whom the Software is | +|furnished to do so, subject to the following conditions: | +| | +|The above copyright notice and this permission notice shall be included in | +|all copies or substantial portions of the Software. | +| | +|THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | +|IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | +|FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE | +|AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | +|LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,| +|OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE| +|SOFTWARE. | ++-----------------------------------------------------------------------------+ +}} diff --git a/src/clibsd.bin b/src/clibsd.bin new file mode 100644 index 0000000..37d719d Binary files /dev/null and b/src/clibsd.bin differ diff --git a/src/clibsd.spin b/src/clibsd.spin new file mode 100644 index 0000000..62f3832 --- /dev/null +++ b/src/clibsd.spin @@ -0,0 +1,1338 @@ +'****************************************************************************** +' C Function Library in Spin +' Author: Dave Hein +' Copyright (c) 2010 +' See end of file for terms of use. +'****************************************************************************** +'****************************************************************************** +' Revison History +' v1.0 - 4/2/2010 First official release +'****************************************************************************** +{{ + This object provides some standard C library functions written in Spin. + There are three main types of functions provided -- string manipulation, + formatted I/O and memory allocation. C functions that use a variable + number of arguments are supported in two ways. The first way uses + unique function names for each number of arguments that is used, such + as printf3 for a printf with three arguments. The second way uses an + argument list, such as vprintf. The argument list consists of an array + of longs that holds the values of the arguments. + + The start function must be called before using any of the serial I/O or + memory allocation functions. It will setup a a serial port that transmits on + pin 30 and receives on pin 31 at 57600 baud. It will also establish a stack + space of 200 longs for the top object. The malloc heap begins immediately + after the stack space, and extends to the end of the RAM. + + Serial I/O and stack space parameters can be specified by calling start1 + instead of start. The first four parameters of start1 are the same as those + used in a call to the FullDuplexSerial start function. The fifth parameter, + stacksize, defines the size in longs of the stack space. + + Additional serial ports can be created by calling the openserial routine. + In additional to rxpin, txpin, mode and baudrate the receive and transmit + buffer sizes are also specified. A file stream pointer is returned, which + is used when calling fputc, fgetc and all of the other "f" routines. + + The functions puts, putchar and getchar use the str1, tx1 and rx1 methods + of a modified FullDuplexSerial object. This object, cserial, allows for multiple + serial ports through the use of a structer pointer called a handle. See + cserial.spin for more information. + + clib uses a file stream pointer to access I/O devices, such as the serial port. + The I/O routines that start with the letter "f" require a file stream pointer, + which is normally named stream. There are three version of clib, with different + levels of support. clib.spin only supports serial I/O. clibsd.spin supports + serial I/O as well as file I/O. clibos.spin supports addition operating system + functions that enables loading and running programs from an SD card. +}} + +OBJ + mem : "cmalloc" + ser : "cserial" + fstr : "cfloatstr" + fileio : "cfileio" + sys : "sysdefs" + vs : "varsubs" + +CON + NEW_LINE = 10 + SEEK_SET = 0 + SEEK_CUR = 1 + SEEK_END = 2 + + stream_serial = 1 + stream_fileio = 2 + stream_size = 8 + stream_type = 0 + stream_handle = 1 + +DAT + stdout long 0 + stdin long 0 + stdio long 0 + echoflag long 1 + config long 2 + workbuf byte 0[80] + currentwd byte 0[80] + +'****************************************************************************** +' Initialization Routines +'****************************************************************************** +PUB start +{{ + This routine sets a top object stack size of 200 longs followed by the malloc + heap space. It initializes the standard I/O serial port to receive on P31 and + transmit on P30 at 57,600 baud. The serial port mode flag is set to use a + lock for multi-cog operation. It returns the stdio pointer if successful, or + NULL if not. +}} + 'return start1(31, 30, %110000, 115200, 200) + return start1(31, 30, %010000, 115200, 200) + +PUB start1(rxpin, txpin, mode, baudrate, stacksize) +{{ + This routine calls the malloc initialization routine. The number of longs in + the top object stack is set by the value of "stacksize". It also calls the + openserial function to initialize the standard I/O serial port. It returns the + stdio pointer if successful, or NULL if not. +}} + mem.mallocinit(stacksize) + stdio := openserial(rxpin, txpin, mode, baudrate, 512, 64) + stdin := malloc(stream_size) + stdout := malloc(stream_size) + memcpy(stdin, stdio, stream_size) + memcpy(stdout, stdio, stream_size) + return stdio + +PUB openserial(rxpin, txpin, mode, baudrate, rxsize, txsize) | stream, handle +{{ + This routine allocates memory for a file info and serial port data structure. + It calls the start1 function in the cserial object to create a serial port in + the next available cog. It returns a pointer to the file info structure if + successful, or a NULL value if not successful. +}} + stream := malloc(stream_size + ser#header_size + rxsize + txsize) + ifnot stream + return 0 + handle := stream + stream_size + long[stream][stream_type] := stream_serial + long[stream][stream_handle] := handle + ifnot ser.start1(handle, rxpin, txpin, mode, baudrate, rxsize, txsize) + free(stream) + return 0 + return stream + +'****************************************************************************** +' String Routines +'****************************************************************************** +PUB strcmp(str1, str2) +{{ + This routine compares two strings and returns a value of zero if they are + equal. If "str1" is greater than "str2" it will return a positive value, and + if "str1" is less than "str2" it will return a positive value. +}} + if (strcomp(str1, str2)) + return 0 + repeat while (byte[str1] and byte[str1] == byte[str2]) + str1++ + str2++ + return byte[str1] - byte[str2] + +PUB strncmp(str1, str2, n) +{{ + This routine compares the first "n" characters of two strings. It returns a + value of zero if they are equal. If "str1" is greater than "str2" it will + return a positive value, and if "str1" is less than "str2" it will return a + negative value. +}} + if (n =< 0) + return 0 + repeat while (--n and byte[str1] and byte[str1] == byte[str2]) + str1++ + str2++ + return byte[str1] - byte[str2] + +PUB memcpy(dest, src, n) | bitflag +{{ + This routine copies "n" bytes from the memory location pointed to by "src" to + the memory location pointed to by "dest". The copy is performed as either longs, + words or bytes depending on the least significant bits of the parameters. A + bytemove is performed if "n" is less than 180 to avoid the extra computational + overhead for small mem copies. The value of "dest" is returned. +}} + if n < 180 + bytemove(dest, src, n) + return dest + bitflag := (dest | src | n) & 3 + ifnot bitflag + longmove(dest, src, n >> 2) + elseifnot bitflag & 1 + wordmove(dest, src, n >> 1) + else + bytemove(dest, src, n) + return dest + +PUB memset(dest, val, n) | bitflag +{{ + This routine sets the "n" bytes pointed to by "dest" to the value in the + least significant byte of "val". It returns the value of "dest". memset + uses either bytefill, wordfill or longfill depending on the least significant + bits of the parameters. A bytefill is used when "n" is less than 220 to + avoid the extra computational overhead for small buffers. +}} + if n < 220 + bytefill(dest, val, n) + return dest + val &= 255 + bitflag := (dest | n) & 3 + ifnot bitflag + val |= (val << 24) | (val << 16) | (val << 8) + longfill(dest, val, n >> 2) + elseifnot bitflag & 1 + val |= (val << 8) + wordfill(dest, val, n >> 1) + else + bytefill(dest, val, n) + return dest + +PUB strcat(dst, src) | dlen, slen +{{ + This routine concatenates the string pointed to by "src" to the string at + "dst". It returns the value of "dst". +}} + dlen := strsize(dst) + slen := strsize(src) + 1 + bytemove(dst + dlen, src, slen) + return dst + +PUB strcpy(dst, src) | slen +{{ + This routine copies the string pointed to by "src" to the location pointed + to by "dst". It returns the value of "dst". +}} + slen := strsize(src) + 1 + bytemove(dst, src, slen) + return dst + +PUB strncpy(dst, src, num) | slen +{{ + This routine copies the first num bytes from the src string to the dst string. + If src contains less than num bytes, then only the bytes contained in the src + string are copied, and the remaining bytes are set to zero. Note that if num + is less than or equal to the string length of src the dst string will not be + terminated with a NULL. This routine returns the value of the dst pointer. +}} + if (num < 1) + return dst + slen := strsize(src) + if (slen > num) + slen := num + bytemove(dst, src, slen) + bytefill(dst + slen, 0, num - slen) + return dst + +PUB isdigit(char) +{{ + This routine return true if the value of "char" represents an ASCII decimal + digit between 0 and 9. Otherwise, it returns false. +}} + return char => "0" and char =< "9" + +PUB itoa(number, str, base) | mask, shift, nbits, str0 +{{ + This routine converts the 32-bit value in "number" to an ASCII string at the + location pointed to by "str". The numeric base is determined by the value + of "base", and must be either 2, 4, 8, 10 or 16. Leading zeros are suppressed, + and the number is treated as unsigned except when the base is 10. The length + of the resulting string is returned. +}} + str0 := str + case base + 10 : return itoa10(number, str) + 2 : nbits := 1 + 4 : nbits := 2 + 8 : nbits := 3 + 16 : nbits := 4 + other: + byte[str] := 0 + return 0 + mask := base - 1 + if (nbits == 3) + shift := 30 + else + shift := 32 - nbits + repeat while shift > 0 and ((number >> shift) & mask) == 0 + shift -= nbits + repeat while (shift => 0) + byte[str++] := HexDigit[(number >> shift) & mask] + shift -= nbits + byte[str++] := 0 + return str - str0 - 1 + +'****************************************************************************** +' Output Routines +'****************************************************************************** +PUB setconfig(val) + config := val + +PUB getconfig + result := config + +PUB putnewline(handle) + if config & 2 + ser.tx1(handle, 13) + if config & 1 + ser.tx1(handle, 10) + +PUB puts(str) +{{ + This routine sends the contents of the string pointed to by "str" to the + standard output. +}} + fputs(str, stdout) + putnewline(stdout) + +PUB putchar(char) +{{ + This routine sends the character in "char" to the standard output. +}} + fputc(char, stdout) + +PUB fputc(char, stream) | type, handle +{{ + This routine sends the character in "char" to the output device defined by + "stream". +}} + type := long[stream][stream_type] + handle := long[stream][stream_handle] + if (type == stream_serial) + if char == NEW_LINE + putnewline(handle) + else + ser.tx1(handle, char) + elseif (type == stream_fileio) + fileio.fwrite(@char, 1, 1, stream) + +PUB fputs(str, stream) | type, handle, len, char +{{ + This routine sens the string pointed to by "str" to the output device defined + by "stream". +}} + type := long[stream][stream_type] + handle := long[stream][stream_handle] + if (type == stream_serial) + 'ser.str1(handle, str) + repeat while (char := byte[str++]) + if char == NEW_LINE + putnewline(handle) + else + ser.tx1(handle, char) + elseif (type == stream_fileio) + fileio.fwrite(str, 1, strsize(str), stream) + +PUB fwrite(buffer, elem_size, num, stream) | type, handle, char +{{ + This routine writes a block of data to the output device defined by "stream". + It returns the number of bytes written, or a -1 if there is an error. +}} + type := long[stream][stream_type] + handle := long[stream][stream_handle] + if (type == stream_serial) + num *= elem_size + repeat num + char := byte[buffer++] + if char == NEW_LINE + putnewline(handle) + else + ser.tx1(handle, char) + elseif type == stream_fileio + num := fileio.fwrite(buffer, elem_size, num, stream) + return num + +PUB printf0(fmtstr) +{{ + This is a version of "printf" with a format string and no additional parameters. +}} + vfprintf(stdout, fmtstr, @fmtstr) + +PUB printf1(fmtstr, arg1) +{{ + This is a version of "printf" with a format string and one additional parameter. +}} + vfprintf(stdout, fmtstr, @arg1) + +PUB printf2(fmtstr, arg1, arg2) +{{ + This is a version of "printf" with a format string and two additional parameters. +}} + vfprintf(stdout, fmtstr, @arg1) + +PUB printf3(fmtstr, arg1, arg2, arg3) +{{ + This is a version of "printf" with a format string and three additional parameters. +}} + vfprintf(stdout, fmtstr, @arg1) + +PUB printf4(fmtstr, arg1, arg2, arg3, arg4) +{{ + This is a version of "printf" with a format string and four additional parameters. +}} + vfprintf(stdout, fmtstr, @arg1) + +PUB printf5(fmtstr, arg1, arg2, arg3, arg4, arg5) +{{ + This is a version of "printf" with a format string and five additional parameters. +}} + vfprintf(stdout, fmtstr, @arg1) + +PUB printf6(fmtstr, arg1, arg2, arg3, arg4, arg5, arg6) +{{ + This is a version of "printf" with a format string and six additional parameters. +}} + vfprintf(stdout, fmtstr, @arg1) + +PUB vprintf(fmtstr, arglist) +{{ + This routine uses the string pointed to by "format" to send a formatted string + to the standard output. The parameter "arglist" points to a long array of + values that are merged with the output string dependent on the contents of the + format string. +}} + vfprintf(stdout, fmtstr, arglist) + +PUB sprintf0(str, fmtstr) +{{ + This is a version of "sprintf" with a format string and no additional parameters. +}} + vsprintf(str, fmtstr, @fmtstr) + +PUB sprintf1(str, fmtstr, arg1) +{{ + This is a version of "sprintf" with a format string and one additional parameter. +}} + vsprintf(str, fmtstr, @arg1) + +PUB sprintf2(str, fmtstr, arg1, arg2) +{{ + This is a version of "sprintf" with a format string and two additional parameters. +}} + vsprintf(str, fmtstr, @arg1) + +PUB sprintf3(str, fmtstr, arg1, arg2, arg3) +{{ + This is a version of "sprintf" with a format string and three additional parameters. +}} + vsprintf(str, fmtstr, @arg1) + +PUB sprintf4(str, fmtstr, arg1, arg2, arg3, arg4) +{{ + This is a version of "sprintf" with a format string and four additional parameters. +}} + vsprintf(str, fmtstr, @arg1) + +PUB sprintf5(str, fmtstr, arg1, arg2, arg3, arg4, arg5) +{{ + This is a version of "sprintf" with a format string and five additional parameters. +}} + vsprintf(str, fmtstr, @arg1) + +PUB sprintf6(str, fmtstr, arg1, arg2, arg3, arg4, arg5, arg6) +{{ + This is a version of "sprintf" with a format string and six additional parameters. +}} + vsprintf(str, fmtstr, @arg1) + +PUB fprintf0(stream, fmtstr) + vfprintf(stream, fmtstr, @fmtstr) + +PUB fprintf1(stream, fmtstr, arg1) + vfprintf(stream, fmtstr, @arg1) + +PUB fprintf2(stream, fmtstr, arg1, arg2) + vfprintf(stream, fmtstr, @arg1) + +PUB fprintf3(stream, fmtstr, arg1, arg2, arg3) + vfprintf(stream, fmtstr, @arg1) + +PUB fprintf4(stream, fmtstr, arg1, arg2, arg3, arg4) + vfprintf(stream, fmtstr, @arg1) + +PUB vfprintf(stream, fmtstr, arglist)| str[25] +{{ + This routine prints a formatted string to the output pointed to by "stream". + It calls vsprintf to generate the formatted string in the stack string array + "str", and then sends it to the output stream by calling fputs. The format is + pointed to by "format" and the parameters are contained on a long array pointed + to by "arglist". Note, care must be taken to ensure that the formatted output + string will fit within the 100-byte space provided by "str". Also, the stack + must be large enough to accomodate "str". +}} + vsprintf(@str, fmtstr, arglist) + fputs(@str, stream) + +PUB vsprintf(str, fmtstr, arglist) | arg, width, digits, fmtstr0 +{{ + This routines generates a formatted output string based on the string pointed + to by "format". The parameter "arglist" is a pointer to a long array of values + that are merged into the output string. The characters in the format string + are copied to the output string, exept for special character sequences that + start is % or \. The % character is used to merge values from "arglist". The + characters following the % are as follows: %[0][width][.digits][l][type]. + If a "0" immediately follows the % it indicates that leading zeros should be + displayed. The optional "width" paramter specifieds the minimum width of the + field. The optional ".digits" parameter specifies the number of fractional + digits for floating point, or it may also be used to specify leading zeros and + the minimum width for integer values. The "l" parameter indicates long values, + and it is ignored in this implementation. The "type" parameter is a single + character that indicates the type of output that should be generated. It can + be one of the following characters: + + d - signed decimal number + i - same as d + u - unsigned decimal number + x - hexidecimal number + o - octal number + b - binary number + c - character + s - string + e - floating-point number using scientific notation + f - floating-point number in standard notation + % - prints the % character + + The \ character is used to print the following special characters: + + b - backspace + n - newline or linefeed + r - carriage return + t - tab, or ASCII 9 + \ - prints the \ character + xxx - this inserts the value of a 3-digital octal number between 000 and 377 + + Note, care must be taken the the generated output string does not exceed the size + of the string. A string size of 100 bytes is normally sufficient. +}} + arg := long[arglist] + arglist += 4 + repeat while (byte[fmtstr]) + if (byte[fmtstr] == "%") + fmtstr0 := fmtstr++ + if (byte[fmtstr] == "0") + width := -1 + digits := getvalue(@fmtstr) + else + width := getvalue(@fmtstr) + if (byte[fmtstr] == ".") + fmtstr++ + digits := getvalue(@fmtstr) + else + digits := -1 + if (byte[fmtstr] == "l") + fmtstr++ + case byte[fmtstr] + "d", "i": str := putdec(str, arg, width, digits) + "u" : str := putudec(str, arg, width, digits) + "o" : str := putoctal(str, arg, width, digits) + "b" : str := putbinary(str, arg, width, digits) + "x" : str := puthex(str, arg, width, digits) + "f" : str := fstr.putfloatf(str, arg, width, digits) + "e" : str := fstr.putfloate(str, arg, width, digits) + "c" : byte[str++] := arg + "%" : byte[str++] := "%" + "s" : + strcpy(str, arg) + str += strsize(arg) + other : + byte[str++] := "%" + fmtstr := fmtstr0 + fmtstr++ + arg := long[arglist] + arglist += 4 + elseif (byte[fmtstr] == 92) + case byte[++fmtstr] + 0 : quit + "b" : byte[str++] := 8 + "t" : byte[str++] := 9 + "r" : byte[str++] := 13 + "n" : byte[str++] := 10 + "0".."3": byte[str++] := getoctalbyte(@fmtstr) + other : byte[str++] := byte[fmtstr] + fmtstr++ + else + byte[str++] := byte[fmtstr++] + byte[str++] := 0 + +PRI getoctalbyte(pstr) | str +{{ + This private routine is used to read a 3-digit octal number contained + in the format string. +}} + str := long[pstr] + repeat 3 + if (byte[str] & $f8) == "0" + result := (result << 3) + (byte[str++] & 7) + else + quit + long[pstr] := str - 1 + +PRI itoa10(number, str) | str0, divisor, temp +{{ + This private routine is used to convert a signed integer contained in + "number" to a decimal character string. It is called by itoa when the + numeric base parameter has a value of 10. +}} + str0 := str + if (number < 0) + byte[str++] := "-" + if (number == $80000000) + byte[str++] := "2" + number += 2_000_000_000 + number := -number + elseif (number == 0) + byte[str++] := "0" + byte[str] := 0 + return 1 + divisor := 1_000_000_000 + repeat while (divisor > number) + divisor /= 10 + repeat while (divisor > 0) + temp := number / divisor + byte[str++] := temp + "0" + number -= temp * divisor + divisor /= 10 + byte[str++] := 0 + return str - str0 - 1 + +PRI getvalue(pstr) | str +{{ + Thie private routine is used to extract the width and digits + fields from a format string. It is called by vsprintf. +}} + str := long[pstr] + ifnot isdigit(byte[str]) + return -1 + result := 0 + repeat while isdigit(byte[str]) + result := (result * 10) + byte[str++] - "0" + long[pstr] := str + +DAT +HexDigit byte "0123456789abcdef" + +PRI printpadded(str, numstr, count, width, digits) +{{ + This private routine is used to generate a formatted string + containg at least "width" characters. The value of count + must be identical to the length of the string in "str". + Leading spaces will be generated if width is larger than the + maximum of count and digits. Leading zeros will be generated + if digits is greater than count. +}} + if digits < count + digits := count + repeat while (width-- > digits) + byte[str++] := " " + if byte[numstr] == "-" + byte[str++] := byte[numstr++] + digits-- + repeat while (digits-- > count) + byte[str++] := "0" + strcpy(str, numstr) + return str + strsize(numstr) + +PRI putbinary(str, number, width, digits) | count, numstr[9] +{{ + This private routine converts a number to a string of binary digits. + printpadded is called to insert leading blanks and zeros. +}} + count := itoa(number, @numstr, 2) + return printpadded(str, @numstr, count, width, digits) + +PRI putoctal(str, number, width, digits) | count, numstr[3] +{{ + This private routine converts a number to a string of octal digits. + printpadded is called to insert leading blanks and zeros. +}} + count := itoa(number, @numstr, 8) + return printpadded(str, @numstr, count, width, digits) + +PRI puthex(str, number, width, digits) | count, numstr[3] +{{ + This private routine converts a number to a string of hexadecimal digits. + printpadded is called to insert leading blanks and zeros. +}} + count := itoa(number, @numstr, 16) + return printpadded(str, @numstr, count, width, digits) + +PRI putdec(str, number, width, digits)| count, numstr[3] +{{ + This private routine converts a signed number to a string of decimal + digits. printpadded is called to insert leading blanks and zeros. +}} + count := itoa10(number, @numstr) + return printpadded(str, @numstr, count, width, digits) + +PRI putudec(str, number, width, digits) | count, numstr[3], adjust +{{ + This private routine converts an unsigned number to a string of decimal + digits. printpadded is called to insert leading blanks and zeros. +}} + adjust := 0 + repeat while (number < 0) + number -= 1_000_000_000 + adjust++ + count := itoa10(number, @numstr) + byte[@numstr] += adjust + return printpadded(str, @numstr, count, width, digits) + +'****************************************************************************** +' Input Routines +'****************************************************************************** +PUB getchar +{{ + This routine returns a single character from the standard input. It will not + return until a character has been received. +}} + return fgetc(stdin) + +PUB gets(str) | last +{{ + This routine returns a string from the standard input. It will not return until + is has received a carriage return. The carriage return is not included in the + returned string. Received characters are echoed back out ot the the serial port. + Backspaces will cause the previous character to be removed from the buffer, and + a character sequence of backspace, space and backspace will be transmitted to the + serial port to erase the previous character. Backspace characters are not inserted + into the string. +}} + result := fgets(str, 100000000, stdin) + if result > 0 + last := strsize(str) - 1 + if last => 0 + str += last + if byte[str] == NEW_LINE + byte[str] := 0 + +PRI gets1(str, size, handle) | char, str0, editflag, echoflag1 + editflag := (handle == long[stdio][stream_handle]) + echoflag1 := editflag & echoflag + str0 := str + repeat while (size > 1) + char := ser.rx1(handle) + if (char == 8 AND editflag) + if (str > str0) + if echoflag1 + ser.tx1(handle, 8) + ser.tx1(handle, " ") + ser.tx1(handle, 8) + str-- + size++ + next + if char == 4 + if (str == str0) + str0 := 0 + quit + if char == NEW_LINE OR char == 13 + if echoflag1 + putnewline(handle) + byte[str++] := NEW_LINE + quit + else + if echoflag1 + ser.tx1(handle, char) + byte[str++] := char + size-- + byte[str] := 0 + return str0 + +PUB fgetc(stream) | char, type, handle +{{ + This routine returns a single character from the input device defined by "stream". + It will not return until a character has been received. +}} + type := long[stream][stream_type] + handle := long[stream][stream_handle] + if (type == stream_serial) + char := ser.rx1(handle) + elseif (type == stream_fileio) + char := 0 + if fileio.fread(@char, 1, 1, stream) < 1 + char := -1 + return char + +PUB fgets(str, size, stream) | type, handle +{{ + This routine returns a string from the input device defined by "stream". It will + return until it has either received a carriage return or "size" number of characters. +}} + type := long[stream][stream_type] + handle := long[stream][stream_handle] + size-- + if (type == stream_serial) + result := gets1(str, size, handle) + elseif (type == stream_fileio) + result := fileio.fgets(str, size, stream) + +PUB fread(buffer, elem_size, num, stream) | type, handle +{{ + This routine reads a block of data from the input device defined by "stream". + It returns the number of bytes read, or a -1 if the end of file is + encountered. +}} + type := long[stream][stream_type] + handle := long[stream][stream_handle] + if (type == stream_serial) + num *= elem_size + repeat num + byte[buffer++] := ser.rx1(handle) + elseif type == stream_fileio + num := fileio.fread(buffer, elem_size, num, stream) + return num + +PUB scanf1(fmtstr, parg1) +{{ + This routine is a version of "scanf" with a format string and one parameter. +}} + vscanf(fmtstr, @parg1) + +PUB scanf2(fmtstr, parg1, parg2) +{{ + This routine is a version of "scanf" with a format string and two parameters. +}} + vscanf(fmtstr, @parg1) + +PUB scanf3(fmtstr, parg1, parg2, parg3) +{{ + This routine is a version of "scanf" with a format string and three parameters. +}} + vscanf(fmtstr, @parg1) + +PUB scanf4(fmtstr, parg1, parg2, parg3, parg4) +{{ + This routine is a version of "scanf" with a format string and four parameters. +}} + vscanf(fmtstr, @parg1) + +PUB vscanf(fmtstr, arglist) | str[25] +{{ + This routine reads a string from the standard input using gets, and it converts + the data based on the format in the "format" string. The converted values are + stored at the locations determined by the list of long pointers in "arglist". + See vsscanf for the description of the format string. +}} + gets(@str) + vsscanf(@str, fmtstr, arglist) + +PUB sscanf1(str, fmtstr, parg1) +{{ + This is a version of sscanf with a format string and one parameter. +}} + vsscanf(str, fmtstr, @parg1) + +PUB sscanf2(str, fmtstr, parg1, parg2) +{{ + This is a version of sscanf with a format string and two parameters. +}} + vsscanf(str, fmtstr, @parg1) + +PUB sscanf3(str, fmtstr, parg1, parg2, parg3) +{{ + This is a version of sscanf with a format string and three parameters. +}} + vsscanf(str, fmtstr, @parg1) + +PUB sscanf4(str, fmtstr, parg1, parg2, parg3, parg4) +{{ + This is a version of sscanf with a format string and four parameters. +}} + vsscanf(str, fmtstr, @parg1) + +PUB vsscanf(str, fmtstr, arglist) | parg +{{ + This routine converts the contents of the string pointed to by "str" into + numerical values that are stored at the locations derermined by the list of + long pointers in "arglist". The format string contains conversion flags, + which are prefixed with the % character. The list of conversion flags is + as follows: + + b - Convert a binary number + o - Convert an octal number + d - Convert a signed decimal number + x - Convert a hexadecimal number + f - Convert a floating point number + e - Same as f + + Any other characters will be ignored, and will cause a character to be skipped + on the input string. Leading spaces are also ignored when converting a + number. +}} + parg := long[arglist] + arglist += 4 + repeat while byte[fmtstr] + if byte[fmtstr++] == "%" + case byte[fmtstr] + "b" : long[parg] := getbin(@str) + "o" : long[parg] := getoct(@str) + "d" : long[parg] := getdec(@str) + "x" : long[parg] := gethex(@str) + "f", "e" : long[parg] := fstr.strtofloat(@str) + other: str++ + fmtstr++ + parg := long[arglist] + arglist += 4 + else + str++ + +PRI getbin(pstr) | str +{{ + This private routine is used by vsscanf to convert a string of binary digits to + a numerical value. +}} + str := long[pstr] + repeat while byte[str] == " " + str++ + repeat while (byte[str] & $fe) == "0" + result := (result << 1) + (byte[str++] & 1) + long[pstr] := str + +PRI getoct(pstr) | str +{{ + This private routine is used by vsscanf to convert a string of octal digits to + a numerical value. +}} + str := long[pstr] + repeat while byte[str] == " " + str++ + repeat while (byte[str] & $f8) == "0" + result := (result << 3) + (byte[str++] & 7) + long[pstr] := str + +PRI gethex(pstr) | str, char +{{ + This private routine is used by vsscanf to convert a string of hexadecimal digits to + a numerical value. +}} + str := long[pstr] + repeat while byte[str] == " " + str++ + repeat + char := byte[str++] + case char + "0".."9": result := (result << 4) + char - "0" + "a".."f": result := (result << 4) + char - "a" + 10 + "A".."F": result := (result << 4) + char - "A" + 10 + other : quit + long[pstr] := str - 1 + +PRI getdec(pstr) | str, signflag +{{ + This private routine is used by vsscanf to convert a string of decimal digits to + a numerical value. +}} + signflag := 0 + str := long[pstr] + repeat while byte[str] == " " + str++ + if isdigit(byte[str]) + result:= byte[str] - "0" + elseif byte[str] == "-" + signflag := 1 + elseif byte[str] <> "+" + long[pstr] := str + return + str++ + repeat while isdigit(byte[str]) + result := (result * 10) + byte[str++] - "0" + if signflag + result := -result + long[pstr] := str + +'****************************************************************************** +' Malloc Passthrough Routines - see cmalloc.spin for documentation +'****************************************************************************** +PUB malloc(size) +{{ + This routine provides an interface to the malloc routine in cmalloc.spin +}} + return mem.malloc(size) + +PUB free(ptr) +{{ + This routine provides an interface to the free routine in cmalloc.spin +}} + return mem.free(ptr) + +PUB calloc(size) +{{ + This routine provides an interface to the calloc routine in cmalloc.spin +}} + return mem.calloc(size) + +'****************************************************************************** +' File I/O Passthrough Routines - see cfileio.spin for documentation +'****************************************************************************** + +PUB fopen(fname, mode) +{{ + This routine opens a file with the name pointed to by "fname". The mode is + determined by the first character of the string pointed to by "mode". The first + character must be "r", "w" or "a" for read, write or append. + It returns a pointer to the file info structure if successful, or a NULL value + if not successful. +}} + resolve_path(fname, @workbuf) + result := fileio.fopen(@workbuf, mode) + +PUB fclose(stream) + result := fileio.fclose(stream) + +PUB remove(fname) + resolve_path(fname, @workbuf) + result := fileio.remove(@workbuf) + +PUB rename(fname1, fname2) | workbuf2[20] + resolve_path(fname1, @workbuf) + resolve_path(fname2, @workbuf2) + result := fileio.rename(@workbuf, @workbuf2) + +PUB create(fname) + resolve_path(fname, @workbuf) + result := fileio.create(@workbuf) + +PUB getfilesize(stream) + result := fileio.getfilesize(stream) + +PUB mount(basepin) + strcpy(@currentwd, string("/")) + result := fileio.mount(basepin) + +PUB mount_explicit(DO, CLK, DI, CS) + strcpy(@currentwd, string("/")) + result := fileio.mount_explicit(DO, CLK, DI, CS) + +PUB getcwd(str, num) | len + len := strsize(@currentwd) + 'printf0(string("getcwd: Howdy\n")) + 'printf3(string("getcwd: %d %d %s\n"), num, len, @currentwd) + if len > num - 1 + len := num - 1 + bytemove(str, @currentwd, len) + byte[str][len] := 0 + result := str + 'result := fileio.getcwd(str, num) + +PUB chdir(path) + resolve_path(path, @workbuf) + result := fileio.chdir(@workbuf) + ifnot result + strcpy(@currentwd, path) + +PUB opendir(path) + resolve_path(path, @workbuf) + result := fileio.opendir(@workbuf) + +PUB readdir(handle) + result := fileio.readdir(handle) + +PUB closedir(handle) + result := fileio.closedir(handle) + +PUB ftell(stream) + result := fileio.ftell(stream) + +PUB fseek(stream, position, origin) +{{ + This routine moves to the location in the file determined by + the "newpos" and the "origin". For origin values of 0, 1 or 2 + the base position is the start of the file, the current position + or the end of the file respectively. +}} + result := fileio.fseek(stream, position, origin) + +PUB fflush(stream) + result := fileio.fflush(stream) + +PUB mkdir(fname) + resolve_path(fname, @workbuf) + result := fileio.mkdir(@workbuf) + +PUB chmod(fname, modebits) + resolve_path(fname, @workbuf) + result := fileio.chmod(@workbuf, modebits) + +PUB getmod(fname) + resolve_path(fname, @workbuf) + result := fileio.getmod(@workbuf) + +PUB enter '(argc, argv) + enter1(200) '(argc, argv, 200) + +'PUB enter1(argc, argv, stacksize) | pins, cfg, dirptr, inptr, outptr +PUB enter1(stacksize) | pins, cfg, dirptr + start1(31, 30, %010000, 115200, stacksize) + 'inptr := long[argv][argc] + 'outptr := long[argv][argc+1] + 'dirptr := long[argv][argc+2] + 'cfg := long[argv][argc+3] + 'pins := long[argv][argc+4] + 'long[sys#config] := cfg + 'long[sys#sd_pins] := pins + cfg := long[sys#config] + pins := long[sys#sd_pins] + setconfig(cfg) +{ + printf1(string("argc = %d\n"), argc) + printf1(string("config = %8.8x\n"), cfg] + printf1(string("pins = %8.8x\n"), pins) + printf1(string("inptr = %s\n"), inptr) + printf1(string("outptr = %s\n"), outptr) + printf1(string("CWD = %s\n"), dirptr) + printf4(string("pins = %d %d %d %d\n"), byte[@pins][0], byte[@pins][1], byte[@pins][2], byte[@pins][3]) + waitcnt(clkfreq/10+cnt) +} + mount_explicit(byte[@pins], byte[@pins][1], byte[@pins][2], byte[@pins][3]) + 'printf0(string("After mount\n")) + dirptr := vs.GetVarVal(string("PWD")) + if byte[dirptr] + chdir(dirptr) + 'printf0(string("After chdir\n")) + if OpenRedirect '(argc, argv) + 'printf0(string("OpenRedirect failed\n")) + exit(1) + 'printf0(string("After OpenRedirect\n")) + +PUB exit(retval) | argv[6] + CloseRedirect + StopCogsReturnLocks + waitcnt(clkfreq/100 + cnt) + long[sys#return_value] := retval + runp.runprog(0, 0, long[sys#shell_sector], long[sys#shell_size], 0) +{ + argv[0] := string("/bin/pshell") + argv[1] := @result + argv[2] := @result + run(1, @argv) +} + +'****************************************************************************** +' Standard I/O redirection routines +'****************************************************************************** +PUB setstdin(stream) + memcpy(stdin, stream, stream_size) + +PUB setstdout(stream) + echoflag := 0 + memcpy(stdout, stream, stream_size) + +PUB resetstdin + memcpy(stdin, stdio, stream_size) + +PUB resetstdout + echoflag := 1 + memcpy(stdout, stdio, stream_size) + +PUB getstdin + return stdin + +PUB getstdout + return stdout + +PUB CheckStackSpace + printf1(string("The last %d longs have not been used on the stack\n"), mem.CheckStackSpace) + +PUB CheckMallocSpace | ptr, numalloc, numfree + numalloc := 0 + numfree := 0 + ptr := mem.GetMallocList + printf0(string("MallocList\n")) + repeat while(ptr) + printf2(string("addr = %04x size = %d\n"), ptr, word[ptr][1]) + numalloc += word[ptr][1] + ptr := word[ptr] + ptr := mem.GetFreeList + printf0(string("FreeList\n")) + repeat while(ptr) + printf2(string("addr = %04x size = %d\n"), ptr, word[ptr][1]) + numfree += word[ptr][1] + ptr := word[ptr] + printf2(string("%d bytes allocated, %d bytes free\n"), numalloc, numfree) + +{{ ++-----------------------------------------------------------------------------+ +| TERMS OF USE: MIT License | ++-----------------------------------------------------------------------------+ +|Permission is hereby granted, free of charge, to any person obtaining a copy | +|of this software and associated documentation files (the "Software"), to deal| +|in the Software without restriction, including without limitation the rights | +|to use, copy, modify, merge, publish, distribute, sublicense, and/or sell | +|copies of the Software, and to permit persons to whom the Software is | +|furnished to do so, subject to the following conditions: | +| | +|The above copyright notice and this permission notice shall be included in | +|all copies or substantial portions of the Software. | +| | +|THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | +|IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | +|FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE | +|AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | +|LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,| +|OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE| +|SOFTWARE. | ++-----------------------------------------------------------------------------+ +}} + +OBJ + fsrwx : "fsrwx" + runp : "runprog" + +PUB run(argc, argv, type) | infile, i, infile1, argv1, dbase, size, handle + + if argc < 1 + printf1(string("argc = %d\n"), argc) + return -4 + + infile := fopen(long[argv], string("r")) + ifnot infile + printf1(string("2Could not open %s\n"), long[argv]) + return -3 + + ' Add cwd, config and SD pins to parms + long[argv][argc+2] := @currentwd + + ' Determine the file size and starting sector number + handle := long[infile][stream_handle] + result := fsrwx.hget_first_sector(handle) + size := fsrwx.hget_filesize(handle) + + ' Copy the arg list to compact it + argv1 := sys#argv_parms + copyarglist(argc, argv, argv1) + + StopCogsReturnLocks + + runp.runprog(argc, argv1, result, size, type) + +PUB StopCogsReturnLocks | cogspi, i + ' Stop all the cogs except this one and the SD SPI cog + cogspi := long[sys#SPI_engine_cog] - 1 + repeat i from 0 to 7 + if i <> cogid and i <> cogspi + cogstop(i) + + ' Clear and return all the locks + repeat i from 0 to 7 + lockclr(i) + lockret(i) +{ +PUB copyarglist(argc, argv, argv1) | ptr, i, num + if argc + ptr := @long[argv1][argc+5] + repeat i from 0 to argc + 2 + long[argv1][i] := ptr + num := strsize(long[argv][i]) + 1 + bytemove(ptr, long[argv][i], num) + ptr += num + long[argv1][argc+3] := long[argv][argc+3] + long[argv1][argc+4] := long[argv][argc+4] +} +PUB copyarglist(argc, argv, argv1) | ptr, i, num + if argc > 0 + ptr := @long[argv1][argc+1] + repeat i from 0 to argc - 1 + long[argv1][i] := ptr + num := strsize(long[argv][i]) + 1 + bytemove(ptr, long[argv][i], num) + ptr += num + long[argv1][argc] := 0 + +PUB kill(cognum) + cogstop(cognum) + +PUB CloseRedirect + if long[stdin][stream_type] == stream_fileio + fclose(stdin) + resetstdin + if long[stdout][stream_type] == stream_fileio + fclose(stdout) + resetstdout + vs.RemoveVar(string("<")) + vs.RemoveVar(string(">")) + vs.RemoveVar(string(">>")) + +PUB OpenRedirect { (argc, argv) } | fname, stream, modestr + fname := vs.GetVarVal(string("<")) + 'fname := long[argv][argc] + if byte[fname] + stream := fopen(fname, string("r")) + ifnot stream + printf1(string("4Could not open %s\n"), fname) + return 1 + else + setstdin(stream) + 'fname := long[argv][argc+1] + fname := vs.GetVarVal(string(">>")) + if byte[fname] + modestr := string("a") + else + fname := vs.GetVarVal(string(">")) + if byte[fname] + modestr := string("w") + if byte[fname] + stream := fopen(fname, modestr) + ifnot stream + CloseRedirect + printf1(string("5Could not open %s\n"), fname) + return 1 + else + setstdout(stream) + +PUB resolve_path(fname, path) | ptr, ptr1, pathLen + if strcomp(fname, string(".")) + fname++ + elseifnot strncmp(fname, string("./"), 2) + fname += 2 + if byte[fname] == "/" + strcpy(path, fname) + elseifnot byte[fname] + 'getcwd(path, 80) + strcpy(path, @currentwd) + else + 'getcwd(path, 80) + strcpy(path, @currentwd) + pathLen := strsize(path) + if byte[path][pathLen - 1] <> "/" + strcat(path, string("/")) + strcat(path, fname) + ' Process .. + ptr := path + repeat while byte[ptr] + if (not strncmp(ptr, string("/.."), 3) and(byte[ptr][3] == 0 or byte[ptr][3] == "/")) + if (ptr == path) + movestr(ptr, ptr + 3) + else + ptr1 := ptr - 1 + repeat while (ptr1 <> path) + if (byte[ptr1] == "/") + quit + ptr1-- + movestr(ptr1, ptr + 3) + ptr := ptr1 + elseif not byte[ptr][1] and ptr <> path + byte[ptr] := 0 + elseif byte[ptr][1] == "/" + movestr(ptr, ptr + 1) + else + ptr++ + repeat while (byte[ptr]) + if (byte[ptr] == "/") + quit + ptr++ + ifnot byte[path] + strcpy(path, string("/")) + 'printf2(string("resolve_path: %s -> %s\n"), fname, path) + +PUB movestr(ptr1, ptr2) + repeat while byte[ptr2] + byte[ptr1++] := byte[ptr2++] + byte[ptr1] := 0 + diff --git a/src/clibsd.spn b/src/clibsd.spn new file mode 100644 index 0000000..c749e43 --- /dev/null +++ b/src/clibsd.spn @@ -0,0 +1,97 @@ +PUB start +PUB start1(rxpin, txpin, mode, baudrate, stacksize) +PUB openserial(rxpin, txpin, mode, baudrate, rxsize, txsize) +PUB strcmp(str1, str2) +PUB strncmp(str1, str2, n) +PUB memcpy(dest, src, n) +PUB memset(dest, val, n) +PUB strcat(dst, src) +PUB strcpy(dst, src) +PUB strncpy(dst, src, num) +PUB isdigit(char) +PUB itoa(number, str, base) +PUB setconfig(val) +PUB getconfig +PUB putnewline(handle) +PUB puts(str) +PUB putchar(char) +PUB fputc(char, stream) +PUB fputs(str, stream) +PUB fwrite(buffer, elem_size, num, stream) +PUB printf0(fmtstr) +PUB printf1(fmtstr, arg1) +PUB printf2(fmtstr, arg1, arg2) +PUB printf3(fmtstr, arg1, arg2, arg3) +PUB printf4(fmtstr, arg1, arg2, arg3, arg4) +PUB printf5(fmtstr, arg1, arg2, arg3, arg4, arg5) +PUB printf6(fmtstr, arg1, arg2, arg3, arg4, arg5, arg6) +PUB vprintf(fmtstr, arglist) +PUB sprintf0(str, fmtstr) +PUB sprintf1(str, fmtstr, arg1) +PUB sprintf2(str, fmtstr, arg1, arg2) +PUB sprintf3(str, fmtstr, arg1, arg2, arg3) +PUB sprintf4(str, fmtstr, arg1, arg2, arg3, arg4) +PUB sprintf5(str, fmtstr, arg1, arg2, arg3, arg4, arg5) +PUB sprintf6(str, fmtstr, arg1, arg2, arg3, arg4, arg5, arg6) +PUB fprintf0(stream, fmtstr) +PUB fprintf1(stream, fmtstr, arg1) +PUB fprintf2(stream, fmtstr, arg1, arg2) +PUB fprintf3(stream, fmtstr, arg1, arg2, arg3) +PUB fprintf4(stream, fmtstr, arg1, arg2, arg3, arg4) +PUB vfprintf(stream, fmtstr, arglist)| str[25] +PUB vsprintf(str, fmtstr, arglist) +PUB getchar +PUB gets(str) +PUB fgetc(stream) +PUB fgets(str, size, stream) +PUB fread(buffer, elem_size, num, stream) +PUB scanf1(fmtstr, parg1) +PUB scanf2(fmtstr, parg1, parg2) +PUB scanf3(fmtstr, parg1, parg2, parg3) +PUB scanf4(fmtstr, parg1, parg2, parg3, parg4) +PUB vscanf(fmtstr, arglist) +PUB sscanf1(str, fmtstr, parg1) +PUB sscanf2(str, fmtstr, parg1, parg2) +PUB sscanf3(str, fmtstr, parg1, parg2, parg3) +PUB sscanf4(str, fmtstr, parg1, parg2, parg3, parg4) +PUB vsscanf(str, fmtstr, arglist) +PUB malloc(size) +PUB free(ptr) +PUB calloc(size) +PUB fopen(fname, mode) +PUB fclose(stream) +PUB remove(fname) +PUB rename(fname1, fname2) +PUB create(fname) +PUB getfilesize(stream) +PUB mount(basepin) +PUB mount_explicit(DO, CLK, DI, CS) +PUB getcwd(str, num) +PUB chdir(path) +PUB opendir(path) +PUB readdir(handle) +PUB closedir(handle) +PUB ftell(stream) +PUB fseek(stream, position, origin) +PUB fflush(stream) +PUB mkdir(fname) +PUB chmod(fname, modebits) +PUB getmod(fname) +PUB enter(argc, argv) +PUB enter1(argc, argv, stacksize) +PUB exit(retval) +PUB setstdin(stream) +PUB setstdout(stream) +PUB resetstdin +PUB resetstdout +PUB getstdin +PUB getstdout +PUB CheckStackSpace +PUB CheckMallocSpace +PUB run(argc, argv) +PUB copyarglist(argc, argv, argv1) +PUB kill(cognum) +PUB CloseRedirect +PUB OpenRedirect(argc, argv) +PUB resolve_path(fname, path) +PUB movestr(ptr1, ptr2) diff --git a/src/cmalloc.spin b/src/cmalloc.spin new file mode 100644 index 0000000..cabe3fd --- /dev/null +++ b/src/cmalloc.spin @@ -0,0 +1,209 @@ +'****************************************************************************** +' C malloc written in Spin +' Author: Dave Hein +' Copyright (c) 2010 +' See end of file for terms of use. +'****************************************************************************** +'****************************************************************************** +' Revison History +' v1.0 - 4/2/2010 First official release +'****************************************************************************** +{ + This object contains the malloc, free and calloc routines used by clib. +} +OBJ + sys : "sysdefs" + +CON + NEXT_BLK = 0 + BLK_SIZE = 1 + HDR_SIZE = 4 + RAM_SIZE = sys#rendezvous + CHECKWORD = $dead1eaf + +DAT + locknum long 0 + memfreelist long 0 + malloclist long 0 + laststackaddr long 0 + +PUB CheckStackSpace | addr + repeat addr from laststackaddr to 0 step 4 + if long[addr] <> CHECKWORD + quit + result++ + +PUB GetMallocList + return malloclist + +PUB GetFreeList + return memfreelist + +PUB mallocinit(stacksize) | currblk, addr +'' Initialize the malloc heap "stacksize" longs after the current stack pointer +'' Allocate a lock and return the lock number plus one if successful, or zero if not + locknum := locknew + 1 + currblk := @stacksize + (stacksize << 2) + 'Fill stack with checkword + laststackaddr := currblk - 4 + longfill(@stacksize + 40, CHECKWORD, stacksize - 10) + 'longfill(RAM_SIZE, 0, ($8000 - RAM_SIZE)/4) + 'longfill(sys#spi_engine_cog, 0, 4) + memfreelist := currblk + malloclist := 0 + word[currblk]{NEXT_BLK} := 0 + word[currblk][BLK_SIZE] := RAM_SIZE - currblk + return locknum + +PUB malloc(size) | prevblk, currblk, nextblk, prevblk0, currblk0, size0 +'' Allocate a memory block of "size" bytes. Return a pointer to the clock if +'' successful, or zero if a large enough memory block could not be found + 'ser.dbprintf1(string("malloc: %d\n"), size) + prevblk := 0 + prevblk0 := 0 + currblk0 := 0 + if (size =< 0) + return 0 + repeat until not lockset(locknum - 1) + currblk := memfreelist + + ' Adjust size to nearest long plus the header size + size := ((size + 3) & (!3)) + HDR_SIZE + + ' Search for a block of memory + repeat while (currblk) + if (word[currblk][BLK_SIZE] => size) + prevblk0 := prevblk + currblk0 := currblk + prevblk := currblk + currblk := word[currblk]{NEXT_BLK} + + currblk := currblk0 + prevblk := prevblk0 + + ' Return null if block not found + if (currblk == 0) + lockclr(locknum - 1) + return 0 + + ' Split block if larger than needed + size0 := word[currblk][BLK_SIZE] - size + if (size0 => HDR_SIZE + 4) + word[currblk][BLK_SIZE] := size0 + currblk += size0 + word[currblk][BLK_SIZE] := size + + ' Otherwise, use space without splitting and remove from memfreelist + else + nextblk := word[currblk]{NEXT_BLK} + if (prevblk) + word[prevblk]{NEXT_BLK} := nextblk + else + memfreelist := nextblk + + ' Add to the beginning of the malloc list + word[currblk]{NEXT_BLK} := malloclist + malloclist := currblk + lockclr(locknum - 1) + return currblk + HDR_SIZE + +PUB freeraw(ptr, size) + repeat until not lockset(locknum - 1) + word[ptr][BLK_SIZE] := size + meminsert(ptr) + lockclr(locknum - 1) + return 1 + +PUB free(ptr) | prevblk, currblk, nextblk +'' Return the memory block at "ptr" to the free list. Return a value of one +'' if successful, or zero if the memory block was not on the allocate list. + prevblk := 0 + repeat until not lockset(locknum - 1) + nextblk := malloclist + currblk := ptr - HDR_SIZE + + ' Search the malloclist for the currblk pointer + repeat while (nextblk) + if (currblk == nextblk) + ' Remove from the malloc list + if (prevblk) + word[prevblk]{NEXT_BLK} := word[nextblk]{NEXT_BLK} + else + malloclist := word[nextblk]{NEXT_BLK} + ' Add to the free list + meminsert(nextblk) + lockclr(locknum - 1) + return 1 + prevblk := nextblk + nextblk := word[nextblk]{NEXT_BLK} + + ' Return a NULL value if not found + lockclr(locknum - 1) + return 0 + +PRI meminsert(currblk) | prevblk, nextblk +'' Insert a memory block back into the free list. Merge blocks together if +'' the memory block is contiguous with other blocks on the list. + prevblk := 0 + nextblk := memfreelist + + ' Find Insertion Point + repeat while (nextblk) + if ((currblk => prevblk) and (currblk =< nextblk)) + quit + prevblk := nextblk + nextblk := word[nextblk]{NEXT_BLK} + + ' Merge with the previous block if contiguous + if (prevblk and (prevblk + word[prevblk][BLK_SIZE] == currblk)) + word[prevblk][BLK_SIZE] += word[currblk][BLK_SIZE] + ' Also merge with next block if contiguous + if (prevblk + word[prevblk][BLK_SIZE] == nextblk) + word[prevblk][BLK_SIZE] += word[nextblk][BLK_SIZE] + word[prevblk]{NEXT_BLK} := word[nextblk]{NEXT_BLK} + + ' Merge with the next block if contiguous + elseif (nextblk and (currblk + word[currblk][BLK_SIZE] == nextblk)) + word[currblk][BLK_SIZE] += word[nextblk][BLK_SIZE] + word[currblk]{NEXT_BLK} := word[nextblk]{NEXT_BLK} + if (prevblk) + word[prevblk]{NEXT_BLK} := currblk + else + memfreelist := currblk + + ' Insert in the middle of the free list if not contiguous + elseif (prevblk) + word[prevblk]{NEXT_BLK} := currblk + word[currblk]{NEXT_BLK} := nextblk + + ' Otherwise, insert at beginning of the free list + else + memfreelist := currblk + word[currblk]{NEXT_BLK} := nextblk + +PUB calloc(size) | ptr +'' Allocate a memory block of "size" bytes and initialize to zero. Return +'' a pointer to the memory block if successful, or zero if a large enough +'' memory block could not be found. + ptr := malloc(size) + if (ptr) + longfill(ptr, 0, (size + 3) >> 2) + return ptr + +{{ +┌──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┐ +│ TERMS OF USE: MIT License │ +├──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┤ +│Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation │ +│files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, │ +│modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software│ +│is furnished to do so, subject to the following conditions: │ +│ │ +│The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.│ +│ │ +│THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE │ +│WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR │ +│COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, │ +│ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. │ +└──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┘ +}} diff --git a/src/config.spin b/src/config.spin new file mode 100644 index 0000000..99abd8b --- /dev/null +++ b/src/config.spin @@ -0,0 +1,47 @@ +'****************************************************************************** +' Copyright (c) 2011, 2012, Dave Hein +' See end of file for terms of use. +'****************************************************************************** +CON + _clkmode = xinput + _xinfreq = $4e495053 ' SPIN + +OBJ + c : "clibsd" + sys : "sysdefs" + +PUB main(argc, argv) | i + + c.enter + + if argc <> 2 + c.printf0(string("usage: config hex_number\n")) + c.exit(1) + + c.sscanf1(long[argv][1], string("%x"), sys#config) + + c.exit(0) + +{{ ++-----------------------------------------------------------------------------+ +| TERMS OF USE: MIT License | ++-----------------------------------------------------------------------------+ +|Permission is hereby granted, free of charge, to any person obtaining a copy | +|of this software and associated documentation files (the "Software"), to deal| +|in the Software without restriction, including without limitation the rights | +|to use, copy, modify, merge, publish, distribute, sublicense, and/or sell | +|copies of the Software, and to permit persons to whom the Software is | +|furnished to do so, subject to the following conditions: | +| | +|The above copyright notice and this permission notice shall be included in | +|all copies or substantial portions of the Software. | +| | +|THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | +|IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | +|FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE | +|AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | +|LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,| +|OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE| +|SOFTWARE. | ++-----------------------------------------------------------------------------+ +}} diff --git a/src/copyall b/src/copyall new file mode 100644 index 0000000..6bf400d --- /dev/null +++ b/src/copyall @@ -0,0 +1,53 @@ +cp ../src/alias.spin . +cp ../src/cat.spin . +cp ../src/cd.spin . +cp ../src/chmod.spin . +cp ../src/config.spin . +cp ../src/cp.spin . +cp ../src/date.spin . +cp ../src/declare.spin . +cp ../src/diag.spin . +cp ../src/diff.spin . +cp ../src/dos2unix.spin . +cp ../src/echo.spin . +cp ../src/export.spin . +cp ../src/ted.spin . +cp ../src/grep.spin . +cp ../src/halt.spin . +cp ../src/head.spin . +cp ../src/history.spin . +cp ../src/kill.spin . +cp ../src/let.spin . +cp ../src/linefeed.spin . +cp ../src/loadi2c.spin . +cp ../src/loadvga.spin . +cp ../src/man.spin . +cp ../src/mkdir.spin . +cp ../src/more.spin . +cp ../src/mv.spin . +cp ../src/od.spin . +cp ../src/printenv.spin . +cp ../src/ps.spin . +cp ../src/pwd.spin . +cp ../src/rb.spin . +cp ../src/rb1.spin . +cp ../src/reboot.spin . +cp ../src/rm.spin . +cp ../src/sb.spin . +cp ../src/sb1.spin . +cp ../src/set.spin . +cp ../src/setret.spin . +cp ../src/shell.spin . +cp ../src/splash.spin . +cp ../src/splash0.spin . +cp ../src/stdout.spin . +cp ../src/tail.spin . +cp ../src/tar.spin . +cp ../src/touch.spin . +cp ../src/unalias.spin . +cp ../src/unix2dos.spin . +cp ../src/unset.spin . +cp ../src/vgatdemo.spin . +cp ../src/wc.spin . +cp ../src/_cloader.spin . +cp ../src/_loader.spin . diff --git a/src/cp.spin b/src/cp.spin new file mode 100644 index 0000000..1fb8766 --- /dev/null +++ b/src/cp.spin @@ -0,0 +1,118 @@ +'****************************************************************************** +' Copyright (c) 2011, 2012, Dave Hein +' See end of file for terms of use. +'****************************************************************************** +CON + _clkmode = xinput + _xinfreq = $4e495053 ' SPIN + + ATTR_DIRECTORY = %10000 + ATTR_EXECUTE = %100000 + ATTR_READ_ONLY = %1 + +OBJ + c : "clibsd" + g : "glob" + r : "resolve" + +DAT + globdata long 0 + count long 0 + str long 0[25] + +PUB main | argc, argv, argi, num, infile, outfile, buf[25], path, attrib, target, attrib1, fname + c.enter + + if argc < 3 + c.printf0(string("usage: cp file1 [file2 ...] path\n")) + c.exit(6) + path := long[argv][argc-1] + + ' Get the destination file attribute + if g.iswild(path) + c.printf1(string("%s contains wildcard characters\n"), path) + c.exit(1) + r.resolve_path(path, @str) + if strcomp(@str, string("/")) + attrib := ATTR_DIRECTORY + else + attrib := c.getmod(path) + if attrib == -1 + attrib := 0 + + if attrib & ATTR_READ_ONLY + c.printf1(string("%s is read only\n"), path) + c.exit(1) + attrib &= ATTR_DIRECTORY + if attrib == 0 and (argc > 3 or g.iswild(long[argv][1])) + c.printf1(string("%s is not a directory\n"), path) + c.exit(2) + + target := @buf + repeat argi from 1 to argc-2 + globdata := 0 + repeat while (fname := g.glob(long[argv][argi], @globdata, @str)) + if count++ and not attrib + c.printf1(string("%s is not a directory\n"), path) + g.exit(@globdata) + infile := c.fopen(fname, string("r")) + ifnot infile + c.printf1(string("Could not open input file %s\n"), fname) + g.exit(@globdata) + attrib1 := c.getmod(fname) + if attrib1 & ATTR_DIRECTORY + c.printf1(string("%s is a directory\n"), fname) + c.fclose(infile) + next + c.strcpy(target, path) + if attrib + c.strcat(target, string("/")) + c.strcat(target, GetFileName(fname)) + outfile := c.fopen(target, string("w")) + ifnot outfile + c.printf1(string("Could not open output file %s\n"), target) + c.fclose(infile) + c.exit(5) + c.chmod(target, attrib1) + repeat while (num := c.fread(@buf, 1, 100, infile)) > 0 + c.fwrite(@buf, 1, num, outfile) + c.fclose(infile) + c.fclose(outfile) + + c.exit(0) + +PRI GetFileName(path) | len + len := strsize(path) + if len =< 0 + return path + path += len + + repeat while len-- + if byte[--path] == "/" + return path + 1 + + return path + +{{ ++-----------------------------------------------------------------------------+ +| TERMS OF USE: MIT License | ++-----------------------------------------------------------------------------+ +|Permission is hereby granted, free of charge, to any person obtaining a copy | +|of this software and associated documentation files (the "Software"), to deal| +|in the Software without restriction, including without limitation the rights | +|to use, copy, modify, merge, publish, distribute, sublicense, and/or sell | +|copies of the Software, and to permit persons to whom the Software is | +|furnished to do so, subject to the following conditions: | +| | +|The above copyright notice and this permission notice shall be included in | +|all copies or substantial portions of the Software. | +| | +|THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | +|IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | +|FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE | +|AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | +|LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,| +|OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE| +|SOFTWARE. | ++-----------------------------------------------------------------------------+ +}} diff --git a/src/cserial.spin b/src/cserial.spin new file mode 100644 index 0000000..01530b8 --- /dev/null +++ b/src/cserial.spin @@ -0,0 +1,575 @@ +'****************************************************************************** +' Serial Driver for the C Function Library in Spin +' Author: Dave Hein +' Copyright (c) 2010 +' See end of file for terms of use. +'****************************************************************************** +'****************************************************************************** +' Revison History +' v1.0 - 4/2/2010 First official release +'****************************************************************************** +{{ + This is a modified version of Ghip Gracey's Full-Duplex Serial Driver. This + serial driver has the following new features: + + - Multiple serial ports may be started from any object or cog + - Any serial port is accessable from any object or cog using a device handle + - Transmit and receiver buffers can be different sizes + - Buffer sizes are defined by calling parameters + - Mode bit 4 enables the use of a lock to make the transmit multi-cog safe + + The original FullDuplexSerial routines are retained for compatibility. A + handle is maintained locally so that the caller does not have to provide the + buffers or handle for the first serial port. + + The enhanced methods use a handle, which is a pointer to a memory buffer that + contains the serial port's state information and transmit and receive buffers. + The size of the memory buffer is equal to header_size + rxsize + txsize in + bytes. The buffer must be long aligned. The transmit and receive buffer + sizes must be a power of 2. They can range anywhere from a value of 2 up to + the size of the available memory. +}} +CON +' Data structure byte offsets + ' long variables + tm_seconds = 0 + tm_clkticks = 4 + bit_ticks = 8 + ' word variables + rx_head = 12 + rx_tail = 14 + tx_head = 16 + tx_tail = 18 + rx_buffer = 20 + tx_buffer = 22 + rx_mask = 24 + tx_mask = 26 + ' byte variables + cog = 28 + lock = 29 + rx_pin = 30 + tx_pin = 31 + rxtx_mode = 32 + header_size = 36 + +' Data structure storage for the default serial port +DAT + handle1 long 0 + data_struct long 0[(header_size+16+16)/4] + +'' +''***************************************************************************** +''Original FullDuplexSerial routines for compatibility +''***************************************************************************** +PUB rxflush +'' Flush receive buffer + rxflush1(handle1) + +PUB rxcheck +'' Check if byte received (never waits) +'' returns -1 if no byte received, $00..$FF if byte + return rxcheck1(handle1) + +PUB rxtime(ms) +'' Wait ms milliseconds for a byte to be received +'' returns -1 if no byte received, $00..$FF if byte + return rxtime1(handle1, ms) + +PUB rx +'' Receive byte (may wait for byte) +'' returns $00..$FF + return rx1(handle1) + +PUB tx(txbyte) +'' Send byte (may wait for room in buffer) + tx1(handle1, txbyte) + +PUB str(stringptr) +'' Send string + str1(handle1, stringptr) + +PUB dec(value) +'' Print a decimal number + dec1(handle1, value) + +PUB hex(value, digits) +'' Print a hexadecimal number + hex1(handle1, value, digits) + +PUB bin(value, digits) +'' Print a binary number + bin1(handle1, value, digits) + +'' +''***************************************************************************** +''Enhanced routines +''***************************************************************************** +PUB rxflush1(handle) +'' Flush receive buffer + word[handle+rx_tail] := word[handle+rx_head] + +PUB rxcheck1(handle) : rxbyte | rx_tail1, rx_buffer1 +'' Check if byte received (never waits) +'' returns -1 if no byte received, $00..$FF if byte + rxbyte-- + rx_tail1 := word[handle+rx_tail] + if rx_tail1 <> word[handle+rx_head] + rx_buffer1 := word[handle+rx_buffer] + rxbyte := byte[rx_buffer1+rx_tail1] + word[handle+rx_tail] := (rx_tail1 + 1) & word[handle+rx_mask] + +PUB rxtime1(handle, ms) : rxbyte | t +'' Wait ms milliseconds for a byte to be received +'' returns -1 if no byte received, $00..$FF if byte + t := cnt + repeat until (rxbyte := rxcheck1(handle)) => 0 or (cnt - t) / (clkfreq / 1000) > ms + +PUB rx1(handle) : rxbyte +'' Receive byte (may wait for byte) +'' returns $00..$FF + repeat while (rxbyte := rxcheck1(handle)) < 0 + +PRI txchar(handle, txbyte) | tx_mask1, tx_buffer1, tx_head1, tx_head2 +'' Send byte (may wait for room in buffer) + tx_mask1 := word[handle+tx_mask] + tx_buffer1 := word[handle+tx_buffer] + tx_head1 := word[handle+tx_head] + tx_head2 := (tx_head1 + 1) & tx_mask1 + repeat until (word[handle+tx_tail] <> tx_head2) + if (byte[handle+rxtx_mode] & %100000) and (txbyte == 10) + txbyte := 13 + byte[tx_buffer1+tx_head1] := txbyte + word[handle+tx_head] := tx_head2 + + if byte[handle+rxtx_mode] & %1000 + rx1(handle) + +PUB tx1(handle, txbyte) | uselock, locknum +'' Send byte - Use lock if mode bit 4 is set + uselock := byte[handle+rxtx_mode] & %10000 + + if (uselock) + locknum := byte[handle+lock] - 1 + repeat until not lockset(locknum) + + txchar(handle, txbyte) + + if (uselock) + lockclr(locknum) + +PUB str1(handle, stringptr) | uselock, locknum, value +'' Send string - Use lock if mode bit 4 is set + uselock := byte[handle+rxtx_mode] & %10000 + + if (uselock) + locknum := byte[handle+lock] - 1 + repeat until not lockset(locknum) + + repeat strsize(stringptr) + txchar(handle, byte[stringptr++]) + + if (uselock) + lockclr(locknum) + +PUB dec1(handle, value) | i +'' Print a decimal number + if (value < 0) + tx1(handle, "-") + if (value == NEGX) + tx1(handle, "2") + value += 2_000_000_000 + value := -value + + i := 1_000_000_000 + repeat while (i > value and i > 1) + i /= 10 + repeat while (i > 0) + tx1(handle, value/i + "0") + value //= i + i /= 10 + +PUB hex1(handle, value, digits) +'' Print a hexadecimal number + value <<= (8 - digits) << 2 + repeat digits + tx(lookupz((value <-= 4) & $F : "0".."9", "A".."F")) + +PUB bin1(handle, value, digits) +'' Print a binary number + value <<= 32 - digits + repeat digits + tx((value <-= 1) & 1 + "0") + +PUB gethandle1 +'' Get the local handle + return handle1 + +PUB dbprintf0(fmtstr) + dbprintf(fmtstr, @fmtstr) + +PUB dbprintf1(fmtstr, arg1) + dbprintf(fmtstr, @arg1) + +PUB dbprintf2(fmtstr, arg1, arg2) + dbprintf(fmtstr, @arg1) + +PUB dbprintf3(fmtstr, arg1, arg2, arg3) + dbprintf(fmtstr, @arg1) + +PUB dbprintf4(fmtstr, arg1, arg2, arg3, arg4) + dbprintf(fmtstr, @arg1) + +PUB dbprintf5(fmtstr, arg1, arg2, arg3, arg4, arg5) + dbprintf(fmtstr, @arg1) + +PUB dbprintf6(fmtstr, arg1, arg2, arg3, arg4, arg5, arg6) + dbprintf(fmtstr, @arg1) + +PUB dbprintf(fmtstr, arglist) | arg, val, digits + arg := long[arglist] + arglist += 4 + repeat while (val := byte[fmtstr++]) + if (val == "%") + digits := 0 + repeat + case (val := byte[fmtstr++]) + "d" : dec(arg) + "x" : + ifnot digits + digits := 8 + hex(arg, digits) + "b" : + ifnot digits + digits := 32 + bin(arg, digits) + "s" : str(arg) + "0".."9": + digits := (digits * 10) + val - "0" + next + 0 : return + other: tx(val) + quit + arg := long[arglist] + arglist += 4 + elseif (val == "\") + case (val := byte[fmtstr++]) + "n" : tx(13) + 0 : return + other: tx(val) + else + tx(val) + +PUB settime(seconds, clkticks) | handle + handle := handle1 + long[handle + tm_seconds] := seconds + long[handle + tm_clkticks] := clkticks | $80000000 + repeat while long[handle + tm_clkticks] & $c0000000 + +PUB gettime(pclkticks) | handle + handle := handle1 + long[handle + tm_clkticks] := $40000000 + repeat while long[handle + tm_clkticks] & $c0000000 + long[pclkticks] := long[handle + tm_clkticks] + return long[handle + tm_seconds] + +'' +''***************************************************************************** +''Original FullDuplexSerial routines for compatibility +''***************************************************************************** +PUB start(rxpin, txpin, mode, baudrate) +'' Start serial driver - starts a cog +'' returns false if no cog available +'' +'' mode bit 0 = invert rx +'' mode bit 1 = invert tx +'' mode bit 2 = open-drain/source tx +'' mode bit 3 = ignore tx echo on rx +'' mode bit 4 = use lock +'' mode bit 5 = convert LF to CR on tx + return start1(@data_struct, rxpin, txpin, mode, baudrate, 256, 16) + +'' +''***************************************************************************** +''Enhanced routines +''***************************************************************************** +PUB start1(handle, rxpin, txpin, mode, baudrate, rxsize, txsize) : okay +'' Start serial driver - starts a cog +'' returns false if no cog available +'' +'' mode bit 0 = invert rx +'' mode bit 1 = invert tx +'' mode bit 2 = open-drain/source tx +'' mode bit 3 = ignore tx echo on rx +'' mode bit 4 = use lock +'' mode bit 5 = convert LF to CR on tx + if (handle1) + if (handle1 == handle) + stop1(handle) + else + handle1 := handle + wordfill(handle+rx_head, 0, 4) + long[handle+tm_clkticks] := clkfreq + cnt + long[handle+tm_seconds] := 0 + byte[handle+rx_pin] := rxpin + byte[handle+tx_pin] := txpin + byte[handle+rxtx_mode] := mode + long[handle+bit_ticks] := clkfreq / baudrate + word[handle+rx_buffer] := handle + header_size + word[handle+tx_buffer] := handle + header_size + rxsize + word[handle+rx_mask] := rxsize - 1 + word[handle+tx_mask] := txsize - 1 + if (mode & %10000) + okay := byte[handle+lock] := locknew + 1 + if (okay == 0) + return 0 + okay := byte[handle+cog] := cognew(@entry, handle) + 1 + +PUB stop1(handle) | cog1 +'' Stop serial driver - frees a cog + cog1 := byte[handle+cog] + if cog1 + cogstop(cog1 - 1) + longfill(handle, 0, header_size >> 2) + +DAT + +'*********************************** +'* Assembly language serial driver * +'*********************************** + + org +' +' +' Entry +' +entry mov t1, par + add t1, #rx_pin + rdbyte t2,t1 'get rx_pin + mov rxbitmask,#1 + shl rxbitmask,t2 + + add t1,#1 'get tx_pin + rdbyte t2,t1 + mov txbitmask,#1 + shl txbitmask,t2 + + add t1,#1 'get rxtx_mode + rdbyte rxtxmode,t1 + + mov t1, par + add t1, #bit_ticks 'get bit_ticks + rdlong bitticks,t1 + + mov t1, par + add t1, #tm_seconds 'get tmseconds + rdlong tmseconds,t1 + + mov t1, par + add t1, #tm_clkticks 'get tmclkticks + rdlong tmclkticks, #0 'get clkfreq + + rdlong tmclkticks,t1 + + mov t1, par + add t1, #rx_buffer 'get rx_buffer ptr + rdword rxbuff,t1 + + add t1,#2 'get tx_buffer ptr + rdword txbuff,t1 + + add t1,#2 'get rx_mask + rdword rxmask,t1 + + add t1,#2 'get tx_mask + rdword txmask,t1 + + test rxtxmode,#%100 wz 'init tx pin according to mode + test rxtxmode,#%010 wc + if_z_ne_c or outa,txbitmask + if_z or dira,txbitmask + + mov txcode,#transmit 'initialize multitasking addresses + mov clcode,#clock + +' +' +' Receive +' +receive jmpret rxcode,txcode 'run a chunk of transmit code, then return + + test rxtxmode,#%001 wz 'wait for start bit on rx pin + test rxbitmask,ina wc + if_z_eq_c jmp #receive + + mov rxbits,#9 'ready to receive byte + mov rxcnt,bitticks + shr rxcnt,#1 + add rxcnt,cnt + +:bit add rxcnt,bitticks 'ready next bit period + +:wait jmpret rxcode,txcode 'run a chuck of transmit code, then return + + mov t1,rxcnt 'check if bit receive period done + sub t1,cnt + cmps t1,#0 wc + if_nc jmp #:wait + + test rxbitmask,ina wc 'receive bit on rx pin + rcr rxdata,#1 + djnz rxbits,#:bit + + shr rxdata,#32-9 'justify and trim received byte + and rxdata,#$FF + test rxtxmode,#%001 wz 'if rx inverted, invert byte + if_nz xor rxdata,#$FF + + mov t1, par + add t1, #rx_head + rdword t2, t1 'save received byte and inc head + add t2,rxbuff + wrbyte rxdata,t2 + sub t2,rxbuff + add t2,#1 + and t2,rxmask + wrword t2,t1 + + jmp #receive 'byte done, receive next byte +' +' +' Transmit +' +transmit jmpret txcode,clcode 'run a chunk of clock code, then return + + mov t1,par 'check for head <> tail + add t1,#tx_head + rdword t2,t1 + add t1,#2 + rdword t3,t1 + cmp t2,t3 wz + if_z jmp #transmit + + add t3,txbuff 'get byte and inc tail + rdbyte txdata,t3 + sub t3,txbuff + add t3,#1 + and t3,txmask + wrword t3,t1 + + or txdata,#$100 'ready byte to transmit + shl txdata,#2 + or txdata,#1 + mov txbits,#11 + mov txcnt,cnt + +:bit test rxtxmode,#%100 wz 'output bit on tx pin according to mode + test rxtxmode,#%010 wc + if_z_and_c xor txdata,#1 + shr txdata,#1 wc + if_z muxc outa,txbitmask + if_nz muxnc dira,txbitmask + add txcnt,bitticks 'ready next cnt + +:wait jmpret txcode,clcode 'run a chunk of clock code, then return + + mov t1,txcnt 'check if bit transmit period done + sub t1,cnt + cmps t1,#0 wc + if_nc jmp #:wait + + djnz txbits,#:bit 'another bit to transmit? + + jmp #transmit 'byte done, transmit next byte + +clock jmpret clcode,rxcode 'run the receive task + mov t2, par + add t2, #tm_clkticks + rdlong t1, t2 + shl t1, #1 wc + if_c jmp #setclock + shl t1, #1 wc + if_c jmp #getclock + mov t1, cnt + sub t1, tmclkticks + shl t1, #1 wc + if_c jmp #clock + add tmseconds, #1 + rdlong t1, #0 + add tmclkticks, t1 + jmp #clock + +setclock shl t1, #3 + shr t1, #4 + rdlong tmclkticks, #0 + add tmclkticks, cnt + sub tmclkticks, t1 + mov t2, par + add t2, #tm_seconds + rdlong tmseconds, t2 + mov t2, par + add t2, #tm_clkticks + wrlong t1, t2 + jmp #clock + +getclock rdlong t1, #0 + mov t2, cnt + add t2, t1 + sub t2, tmclkticks + mov t3, par + add t3, #tm_seconds + wrlong tmseconds, t3 + mov t3, par + add t3, #tm_clkticks + wrlong t2, t3 + jmp #clock + +' +' +' Uninitialized data +' +t1 res 1 +t2 res 1 +t3 res 1 + +rxtxmode res 1 +bitticks res 1 + +rxbitmask res 1 +rxbuff res 1 +rxdata res 1 +rxbits res 1 +rxcnt res 1 +rxcode res 1 +rxmask res 1 + +txbitmask res 1 +txbuff res 1 +txdata res 1 +txbits res 1 +txcnt res 1 +txcode res 1 +txmask res 1 + +tmclkticks res 1 +tmseconds res 1 +clcode res 1 + + fit $100 + +{{ +┌──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┐ +│ TERMS OF USE: MIT License │ +├──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┤ +│Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation │ +│files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, │ +│modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software│ +│is furnished to do so, subject to the following conditions: │ +│ │ +│The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.│ +│ │ +│THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE │ +│WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR │ +│COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, │ +│ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. │ +└──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┘ +}} \ No newline at end of file diff --git a/src/date.spin b/src/date.spin new file mode 100644 index 0000000..3636fcb --- /dev/null +++ b/src/date.spin @@ -0,0 +1,156 @@ +'****************************************************************************** +' Copyright (c) 2013, Dave Hein +' See end of file for terms of use. +'****************************************************************************** + +CON + _clkmode = xinput + _xinfreq = $4e495053 ' SPIN + +OBJ + ut : "unixtime" + c : "clibsd" + sys : "sysdefs" + +con + CYCLE0 = sys#cycle0 + SECONDS = sys#unixtime + TIMEZONE = sys#timezone + + UNIX_SPEC = 0 + HHMM_SPEC = 1 + TIME_SPEC = 2 + DATE_SPEC = 3 + +pub main(argc, argv) | unixtime, date, time, hour, minute, second, year, month, day, cycle, temp, tz + c.enter + tz := long[TIMEZONE] + if argc == 1 + unixtime := long[SECONDS] + date := ut.date(unixtime, tz) + time := ut.time(unixtime, tz) + year := word[@date][1] + month := byte[@date][1] + day := byte[@date][0] + hour := word[@time][1] + minute := byte[@time][1] + second := byte[@time][0] + c.printf3(string("%2.2d/%2.2d/%2.2d "), year, month, day) + c.printf3(string("%2.2d:%2.2d:%2.2d\n"),hour, minute, second) + c.exit(0) + + if argc == 2 and strcomp(long[argv][1], string("-s")) + c.printf1(string("%d\n"), long[SECONDS]) + c.exit(0) + + if (argc == 3 or argc == 4) and strcomp(long[argv][1], string("-s")) + result := CheckString(long[argv][2]) + if result == UNIX_SPEC + if argc == 4 + usage + c.sscanf1(long[argv][2], string("%d"), @unixtime) + cycle := cnt + else + if result == DATE_SPEC + c.sscanf3(long[argv][2], string("%d %d %d"), @year, @month, @day) + elseif result == TIME_SPEC + c.sscanf3(long[argv][2], string("%d %d %d"), @hour, @minute, @second) + else + c.sscanf2(long[argv][2], string("%d %d"), @hour, @minute) + second := 0 + if argc == 4 + temp := CheckString(long[argv][3]) + if temp == UNIX_SPEC or temp == result + usage + if temp == DATE_SPEC + c.sscanf3(long[argv][3], string("%d %d %d"), @year, @month, @day) + cycle := long[CYCLE0] + elseif temp == TIME_SPEC + c.sscanf3(long[argv][3], string("%d %d %d"), @hour, @minute, @second) + cycle := cnt + else + c.sscanf2(long[argv][3], string("%d %d"), @hour, @minute) + second := 0 + cycle := cnt + else + unixtime := long[SECONDS] + if result == DATE_SPEC + time := ut.time(unixtime, tz) + hour := word[@time][1] + minute := byte[@time][1] + second := byte[@time][0] + else + date := ut.date(unixtime, tz) + year := word[@date][1] + month := byte[@date][1] + day := byte[@date][0] + cycle := cnt + unixtime := ut.unix_time(year, month, day, hour, minute, second, tz) + long[CYCLE0] := cycle + long[SECONDS] := unixtime + save_sysparm + c.exit(0) + + usage + c.exit(0) + +pub CheckString(str) | slashcnt, coloncnt + slashcnt := 0 + coloncnt := 0 + repeat while byte[str] + if byte[str] == "/" + slashcnt++ + elseif byte[str] == ":" + coloncnt++ + str++ + if slashcnt == 2 and coloncnt == 0 + result := DATE_SPEC + elseif slashcnt == 0 and coloncnt == 1 + result := HHMM_SPEC + elseif slashcnt == 0 and coloncnt == 2 + result := TIME_SPEC + elseif slashcnt == 0 and coloncnt == 0 + result := UNIX_SPEC + else + usage + +pub usage + c.printf0(string("usage: date [-s [yyyy/mm/dd] [hh:mm[:ss]]]\n")) + c.exit(1) + +pub save_sysparm | outfile, ptr + outfile := c.fopen(string("/_sysparm"), string("w")) + ifnot outfile + c.printf0(string("Can't open /_sysparm\n")) + return + c.fprintf1(outfile, string("%d\n"), long[sys#unixtime]) + c.fprintf2(outfile, string("%d %d 0\n"), long[sys#timezone], long[sys#config]) + c.fprintf0(outfile, string("0 0 0\n")) + c.fprintf0(outfile, string("GPWD /\n")) + c.fprintf0(outfile, string("LSCRIPT_FILE /bin/_shellrc\n")) + c.fprintf0(outfile, string("P# 0\n")) + c.fclose(outfile) + +{{ ++-----------------------------------------------------------------------------+ +| TERMS OF USE: MIT License | ++-----------------------------------------------------------------------------+ +|Permission is hereby granted, free of charge, to any person obtaining a copy | +|of this software and associated documentation files (the "Software"), to deal| +|in the Software without restriction, including without limitation the rights | +|to use, copy, modify, merge, publish, distribute, sublicense, and/or sell | +|copies of the Software, and to permit persons to whom the Software is | +|furnished to do so, subject to the following conditions: | +| | +|The above copyright notice and this permission notice shall be included in | +|all copies or substantial portions of the Software. | +| | +|THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | +|IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | +|FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE | +|AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | +|LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,| +|OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE| +|SOFTWARE. | ++-----------------------------------------------------------------------------+ +}} diff --git a/src/declare.spin b/src/declare.spin new file mode 100644 index 0000000..7d6c9e6 --- /dev/null +++ b/src/declare.spin @@ -0,0 +1,158 @@ +'****************************************************************************** +' Copyright (c) 2013 Dave Hein +' See end of file for terms of use. +'****************************************************************************** +CON + _clkmode = xinput + _xinfreq = $4e495053 ' SPIN + + DOUBLE_QUOTE = $22 + +DAT + printflag long 0 + globalflag long 0 + +OBJ + c : "clibsd" + sys : "sysdefs" + vs : "varsubs" + +PUB main(argc, argv) | ptr, argi + + c.enter + + ' Check option flags + argi := 1 + repeat while argi < argc and byte[long[argv][argi]] == "-" + if strcomp(long[argv][argi], string("-p")) + printflag := 1 + elseif strcomp(long[argv][argi], string("-x")) + globalflag := 1 + else + usage + argi++ + + ' Print if no other parms + if argi == argc + if globalflag + PrintVars(1) + elseif printflag + PrintVars(2) + else + PrintVars(0) + c.exit(0) + + ' Print if -p was specified + if printflag + repeat while argi < argc + ptr := vs.FindVar(long[argv][argi]) + ifnot ptr + c.printf1(string("%s not found\n"), long[argv][argi]) + else + PrintVar(ptr, 2) + argi++ + c.exit(0) + + repeat while argi < argc + ProcessParm(long[argv][argi]) + argi++ + + c.exit(0) + +PUB ProcessParm(name) | ptr, len1, len2, space, value, noequal + + ' Check for "=" and get value + value := vs.FindChar(name, "=") + ifnot byte[value] + noequal := 1 + else + noequal := 0 + byte[value++] := 0 + + ' Check if variable already exists + if (ptr := vs.FindVar(name)) + if globalflag and noequal + byte[ptr] := "0" + return + if byte[ptr] == "0" + globalflag := 1 + ptr := vs.RemoveEntry(ptr) + else + ptr := vs.FindEnd + + ' Check if space is available + space := sys#environ_vars_end - ptr + 1 + len1 := strsize(name) + len2 := strsize(value) + if space - len1 - len2 - 4 < 50 + c.printf2(string("space = %d, need = %d\n"), space, len1 + len2 + 4) + if (space < len1 + len2 + 4) + c.printf0(string("Not enough variable space\n")) + c.exit(1) + + ' Add variable + if globalflag + byte[ptr++] := VS#GLOBAL_FLAG + else + byte[ptr++] := VS#LOCAL_FLAG + c.strcpy(ptr, name) + ptr += len1 + 1 + c.strcpy(ptr, value) + ptr += len2 + 1 + byte[ptr] := 0 + +PUB usage + c.printf0(string("usage: declare [-x] [-p] [var[=[value]]...]\n")) + c.exit(1) + +PUB PrintVars(mode) | ptr + ptr := sys#environ_vars + repeat while byte[ptr] + ptr := PrintVar(ptr, mode) + +PUB PrintVar(ptr, mode) | vartype, name, value + vartype := byte[ptr++] + name := ptr + ptr += strsize(ptr) + 1 + value := ptr + ptr += strsize(ptr) + 1 + if mode + if vartype == VS#GLOBAL_FLAG + c.printf2(string("declare -x %s=", DOUBLE_QUOTE, "%s", DOUBLE_QUOTE, "\n"), name, value) + elseif mode == 2 and vartype == VS#LOCAL_FLAG + c.printf2(string("declare -- %s=", DOUBLE_QUOTE, "%s", DOUBLE_QUOTE, "\n"), name, value) + elseif vartype == VS#GLOBAL_FLAG or vartype == VS#LOCAL_FLAG + if ContainsBlank(value) + c.printf2(string("%s=", DOUBLE_QUOTE, "%s", DOUBLE_QUOTE, "\n"), name, value) + else + c.printf2(string("%s=%s\n"), name, value) + return ptr + +PUB ContainsBlank(ptr) + repeat while byte[ptr] + if byte[ptr++] == " " + return 1 + +{{ ++-----------------------------------------------------------------------------+ +| TERMS OF USE: MIT License | ++-----------------------------------------------------------------------------+ +|Permission is hereby granted, free of charge, to any person obtaining a copy | +|of this software and associated documentation files (the "Software"), to deal| +|in the Software without restriction, including without limitation the rights | +|to use, copy, modify, merge, publish, distribute, sublicense, and/or sell | +|copies of the Software, and to permit persons to whom the Software is | +|furnished to do so, subject to the following conditions: | +| | +|The above copyright notice and this permission notice shall be included in | +|all copies or substantial portions of the Software. | +| | +|THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | +|IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | +|FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE | +|AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | +|LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,| +|OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE| +|SOFTWARE. | ++-----------------------------------------------------------------------------+ +}} diff --git a/src/diag.spin b/src/diag.spin new file mode 100644 index 0000000..38a028b --- /dev/null +++ b/src/diag.spin @@ -0,0 +1,361 @@ +'****************************************************************************** +' Copyright (c) 2011, 2012, 2013, Dave Hein +' See end of file for terms of use. +'****************************************************************************** +CON + _clkmode = xinput + _xinfreq = $4e495053 ' SPIN + + CHECKWORD = $dead1eaf + NEW_LINE = 10 + eeprom_pin = 28 + +OBJ + c : "clibsd" + mem : "cmalloc" + ser : "cserial" + eeprom : "pasm_i2c_driver" + fsrwx : "fsrwx" + sys : "sysdefs" + vs : "varsubs" + g : "glob" + +DAT + badflag long 0 + buffer byte 0[100] + strbuf byte 0[100] + i2c_cog long 0 + nullstr long 0 + +DAT + tokens long 0[20] + commands byte + byte "dumpm", 0, "peekm", 0, "pokem", 0 + byte "stack", 0, "malloc", 0, "help", 0, "exit", 0 + byte "dump", 0, "peek", 0, "poke", 0, "cd", 0, "echo", 0, "pwd", 0, 0 + i2cmsg byte "The I2C driver is not loaded. Run loadi2c\n", 0 + +PUB main(argc, argv) | i, blocklongs, num, index, outfile, infile, startaddr, endaddr, TopAddr, TopObjList + c.enter + SetShell(string("/bin/diag")) + i2c_cog := eeprom.Initialize(eeprom_pin) + 'c.printf1(string("long[sys#i2c_cog] = %8.8x\n"), long[sys#i2c_cog]) + REPEAT + c.CloseRedirect + + c.printf0(STRING("diag> ")) + c.gets(@buffer) + argc := tokenize(@buffer, @tokens) + argc := Redirection(argc, @tokens) + + IF (argc == 0) + NEXT + + num := 0 + index := @commands + repeat while byte[index] + if c.strcmp(tokens[0], index) == 0 + quit + index += strsize(index) + 1 + num++ + + case num + 0 : + c.sscanf1(tokens[1], STRING("%x"), @index) + c.sscanf1(tokens[2], STRING("%x"), @num) + DumpMemory(index, num) + 1 : + c.sscanf1(tokens[1], STRING("%x"), @index) + c.printf2(STRING("Memory[%04x] = %02x\n"), index, byte[index]) + 2 : + c.sscanf1(tokens[1], STRING("%x"), @index) + c.sscanf1(tokens[2], STRING("%x"), @num) + byte[index] := num + 3: + c.CheckStackSpace '(argc, @tokens) + 4: + c.CheckMallocSpace + 5: + if (infile := c.fopen(string("/manpages/diaghelp.txt"), string("r"))) + repeat while c.fgets(@buffer, 100, infile) + c.fputs(@buffer, c.getstdout) + c.fclose(infile) + 6: + exit(0) + 7 : + ifnot i2c_cog + c.printf0(@i2cmsg) + next + c.sscanf1(tokens[1], STRING("%x"), @index) + c.sscanf1(tokens[2], STRING("%x"), @num) + repeat i from 0 to num - 1 + if ((i & 15) == 0) + c.printf1(STRING("\n%05x:"), i + index) + c.printf1(STRING(" %02x"), eeprom.ReadByte(28, $A0, i + index)) + c.printf0(STRING("\n")) + 8 : + ifnot i2c_cog + c.printf0(@i2cmsg) + next + c.sscanf1(tokens[1], STRING("%x"), @index) + c.printf2(STRING("EEPROM[%04x] = %02x\n"), index, eeprom.ReadByte(28, $A0, index)) + 9 : + ifnot i2c_cog + c.printf0(@i2cmsg) + next + c.sscanf1(tokens[1], STRING("%x"), @index) + c.sscanf1(tokens[2], STRING("%x"), @num) + eeprom.WriteByte(28, $A0, index, num) + repeat while eeprom.WriteWait(28, $A0, index) + 10 : + ChangeDirectory(argc, @tokens) + 11 : + Echo(argc, @tokens) + 12 : + PrintWD(argc, @tokens) + other: + Execute(argc, @tokens) + + exit(0) + +PUB SetShell(name) | infile, handle + infile := c.fopen(name, string("r")) + if (infile) + handle := long[infile][c#stream_handle] + long[sys#shell_size] := fsrwx.hget_filesize(handle) + long[sys#shell_sector] := fsrwx.hget_first_sector(handle) + c.fclose(infile) + +PUB exit(val) + SetShell(string("/bin/shell")) + cogstop(i2c_cog-1) + c.exit(val) + +PUB Execute(argc, argv) | target[20] + 'c.printf0(string("Execute\n")) + 'PrintArgv(argc, argv) + 'c.printf1(string("Trace %d\n"), 9) + result := FindExecTarget(long[argv], @target) + 'c.printf1(string("Trace %d\n"), 20) + if result => 2 and result =< 4 + long[argv] := @target + if result == 4 + c.printf0(string("Can't execute stand-alone files\n")) + return + c.run(argc, argv, result - 2) + c.printf1(string("Could not run %s\n"), long[argv]) + elseif result == 1 + c.printf0(string("Can't execute script files\n")) + +PUB SkipChar(ptr, val) + repeat while byte[ptr] + if byte[ptr] <> val + quit + ptr++ + return ptr + +PUB FindChar(ptr, val) + repeat while byte[ptr] + if byte[ptr] == val + quit + ptr++ + return ptr + +PUB PrintWD(argc, argv) + if c.OpenRedirect '(argc, argv) + return 1 + c.getcwd(@buffer, 100) + c.printf1(string("%s\n"), @buffer) + +PUB ChangeDirectory(argc, argv) | path[25] + if c.OpenRedirect '(argc, argv) + return 1 + if argc > 2 + c.printf0(string("cd [path]\n")) + return 1 + if argc == 1 + c.strcpy(@path, string("/")) + else + c.resolve_path(long[argv][1], @path) + ifnot strcomp(@path, string("/")) + ifnot c.getmod(@path) & %10000 + c.printf1(string("%s is not a directory\n"), @path) + return 1 + if c.chdir(@path) + c.printf1(string("Could not find path %s\n"), @path) + return 2 + vs.SaveVar(string("PWD"), @path, vs#GLOBAL_FLAG) + +PUB Echo(argc, argv) | i, ptr, space, globdata, str[25] + if c.OpenRedirect '(argc, argv) + return 1 + if argc > 1 + space := 0 + repeat i from 1 to argc - 1 + globdata := 0 + repeat while (ptr := g.glob(long[argv][i], @globdata, @str)) + c.printf2(string("%s%s"), @space, ptr) + space := " " + c.printf0(string("\n")) + +PUB FindExecTarget(fname, pathstr) | temp, infile, typestr[8] + 'c.printf1(string("FindExecTarget %s\n"), fname) + c.strcpy(pathstr, fname) + 'c.printf1(string("pathstr = %s\n"), pathstr) + 'c.CheckStackSpace + 'c.CheckMallocSpace + 'c.printf1(string("Trace %d\n"), 10) + repeat 2 + 'c.printf1(string("Trace %d\n"), 11) + 'c.printf0(string("repeat\n")) + if (infile := c.fopen(pathstr, string("r"))) + 'c.printf1(string("infile nonzero -- %d\n"), infile) + quit + 'c.printf1(string("infile zero -- %d\n"), infile) + temp := FindChar(pathstr, "/") + if byte[temp] + quit + c.strcpy(pathstr, string("/bin/")) + c.strcat(pathstr, fname) + + 'c.printf1(string("infile equals %d\n"), infile) + 'c.printf1(string("Trace %d\n"), 12) + + ifnot infile + c.printf1(string("1Could not open %s\n"), fname) + return + + longfill(@typestr, 0, 8) + c.fread(@typestr, 1, 32, infile) + c.fclose(infile) + + 'c.printf1(string("Trace %d\n"), 13) + if typestr == $4e495053 ' SPIN + result := 2 + elseif typestr == $50504143 ' CAPP + result := 3 + elseif typestr => 80_000_000 and typestr =< 120_000_000 ' Stand-alone + result := 4 + elseifnot c.strncmp(@typestr, string("#shell"), 6) + 'c.printf1(string("%s is a shell script\n"), fname) + result := 1 + else + c.printf1(string("%s is not an executable file\n"), fname) + +PUB Redirection(argc, argv) | i, argc1, ptr, char, outptr, inptr, outstr + if argc =< 0 + return argc + i := 1 + argc1 := 1 + inptr := @nullstr + outptr := @nullstr + repeat while i < argc + ptr := long[argv][i] + char := byte[ptr++] + if char == "<" + if byte[ptr] + inptr := ptr + elseif ++i < argc + inptr := long[argv][i] + vs.SaveVar(string("<"), inptr, vs#LOCAL_FLAG) + elseif char == ">" + if byte[ptr] == ">" + ptr++ + outstr := STRING(">>") + else + outstr := STRING(">") + if byte[ptr] + outptr := ptr + elseif ++i < argc + outptr := long[argv][i] + vs.SaveVar(outstr, outptr, vs#LOCAL_FLAG) + elseif char + long[argv][argc1++] := long[argv][i] + i++ + 'long[argv][argc1] := inptr + 'long[argv][argc1+1] := outptr + return argc1 + +' This routine extracts tokens from the input string that +' are seperated by at least one space character +PUB tokenize(ptr, argv) | num + num := 0 + repeat + ptr := SkipChar(ptr, " ") + ifnot byte[ptr] + quit + num++ + if byte[ptr] == $22 + long[argv] := ++ptr + ptr := FindChar(ptr, $22) + else + long[argv] := ptr + ptr := FindChar(ptr, " ") + argv += 4 + IF (BYTE[ptr] == 0) + QUIT + BYTE[ptr++] := 0 + RETURN num + +PUB DumpMemory(address, num) | i, ptr + ptr := address + i := 0 + REPEAT WHILE (i < num) + IF ((i & 15) == 0) + c.printf1(STRING("\n%05x:"), i + address) + c.printf1(STRING(" %02x"), BYTE[ptr][i]) + i++ + c.printf0(STRING("\n")) +{ +PUB CheckStackSpace(argc, argv1) | addr, laststackaddr, entryptr, cognum, AppAddr, dbase + cognum := cogid + + dbase := word[10] + + laststackaddr := dbase + 800 - 4 + repeat addr from laststackaddr to 0 step 4 + if long[addr] <> sys#CHECKWORD + quit + result++ + c.printf1(string("The last %d longs have not been used on the stack\n"), result) + +PUB CheckMallocSpace | ptr, numalloc, numfree + numalloc := 0 + numfree := 0 + ptr := long[sys#malloclist] + c.printf0(string("MallocList\n")) + repeat while(ptr) + c.printf2(string("addr = %04x size = %d\n"), ptr, word[ptr][1]) + numalloc += word[ptr][1] + ptr := word[ptr] + ptr := long[sys#memfreelist] + c.printf0(string("FreeList\n")) + repeat while(ptr) + c.printf2(string("addr = %04x size = %d\n"), ptr, word[ptr][1]) + numfree += word[ptr][1] + ptr := word[ptr] + c.printf2(string("%d bytes allocated, %d bytes free\n"), numalloc, numfree) +} +{{ ++-----------------------------------------------------------------------------+ +| TERMS OF USE: MIT License | ++-----------------------------------------------------------------------------+ +|Permission is hereby granted, free of charge, to any person obtaining a copy | +|of this software and associated documentation files (the "Software"), to deal| +|in the Software without restriction, including without limitation the rights | +|to use, copy, modify, merge, publish, distribute, sublicense, and/or sell | +|copies of the Software, and to permit persons to whom the Software is | +|furnished to do so, subject to the following conditions: | +| | +|The above copyright notice and this permission notice shall be included in | +|all copies or substantial portions of the Software. | +| | +|THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | +|IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | +|FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE | +|AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | +|LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,| +|OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE| +|SOFTWARE. | ++-----------------------------------------------------------------------------+ +}} diff --git a/src/diff.spin b/src/diff.spin new file mode 100644 index 0000000..6883758 --- /dev/null +++ b/src/diff.spin @@ -0,0 +1,246 @@ +'****************************************************************************** +' Copyright (c) 2011, 2012, Dave Hein +' See end of file for terms of use. +'****************************************************************************** +CON + _clkmode = xinput + _xinfreq = $4e495053 ' SPIN + +OBJ + c : "clibsd" + sm : "smalloc" + +CON + s_next = 0 ' Word index for the linked-list next pointer + s_str = 2 ' Word index for the string data + NUMLINES = 1000000 ' Set NUMLINES to a large value so it doesn't limit + BUFFER_SIZE = 2000 ' Define a buffer size of 2000 bytes + +VAR + long buffer[BUFFER_SIZE / 4] ' Ensure that the buffer is long-aligned + byte linebuf[200] ' This is the input line buffer + +DAT + ' This flag indicates if there is a line in linebuf -- 1 for file1 and 2 for file2 + pending long 0 + + ' List 1 + head1 long 0 + tail1 long 0 + level1 long 0 + eofflag1 long 0 + + ' List 2 + head2 long 0 + tail2 long 0 + level2 long 0 + eofflag2 long 0 + +' This program implements the standard UNIX diff function between two files +PUB main(argc, argv) | retval1, retval2, infile1, infile2, line1, line2, num1, num2, dashflag, next_1 + c.enter + line1 := 0 + line2 := 0 + if (argc <> 3) + usage + ' Open file 1 + if (not(infile1 := c.fopen(long[argv][1], string("r")))) + c.printf1(string("Could not open %s\n"), word[argv][1]) + c.exit(1) + ' Open file 2 + if (not(infile2 := c.fopen(long[argv][2], string("r")))) + c.printf1(string("Could not open %s\n"), word[argv][2]) + c.fclose(infile1) + c.exit(2) + ' Initialize the line buffer + sm.smallocinit(@buffer, BUFFER_SIZE) + ' Begin main loop + repeat + ' Read lines into list1 and list2 + FillBuffers(infile1, infile2) + ' Check if list1 and list2 are empty + if (not head1 and not head2) + quit + ' Compare the top line on the two lists + if (not head1 or not head2 or c.strcmp((@word[head1][s_str]),(@word[head2][s_str]))) + ' Find matching lines if the top lines do not match + FindMatch(@num1, @num2) + ' Handle the case where the top line on list2 matches a later line on list1 + if (num1 > 0 and num2 == 0) + dashflag := 0 + c.printf1(string("%d"), line1 + 1) + if (num1 > 1) + c.printf1(string(",%d"), line1 + num1) + c.printf1(string("d%d\n"), line2) + ' Handle the case where the top line on list1 matches a later line on list2 + elseif (num2 > 0 and num1 == 0) + dashflag := 0 + c.printf2(string("%dd%d"), line1, line2 + 1) + if (num2 > 1) + c.printf1(string(",%d"), line2 + num2) + c.printf0(string("\n")) + ' Otherwise, handle the case where later lines on list1 and list2 match + else + dashflag := 1 + c.printf1(string("%d"), line1 + 1) + if (num1 > 1) + c.printf1(string(",%d"), line1 + num1) + c.printf1(string("c%d"), line2 + 1) + if (num2 > 1) + c.printf1(string(",%d"), line2 + num2) + c.printf0(string("\n")) + ' Print lines from list1 and free them + if (num1 > 0) + line1 += num1 + repeat while (num1-- > 0) + c.printf1(string("< %s"),(@word[head1][s_str])) + next_1 := word[head1]{s_next} + sm.sfree(head1) + head1 := next_1 + level1-- + ' Print dashes if we are printing lines from both lists + if (dashflag) + c.printf0(string("---\n")) + ' Print lines from list2 and free them + if (num2 > 0) + line2 += num2 + repeat while (num2-- > 0) + c.printf1(string("> %s"),(@word[head2][s_str])) + next_1 := word[head2]{s_next} + sm.sfree(head2) + head2 := next_1 + level2-- + ' Otherwise, don't print the top lines + else + ' Free the top line from list1 + if (head1) + line1++ + next_1 := word[head1]{s_next} + sm.sfree(head1) + head1 := next_1 + level1-- + ' Free the top line from list2 + if (head2) + line2++ + next_1 := word[head2]{s_next} + sm.sfree(head2) + head2 := next_1 + level2-- + ' Clear the tails if lists are empty + if (not head1) + tail1 := 0 + if (not head2) + tail2 := 0 + ' Close the files before exiting + c.fclose(infile1) + c.fclose(infile2) + c.exit(0) + +' Print out the usage statement and exit +PUB usage + c.printf0(string("usage: diff file1 file2\n")) + c.exit(1) + +' This routine reads lines from the input files and adds them +' to list1 and list2 until one of the following occurs: +' - The end of file is reached on both files +' - There are NUMLINES in the buffer for both files +' - There is no more room left in the buffer to add a line +PUB FillBuffers(infile1, infile2) | next_1 + repeat + ' Quit if EOF or there are NUMLINES on the list for both files + if ((eofflag1 or level1 => NUMLINES) and(eofflag2 or level2 => NUMLINES)) + quit + ' Read a line from an input file if one is not pending + if (not pending) + ' Read from file 1 if list1 has fewer lines than list2 + if (not eofflag1 and (level1 =< level2 or eofflag2)) + if (c.fgets(@linebuf, 200, infile1) =< 0) + eofflag1 := 1 + next + pending := 1 + ' Otherwise, read from file 2 + elseif (not eofflag2) + if (c.fgets(@linebuf, 200, infile2) =< 0) + eofflag2 := 1 + next + pending := 2 + ' Quit if no lines are pending - EOF on both files + if (not pending) + quit + ' Attempt to allocate a line buffer, and quit if there is no room + next_1 := sm.smalloc(strsize(@linebuf) + 1) + if (not next_1) + quit + c.strcpy((@word[next_1][s_str]), @linebuf) + ' Add the line to list1 + if (pending == 1) + if (tail1) + word[tail1]{s_next} := next_1 + else + head1 := next_1 + tail1 := next_1 + level1++ + ' Add the line to list2 + elseif (pending == 2) + if (tail2) + word[tail2]{s_next} := next_1 + else + head2 := next_1 + tail2 := next_1 + level2++ + pending := 0 + +' This routine searches for a line that matches in list1 and list2. It returns +' the smallest number of lines between list1 and list2 where a match is found. +PUB FindMatch(pnum1, pnum2) | minsum, num1, minnum1, count1, num2, minnum2, count2, next1, next2 + minsum := 1000000 + next1 := head1 + next2 := head2 + minnum1 := level1 + minnum2 := level2 + num1 := 0 + count1 := level1 + repeat while (count1 > 0) + num2 := 0 + count2 := level2 + next2 := head2 + repeat while (count2 > 0) + if (c.strcmp((@word[next1][s_str]),(@word[next2][s_str])) == 0) + if (num2 + num1 < minsum) + minnum2 := num2 + minnum1 := num1 + minsum := num1 + num2 + quit + next2 := word[next2]{s_next} + count2-- + num2++ + next1 := word[next1]{s_next} + count1-- + num1++ + long[pnum1] := minnum1 + long[pnum2] := minnum2 + +{{ ++-----------------------------------------------------------------------------+ +| TERMS OF USE: MIT License | ++-----------------------------------------------------------------------------+ +|Permission is hereby granted, free of charge, to any person obtaining a copy | +|of this software and associated documentation files (the "Software"), to deal| +|in the Software without restriction, including without limitation the rights | +|to use, copy, modify, merge, publish, distribute, sublicense, and/or sell | +|copies of the Software, and to permit persons to whom the Software is | +|furnished to do so, subject to the following conditions: | +| | +|The above copyright notice and this permission notice shall be included in | +|all copies or substantial portions of the Software. | +| | +|THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | +|IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | +|FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE | +|AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | +|LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,| +|OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE| +|SOFTWARE. | ++-----------------------------------------------------------------------------+ +}} diff --git a/src/dos2unix.spin b/src/dos2unix.spin new file mode 100644 index 0000000..9f5bbe4 --- /dev/null +++ b/src/dos2unix.spin @@ -0,0 +1,94 @@ +'****************************************************************************** +' Copyright (c) 2011, 2012, Dave Hein +' See end of file for terms of use. +'****************************************************************************** +CON + _clkmode = xinput + _xinfreq = $4e495053 ' SPIN + +OBJ + c : "clibsd" + +DAT + buffer long 0[50] + +PUB main(argc, argv) | infile, outfile, len, ptr + + c.enter + + if argc <> 2 + c.printf0(string("usage: dos2unix file\n")) + c.exit(1) + + ' Copy file to temporary file and remove CR + infile := c.fopen(long[argv][1], string("r")) + ifnot infile + c.printf1(string("Could not open %s\n"), long[argv][1]) + c.exit(1) + + outfile := c.fopen(string("_temp123.tmp"), string("w")) + ifnot outfile + c.fclose(infile) + c.printf0(string("Could not open temporary file\n")) + c.exit(1) + + repeat while c.fgets(@buffer, 198, infile) + len := strsize(@buffer) + ptr := @buffer + len - 1 + if len and byte[ptr] == 10 + len-- + ptr-- + if len and byte[ptr] == 13 + len-- + ptr-- + byte[ptr][1] := 10 + byte[ptr][2] := 0 + c.fputs(@buffer, outfile) + + c.fclose(infile) + c.fclose(outfile) + + ' Copy temporary file to file + infile := c.fopen(string("_temp123.tmp"), string("r")) + ifnot infile + c.printf0(string("Could not open temporary file\n")) + c.exit(1) + + outfile := c.fopen(long[argv][1], string("w")) + ifnot outfile + c.fclose(infile) + c.printf1(string("Could not open %s\n"), long[argv][1]) + c.exit(1) + + repeat while (len := c.fread(@buffer, 1, 200, infile)) > 0 + c.fwrite(@buffer, 1, len, outfile) + + c.fclose(infile) + c.fclose(outfile) + + c.remove(string("_temp123.tmp")) + c.exit(0) + +{{ ++-----------------------------------------------------------------------------+ +| TERMS OF USE: MIT License | ++-----------------------------------------------------------------------------+ +|Permission is hereby granted, free of charge, to any person obtaining a copy | +|of this software and associated documentation files (the "Software"), to deal| +|in the Software without restriction, including without limitation the rights | +|to use, copy, modify, merge, publish, distribute, sublicense, and/or sell | +|copies of the Software, and to permit persons to whom the Software is | +|furnished to do so, subject to the following conditions: | +| | +|The above copyright notice and this permission notice shall be included in | +|all copies or substantial portions of the Software. | +| | +|THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | +|IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | +|FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE | +|AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | +|LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,| +|OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE| +|SOFTWARE. | ++-----------------------------------------------------------------------------+ +}} diff --git a/src/echo.spin b/src/echo.spin new file mode 100644 index 0000000..e46083e --- /dev/null +++ b/src/echo.spin @@ -0,0 +1,49 @@ +'****************************************************************************** +' Copyright (c) 2011, 2012, Dave Hein +' See end of file for terms of use. +'****************************************************************************** +CON + _clkmode = xinput + _xinfreq = $4e495053 ' SPIN + +OBJ + c : "clibsd" + g : "glob" + +PUB main(argc, argv) | i, ptr, space, globdata, str[25] + c.enter + + if argc > 1 + space := 0 + repeat i from 1 to argc - 1 + globdata := 0 + repeat while (ptr := g.glob(long[argv][i], @globdata, @str)) + c.printf2(string("%s%s"), @space, ptr) + space := " " + c.printf0(string("\n")) + + c.exit(0) + +{{ ++-----------------------------------------------------------------------------+ +| TERMS OF USE: MIT License | ++-----------------------------------------------------------------------------+ +|Permission is hereby granted, free of charge, to any person obtaining a copy | +|of this software and associated documentation files (the "Software"), to deal| +|in the Software without restriction, including without limitation the rights | +|to use, copy, modify, merge, publish, distribute, sublicense, and/or sell | +|copies of the Software, and to permit persons to whom the Software is | +|furnished to do so, subject to the following conditions: | +| | +|The above copyright notice and this permission notice shall be included in | +|all copies or substantial portions of the Software. | +| | +|THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | +|IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | +|FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE | +|AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | +|LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,| +|OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE| +|SOFTWARE. | ++-----------------------------------------------------------------------------+ +}} diff --git a/src/ed.spin b/src/ed.spin new file mode 100644 index 0000000..d978cb8 --- /dev/null +++ b/src/ed.spin @@ -0,0 +1,1094 @@ +'********************************************** +'* Spin code generated by cspin version 0.066 * +'********************************************** + +CON + _clkmode = xinput + _xinfreq = $4e495053 ' SPIN + +OBJ + c : "clibsd" + +PUB start(argc, argv) + c.enter + main(argc, argv) + +'****************************************************************************** +' ED Text Editor +' Copyright (c) 2011, Dave Hein +' See end of file for terms of use. +'****************************************************************************** + +CON + HDR_SIZE = 8 +' typedef struct StringS +next_0 = 0 +size = 4 +str = 8 +'#define EX_MODE + UNSPECIFIED = - 2 + PARSING_ERROR = - 3 + TAB_CHAR = 9 + MAX_CHARS = 200 +' Uninitialized global variables + +VAR + long memorysize + long ListHead + long YankHead + byte srchstr[100] + byte rplcstr1[100] + byte rplcstr2[100] + byte filename[100] + byte inbuf[MAX_CHARS] +' Initialized global variables + +DAT + rflag long 0 + gflag long 0 + vmode long 1 + prompt long 0 + vlines long 21 + scroll long 20 + changed long 0 + numlines long 0 + currline long 0 + currcol long 1 +' Main program entry + +PUB main(argc, argv) | cmd, retcode, strbuf + 'argc := 1 + ' Initialization + srchstr[0] := 0 + rplcstr1[0] := 0 + rplcstr2[0] := 0 + ListHead := 0 + YankHead := 0 + result := c.malloc(600) + ' Allocate the string buffer + memorysize := 16000 + repeat while (memorysize > 0) + strbuf := c.malloc(memorysize) + if (strbuf) + quit + memorysize -= 1000 + c.free(result) + 'c.printf1(string("memorysize = %d\n"), memorysize) + 'c.printf0(string("Press any key\n")) + 'c.getchar + smallocinit(strbuf, memorysize) + ' Copy the input file name if specified + if (argc > 1) + c.strcpy(@filename, long[argv][1]) + else + filename[0] := 0 + currline := ReadFile(@filename, 0) + ' Get a command and execute it + repeat while (1) + gflag := 0 + CheckMemory + if (vmode) + PrintScreen(currline) + if (prompt) + c.printf1(string("%c"), prompt) + c.gets(@inbuf) + retcode := ExecuteCommand + if (retcode > 0) + quit + elseif (retcode < 0) + c.printf0(string("?\n")) + c.free(strbuf) + c.exit(0) +' Allocate a string link and copy contents of "str" + +PUB AllocateString(str_0) | link + link := smalloc(strsize(str_0) + 1) + if (link) + c.strcpy((link + str), str_0) + return link +' Duplicate a string link + +PUB DuplicateString(link) + return AllocateString((link + str)) +' Check for a memory leak + +PUB CheckMemory | total, curr, listsize, yanksize, freesize + listsize := 0 + yanksize := 0 + freesize := 0 + curr := ListHead + repeat while (curr) + listsize += long[curr + size] + curr := long[curr + next_0] + curr := YankHead + repeat while (curr) + yanksize += long[curr + size] + curr := long[curr + next_0] + curr := smfreelist + repeat while (curr) + freesize += long[curr + size] + curr := long[curr + next_0] + total := listsize + yanksize + freesize + if (total <> memorysize) + c.printf0(string("Memory leak!\n")) + c.printf4(string("listsize = %d, yanksize = %d, freesize = %d, total = %d\n"), listsize, yanksize, freesize, listsize + yanksize + freesize) +' Get a pointer to a string link from its line number + +PUB GetLinePtr(linenum, pprev) | prev, curr + prev := 0 + curr := ListHead + repeat while (curr and--linenum > 0) + prev := curr + curr := long[curr + next_0] + long[pprev] := prev + return curr +' Remove the CR and LF characters from the end of a line + +PUB RemoveCRLF(str_0) | len + len := strsize(str_0) + str_0 += len - 1 + repeat while (len > 0) + if (byte[str_0] <> 10 and byte[str_0] <> 13) + quit + byte[str_0--] := 0 + len-- +' Insert a string link at the line given by "linenum" + +PUB InsertLine(line, linenum) | prev, curr + curr := GetLinePtr(linenum - 1, @prev) + if (linenum < 1 or linenum > numlines + 1) + c.printf2(string("InsertLine: linenum = %d, numlines = %d\n"), linenum, numlines) + if (curr == 0 and(linenum <> 1 or ListHead <> 0)) + c.printf1(string("InsertLine: curr == 0, linenum = %d\n"), linenum) + if (linenum == 1) + long[line + next_0] := ListHead + ListHead := line + else + long[line + next_0] := long[curr + next_0] + long[curr + next_0] := line + numlines++ +' Delete the line at "linenum" + +PUB DeleteLine(linenum) | prev, curr + curr := GetLinePtr(linenum, @prev) + if (not curr) + c.printf1(string("DeleteLine: curr = 0, linenum = %d\n"), linenum) + return + if (not prev) + ListHead := long[curr + next_0] + else + long[prev + next_0] := long[curr + next_0] + sfree(curr) + numlines-- +' Based on the value of gflag, check for if the line +' at "lineptr" does, or does not contains "str" + +PUB CheckMatch(lineptr, str_0) + if (gflag == 0) + return 0 + if (gflag == 1) + if (not FindString(lineptr, str_0)) + return 1 + return 0 + if (FindString(lineptr, str_0)) + return 1 + return 0 +' Print lines using the "n", "v", "l" or "p" format + +PUB PrintLines(linenum, first, last, mode) | col, len, line, fflag, lastproc, LinePtr_0, prev, curr + lastproc := linenum + curr := GetLinePtr(first, @prev) + line := first + repeat while (line =< last) + if (not curr) + c.printf1(string("PrintLines: curr = 0, line = %s\n"), line) + return 0 + LinePtr_0 :=(curr + str) + curr := long[curr + next_0] + if (CheckMatch(LinePtr_0, @srchstr)) + line++ + next + if (mode == "n") + c.printf2(string("%6d %s\n"), line, LinePtr_0) + elseif (mode == "v") + if (line == linenum) + c.printf2(string("%6d >%s\n"), line, LinePtr_0) + else + c.printf2(string("%6d %s\n"), line, LinePtr_0) + elseif (mode == "l") + repeat while (byte[LinePtr_0]) + if (byte[LinePtr_0] => 32 and byte[LinePtr_0] < 127) + c.printf1(string("%c"), byte[LinePtr_0]) + elseif (byte[LinePtr_0] == TAB_CHAR) + c.printf0(string("\\t")) + else + c.printf1(string("\\x%2.2x"), byte[LinePtr_0]) + LinePtr_0++ + c.printf0(string("$\n")) + else + c.printf1(string("%s\n"), LinePtr_0) + lastproc := line + line++ + return lastproc +' Decode a number from the string + +PUB getnum(pstr) | val, str_0 + val := 0 + str_0 := long[pstr] + repeat while (c.isdigit(byte[str_0])) + val :=(val * 10) + byte[str_0++] - "0" + long[pstr] := str_0 + return val +' Find the next character that matches "val" + +PUB FindChar(str_0, val) + repeat while (byte[str_0]) + if (byte[str_0] == val) + quit + str_0++ + return str_0 +' Skip the next character that does not match "val" + +PUB SkipChar(str_0, val) + repeat while (byte[str_0]) + if (byte[str_0] <> val) + quit + str_0++ + return str_0 +' Return true if "str2" is found in "str1" + +PUB FindString(str1, str2) | len + len := strsize(str2) + repeat while (byte[str1]) + if (c.strncmp(str1, str2, len) == 0) + return 1 + str1++ + return 0 +' Use the first character as a delimeter and copy the string to srchstr + +PUB GetString(pstr) | ptr, str_0, val, len + str_0 := long[pstr] + val := byte[str_0++] + if (not val) + return 0 + ptr := FindChar(str_0, val) + len :=(ptr) -(str_0) + if (byte[ptr]) + ptr++ + long[pstr] := ptr + if (len > 0) + c.memcpy(@srchstr, str_0, len) + srchstr[len] := 0 + return 1 +' Search forward through all the lines for the first occurance of srchstr + +PUB SearchStringForward(pstr, linenum) | i, prev, curr + curr := GetLinePtr(linenum + 1, @prev) + if (not GetString(pstr)) + return PARSING_ERROR + i := linenum + 1 + repeat while (i =< numlines) + if (not curr) + c.printf1(string("SearchStringForward: curr = 0, line = %d\n"), i) + return - 3 + if (FindString((curr + str), @srchstr)) + return i + curr := long[curr + next_0] + i++ + curr := ListHead + i := 1 + repeat while (i =< linenum) + if (not curr) + c.printf1(string("SearchStringForward: curr = 0, line = %d\n"), i) + return - 3 + if (FindString((curr + str), @srchstr)) + return i + curr := long[curr + next_0] + i++ + return PARSING_ERROR +' Search backward through all the lines for the first occurance of srchstr +' Note: This is not as efficient as the forward seach because we must use +' GetLinePtr to get the string link pointer + +PUB SearchStringBackward(pstr, linenum) | i, prev, curr + if (not GetString(pstr)) + return PARSING_ERROR + i := linenum - 1 + repeat while (i => 1) + curr := GetLinePtr(i, @prev) + if (not curr) + c.printf1(string("SearchStringBackward: curr = 0, line = %d\n"), i) + return - 3 + if (FindString((curr + str), @srchstr)) + return i + i-- + i := numlines + repeat while (i => linenum) + curr := GetLinePtr(i, @prev) + if (not curr) + c.printf1(string("SearchStringBackward: curr = 0, line = %d\n"), i) + return - 3 + if (FindString((curr + str), @srchstr)) + return i + i-- + return PARSING_ERROR +' Parse the replace string command and extract rplcstr1 and rplcstr2 + +PUB ParseReplaceString(str_0) | ptr + if (byte[str_0++] <> "s") + return 0 + if (byte[str_0] == 0) + return 1 + if (byte[str_0++] <> "/") + return 0 + ptr := FindChar(str_0, "/") + if (byte[ptr] <> "/") + return 0 + byte[ptr++] := 0 + if (byte[str_0]) + c.strcpy(@rplcstr1, str_0) + else + c.strcpy(@rplcstr1, @srchstr) + if (byte[ptr] == 0) + return 1 + str_0 := FindChar(ptr, "/") + if (byte[str_0] <> "/") + return 0 + byte[str_0++] := 0 + c.strcpy(@rplcstr2, ptr) + rflag :=(byte[str_0] == "g") + return 1 +' Store the results of the replace-string command on str1 into str2 + +PUB ReplaceString(str1, str2) | changeflag, len1, len2 + changeflag := 0 + len1 := strsize(@rplcstr1) + len2 := strsize(@rplcstr2) + ' Search through str1 for rplcstr1 and replace with rplcstr2 + repeat while (byte[str1]) + if (c.strncmp(str1, @rplcstr1, len1) == 0) + changeflag := 1 + c.memcpy(str2, @rplcstr2, len2) + str2 += len2 + str1 += len1 + ' Quit after the first time if 'g' was not specified + if (not rflag) + quit + else + byte[str2++] := byte[str1++] + ' Copy the remainder of str1 to str2 + repeat while (byte[str1]) + byte[str2++] := byte[str1++] + byte[str2] := 0 + return changeflag +' Execute the replace-string command on multiple lines in the text buffer + +PUB ReplaceStringCommand(inptr, first, last, linenum) | i, lastproc, changeflag, link, prev, curr + lastproc := linenum + changeflag := 0 + curr := GetLinePtr(first, @prev) + if (not ParseReplaceString(inptr)) + c.printf0(string("?\n")) + return last + i := first + repeat while (i =< last) + if (not curr) + c.printf1(string("ReplaceStringCommand: line = %d\n"), i) + return 1 + if (CheckMatch((curr + str), @srchstr)) + prev := curr + curr := long[curr + next_0] + i++ + next + if (ReplaceString((curr + str), @inbuf)) + link := curr + curr := smalloc(strsize(@inbuf) + 1) + if (not curr) + return lastproc + c.strcpy((curr + str), @inbuf) + if (prev) + long[prev + next_0] := curr + else + ListHead := curr + long[curr + next_0] := long[link + next_0] + sfree(link) + changeflag := 1 + changed := 1 + prev := curr + curr := long[curr + next_0] + lastproc := i + i++ + if (not changeflag) + c.printf0(string("?\n")) + return lastproc +' Extract a line number parameter from a command + +PUB GetLineNumber(str_0, linenum, num) | val + if (byte[str_0][0] == ".") + long[num] := linenum + str_0++ + elseif (byte[str_0][0] == "-") + str_0++ + if (c.isdigit(byte[str_0])) + val := getnum(@str_0) + else + val := 1 + long[num] := linenum - val + elseif (byte[str_0][0] == "+") + str_0++ + if (c.isdigit(byte[str_0])) + val := getnum(@str_0) + else + val := 1 + long[num] := linenum + val + elseif (byte[str_0][0] == "$") + long[num] := numlines + str_0++ + elseif (byte[str_0][0] == "/") + long[num] := SearchStringForward(@str_0, linenum) + elseif (byte[str_0][0] == "?") + long[num] := SearchStringBackward(@str_0, linenum) + elseif (c.isdigit(byte[str_0])) + long[num] := getnum(@str_0) + else + long[num] := UNSPECIFIED + if (long[num] <> UNSPECIFIED and(long[num] < 0 or long[num] > numlines)) + long[num] := PARSING_ERROR + return str_0 +' Extract the first and last numbers and the command from a command string + +PUB ParseCommand(pstr, linenum, first, last, cmd, bang) | done, str_0 + done := 0 + str_0 := long[pstr] + long[bang] := 0 + if (byte[str_0] == ",") + long[first] := 1 + long[last] := numlines + done := 1 + elseif (byte[str_0] == ";") + long[first] := linenum + long[last] := numlines + done := 1 + if (done) + str_0++ + long[cmd] := byte[str_0] + if (byte[str_0] and byte[str_0][1] == "!") + long[bang] := 1 + str_0++ + long[pstr] := str_0 + return 1 + str_0 := GetLineNumber(str_0, linenum, first) + if (long[first] == PARSING_ERROR) + return 0 + if (long[first] == UNSPECIFIED) + if (numlines == 0 and byte[str_0] == "i") + long[first] := long[last] := 1 + elseif (byte[str_0] == "g" or byte[str_0] == "v" or byte[str_0] == "w" or byte[str_0] == "x") + long[first] := 1 + long[last] := numlines + elseif (byte[str_0] == "j") + long[first] := linenum + long[last] := linenum + 1 + if (long[last] > numlines) + return 0 + elseif (byte[str_0] == "r") + long[first] := numlines + long[last] := numlines + elseif (byte[str_0] == 0 or byte[str_0] == "z") + long[first] := long[last] := linenum + 1 + if (long[first] > numlines) + return 0 + elseif (byte[str_0] == "=") + long[first] := long[last] := numlines + else + long[first] := linenum + long[last] := linenum + elseif (byte[str_0] == "," or byte[str_0] == ";") + str_0 := GetLineNumber(str_0 + 1, linenum, last) + if (long[last] == PARSING_ERROR) + return 0 + if (long[last] == UNSPECIFIED) + long[last] := long[first] + if (long[last] <(long[first])) + return 0 + else + long[last] := long[first] + long[cmd] := byte[str_0] + if (byte[str_0] and byte[str_0][1] == "!") + long[bang] := 1 + str_0++ + long[pstr] := str_0 + ' If first is zero, check if commands allows that + if (long[first] == 0) + str_0 := FindChar(string("aq=EerxQhVfP"), long[cmd]) + if (byte[str_0] == 0) + return 0 + return 1 +' Print the help information + +PUB help | infile, line, char, buffer[25] + infile := c.fopen(string("/manpages/edhelp.txt"), string("r")) + ifnot infile + return + line := 0 + repeat while c.fgets(@buffer, 100, infile) + RemoveCRLF(@buffer) + c.printf1(string("%s\n"), @buffer) + if ++line => vlines + 2 + c.printf0(string("")) + char := c.getchar + c.printf0(string("\b\b\b\b\b\b \b\b\b\b\b\b")) + if char == "q" OR char == 3 + quit + elseif char == 13 + line-- + else + line := 0 + if ++line + c.printf0(string("")) + char := c.getchar + c.printf0(string("\b\b\b\b\b\b \b\b\b\b\b\b")) + c.fclose(infile) + +{{ ++-----------------------------------------------------------------------------+ +| TERMS OF USE: MIT License | ++-----------------------------------------------------------------------------+ +|Permission is hereby granted, free of charge, to any person obtaining a copy | +|of this software and associated documentation files (the "Software"), to deal| +|in the Software without restriction, including without limitation the rights | +|to use, copy, modify, merge, publish, distribute, sublicense, and/or sell | +|copies of the Software, and to permit persons to whom the Software is | +|furnished to do so, subject to the following conditions: | +| | +|The above copyright notice and this permission notice shall be included in | +|all copies or substantial portions of the Software. | +| | +|THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | +|IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | +|FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE | +|AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | +|LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,| +|OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE| +|SOFTWARE. | ++-----------------------------------------------------------------------------+ +}} + +' Print a visualization mode screen + +PUB PrintScreen(linenum) | i, vlines1, vlines2, first, last + vlines1 := vlines - 1 + vlines2 := vlines / 2 + first := linenum - vlines2 + last := first + vlines1 + ' Determine the first and last lines to display + if (first < 1) + first := 1 + last := first + vlines1 + if (last > numlines) + last := numlines + elseif (last > numlines) + last := numlines + first := last - vlines1 + if (first < 1) + first := 1 + c.printf0(string("===============================================================\n")) + PrintLines(linenum, first, last, "v") + i := last - first + repeat while (i < vlines1) + c.printf0(string("~\n")) + i++ + c.printf0(string("===============================================================\n")) +' Empty a list and free the string links + +PUB EmptyList(list) | next_1 + repeat while (list) + next_1 := long[list + next_0] + sfree(list) + list := next_1 +' Delete lines from the text file list and move them to the yank list + +PUB DeleteLines(first, last) | i, prev, curr, yank + curr := GetLinePtr(first, @prev) + yank := 0 + EmptyList(YankHead) + YankHead := 0 + i := first + repeat while (i =< last) + if (not curr) + c.printf1(string("DeleteLines: line = %d\n"), first) + return 1 + if (not yank) + YankHead := curr + else + long[yank + next_0] := curr + yank := curr + curr := long[curr + next_0] + numlines-- + i++ + if (prev) + long[prev + next_0] := curr + else + ListHead := curr + long[yank + next_0] := 0 + changed := 1 + if (first > numlines) + first := numlines + return first +' Copy lines from the text file list to the yank list + +PUB YankLines(first, last) | line, link, list, curr + list := 0 + curr := GetLinePtr(first, @link) + EmptyList(YankHead) + YankHead := 0 + line := first + repeat while (line =< last) + if (not curr) + c.printf1(string("YankLines: line = %d\n"), line) + return 1 + link := DuplicateString(curr) + if (not link) + quit + if (not list) + YankHead := link + else + long[list + next_0] := link + curr := long[curr + next_0] + list := link + line++ + if (list) + long[list + next_0] := 0 + return last +' Copy the lines on the yank list to the text file list + +PUB PushLines(line) | link, curr + curr := YankHead + repeat while (curr) + link := DuplicateString(curr) + if (not link) + quit + InsertLine(link,++line) + curr := long[curr + next_0] + changed := 1 + return line +' Go into the text input mode and append after "linenum" + +PUB AppendLines(linenum) | prev, curr, link + curr := GetLinePtr(linenum + 1, @prev) + repeat while (1) + c.gets(@inbuf) + RemoveCRLF(@inbuf) + if (inbuf[0] == "." and inbuf[1] == 0) + quit + link := AllocateString(@inbuf) + if (not link) + quit + long[link + next_0] := curr + if (prev) + long[prev + next_0] := link + else + ListHead := link + prev := link + linenum++ + numlines++ + changed := 1 + return linenum +' Go into the text input mode and insert lines before "linenum" + +PUB InsertLines(linenum) | linenum1 + linenum1 := AppendLines(linenum - 1) + if (linenum1 > linenum) + linenum := linenum1 + return linenum +' Join lines together and move the original lines to the yank buffer + +PUB JoinLines(first, last) | i, len, str_0, prev, curr, link, yank + len := 0 + curr := GetLinePtr(first, @prev) + link := curr + yank := 0 + EmptyList(YankHead) + YankHead := 0 + ' Determine the total length of the selected lines + i := first + repeat while (i =< last) + if (not link) + c.printf1(string("JoinLines: line = %d\n"), i) + return 1 + len += strsize((link + str)) + link := long[link + next_0] + i++ + ' Allocate a new string buffer and concatenate the strings + link := smalloc(len + 1) + if (not link) + return first + str_0 :=(link + str) + byte[str_0] := 0 + i := first + repeat while (i =< last) + if (not curr) + c.printf1(string("JoinLines: line = %d\n"), i) + return 1 + c.strcat(str_0,(curr + str)) + if (not yank) + YankHead := curr + else + long[yank + next_0] := curr + yank := curr + curr := long[curr + next_0] + numlines-- + changed := 1 + i++ + ' Insert the new string + if (prev) + long[prev + next_0] := link + else + ListHead := link + long[link + next_0] := curr + long[yank + next_0] := 0 + numlines++ + return first +' Move or copy lines to after the destination address + +PUB MoveLines(first, last, dstaddr, cmd) | i, prev, curr, link, srcaddr, insertbefore + if (dstaddr < 0 or dstaddr > numlines or(first =< dstaddr and dstaddr < last)) + c.printf0(string("?\n")) + else + srcaddr := first + insertbefore := dstaddr < first + i := first + repeat while (i =< last) + curr := GetLinePtr(srcaddr++, @prev) + link := DuplicateString(curr) + if (not link) + quit + InsertLine(link,++dstaddr) + if (insertbefore) + srcaddr++ + if (cmd == "m") + DeleteLine(--srcaddr) + if (not insertbefore) + dstaddr-- + i++ + changed := 1 + return dstaddr +' Read a file after "linenum" + +PUB ReadFile(fname, linenum) | numchar, infile, link + numchar := 0 + infile := c.fopen(fname, string("r")) + if (infile) + repeat while (c.fgets(@inbuf, MAX_CHARS, infile)) + RemoveCRLF(@inbuf) + link := AllocateString(@inbuf) + if (not link) + quit + InsertLine(link,++linenum) + numchar += strsize(@inbuf) + c.fclose(infile) + c.printf1(string("%d\n"), numchar) + return linenum +' Write lines first to last to the output file + +PUB WriteFile(fname, first, last) | i, LinePtr_0, curr, prev, numchar, outfile + numchar := 0 + outfile := c.fopen(fname, string("w")) + if (not outfile) + return - 1 + i := first + repeat while (i =< last) + curr := GetLinePtr(i, @prev) + LinePtr_0 :=(curr + str) + numchar += strsize(LinePtr_0) + c.fprintf1(outfile, string("%s\n"), LinePtr_0) + i++ + c.fclose(outfile) + c.printf1(string("%d\n"), numchar) + if (first == 1 and last == numlines) + changed := 0 + return 0 +' Execute a command + +PUB ExecuteCommand | curr, prev, LinePtr_0, outfile, inptr, first, last, cmd, i, temp, numchar, bang + inptr := @inbuf + ' Parse the command and extract the parameters + if (not ParseCommand(@inptr, currline, @first, @last, @cmd, @bang)) + return - 2 + ' Parse a "g" or "v" command if present + if (cmd == "g" or cmd == "v") + inptr++ + if (GetString(@inptr)) + if (cmd == "g") + gflag := 1 + else + gflag := 2 + cmd := byte[inptr] + if (cmd == 0) + cmd := "p" + c.printf1(string("cmd = %c\n"), cmd) + else + return - 3 + ' Select the command and execute it + case(cmd) + 0 : + ' Print the current line if no command + if (numlines == 0) + return - 4 + if (last => 0) + currline := last + elseif (first => 0) + currline := first + curr := GetLinePtr(currline, @prev) + LinePtr_0 :=(curr + str) + c.printf1(string("%s\n"), LinePtr_0) + "Q" : + ' Quit unconditionally + return 1 + "q" : + ' Check for changes and quit if OK + if (changed == 0) + return 1 + changed := 0 + return - 5 + "e", "E" : + ' Reset the text buffer to the contents of the default file + if (changed and cmd == "e") + changed := 0 + return - 6 + inptr := SkipChar(inptr + 1, " ") + if (byte[inptr] == 0 and filename == 0) + return - 7 + if (byte[inptr]) + c.strcpy(@filename, inptr) + EmptyList(YankHead) + YankHead := 0 + EmptyList(ListHead) + ListHead := 0 + numlines := 0 + currline := ReadFile(@filename, 0) + changed := 0 + "r" : + ' Read the contents of a file after the specified line + inptr := SkipChar(inptr + 1, " ") + if (byte[inptr] == 0 and filename == 0) + return - 8 + if (byte[inptr]) + if (not filename[0]) + c.strcpy(@filename, inptr) + else + inptr := @filename + temp := numlines + currline := ReadFile(inptr, first) + if (temp and numlines <> temp) + changed := 1 + "w" : + ' Write the text buffer to a file + inptr := SkipChar(inptr + 1, " ") + if (byte[inptr] == 0) + if (filename == 0) + return - 9 + inptr := @filename + if (WriteFile(inptr, first, last) < 0) + return - 10 + if (cmd == "x") + return 1 + if (filename == 0) + c.strcpy(@filename, inptr) + "d" : + ' Delete the specified lines + currline := DeleteLines(first, last) + "m", "t" : + ' Move or copy the specified lines + if (byte[inptr][1]) + c.sscanf1(inptr + 1, string("%d"), @temp) + else + temp := currline + currline := MoveLines(first, last, temp, cmd) + "c" : + ' Change the specified lines + temp :=(last == numlines) + currline := DeleteLines(first, last) + if (temp) + currline := AppendLines(currline) + else + currline := InsertLines(currline) + "a" : + ' Append text after the specified line + currline := AppendLines(first) + "i" : + ' Insert text before the specified line + currline := InsertLines(first) + "j" : + ' Join together lines + currline := JoinLines(first, last) + "p", "l", "n" : + ' Print lines in either the "p", "l" or "n" format + currline := PrintLines(currline, first, last, cmd) + "h" : + ' Print help information + help + "V" : + ' Set the number of lines in the visualization mode or + ' toggle the visualization mode if no lines are specified + if (byte[inptr][1]) + c.sscanf1(inptr + 1, string("%d"), @temp) + if (temp =< 0) + return - 11 + vlines := temp + vmode := 1 + else + vmode := vmode ^ 1 + "s" : + ' Replace text in the specified lines + currline := ReplaceStringCommand(inptr, first, last, currline) + "x" : + ' Push text from the yank buffer + currline := PushLines(first) + "y" : + ' Yank lines to the yank buffer + currline := YankLines(first, last) + "z" : + ' Set the number of lines to scroll if specified, + ' and scroll forward + first := last + if (byte[inptr][1]) + c.sscanf1(inptr + 1, string("%d"), @temp) + if (temp =< 0) + return - 12 + scroll := temp + last := first + scroll + if (last > numlines) + last := numlines + PrintLines(currline, first, last, "p") + currline := last + "f" : + ' Set the default file name if specifed and print it + inptr := SkipChar(inptr + 1, " ") + if (byte[inptr] == 0 and filename == 0) + return - 13 + if (byte[inptr]) + c.strcpy(@filename, inptr) + c.printf1(string("%s\n"), @filename) + "=" : + ' Print the number of lines, or the line number if specified + c.printf1(string("%d\n"), last) + "P" : + ' Toggle the prompt mode + if (prompt) + prompt := 0 + else + prompt := "*" + OTHER : + ' Return error code if not a valid command + return - 14 + return 0 +'****************************************************************************** +' String Allocation Routines +' Copyright (c) 2011, Dave Hein +' See end of file for terms of use. +'****************************************************************************** + +VAR + long smfreelist + +PUB smallocinit(ptr, size_0) | curr + curr := ptr + smfreelist := curr + long[curr + next_0] := 0 + long[curr + size] := size_0 +' Allocate a block of "size" bytes. Return a pointer to the block if +' successful, or zero if a large enough memory block could not be found + +PUB smalloc(size_0) | prev, curr, next_1 + 'printf("smalloc: %d\n", size); + if (size_0 =< 0) + return 0 + ' Adjust size to nearest long plus the header size + size_0 :=((size_0 + 3) &(! 3)) + HDR_SIZE + ' Search for a block of memory + prev := 0 + curr := smfreelist + repeat while (curr) + if (long[curr + size] => size_0) + quit + prev := curr + curr := long[curr + next_0] + ' Return null if block not found + if (curr == 0) + c.printf0(string("Out of memory!\n")) + return 0 + ' Get next pointer + next_1 := long[curr + next_0] + ' Split block if larger than needed + if (long[curr + size] => size_0 + HDR_SIZE + 16) + next_1 :=(((curr) + size_0)) + long[next_1 + next_0] := long[curr + next_0] + long[next_1 + size] := long[curr + size] - size_0 + long[curr + next_0] := next_1 + long[curr + size] := size_0 + ' Remove block from the free list + long[curr + next_0] := 0 + if (prev) + long[prev + next_0] := next_1 + else + smfreelist := next_1 + return curr +' Insert a memory block back into the free list. Merge blocks together if +' the memory block is contiguous with other blocks on the list. + +PUB sfree(curr) | prev, next_1 + prev := 0 + next_1 := smfreelist + ' Find Insertion Point + repeat while (next_1) + if (curr => prev and curr =< next_1) + quit + prev := next_1 + next_1 := long[next_1 + next_0] + ' Merge with the previous block if contiguous + if (prev and(((prev) + long[prev + size])) == curr) + long[prev + size] += long[curr + size] + ' Also merge with next block if contiguous + if ((((prev) + long[prev + size])) == next_1) + long[prev + size] += long[next_1 + size] + long[prev + next_0] := long[next_1 + next_0] + ' Merge with the next block if contiguous + elseif (next_1 and(((curr) + long[curr + size])) == next_1) + long[curr + size] += long[next_1 + size] + long[curr + next_0] := long[next_1 + next_0] + if (prev) + long[prev + next_0] := curr + else + smfreelist := curr + ' Insert in the middle of the free list if not contiguous + elseif (prev) + long[prev + next_0] := curr + long[curr + next_0] := next_1 + ' Otherwise, insert at beginning of the free list + else + smfreelist := curr + long[curr + next_0] := next_1 +{ ++----------------------------------------------------------------------------+ +| TERMS OF USE: MIT License | ++----------------------------------------------------------------------------+ +|Permission is hereby granted, free of charge, to any person obtaining a copy| +|of this software and associated documentation files (the "Software"), to | +|deal in the Software without restriction, including without limitation the | +|rights to use, copy, modify, merge, publish, distribute, sublicense, and/or | +|sell copies of the Software, and to permit persons to whom the Software is | +|furnished to do so, subject to the following conditions: | +| | +|The above copyright notice and this permission notice shall be included in | +|all copies or substantial portions of the Software. | +| | +|THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | +|IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | +|FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE | +|AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | +|LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING | +|FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS| +|IN THE SOFTWARE. | ++----------------------------------------------------------------------------+ +} diff --git a/src/eeprom.spin b/src/eeprom.spin new file mode 100644 index 0000000..0cd14ae --- /dev/null +++ b/src/eeprom.spin @@ -0,0 +1,280 @@ +CON + _clkmode = xtal1 + pll16x + _xinfreq = 5_000_000 + + eeprom_pin = 28 + +OBJ + c : "clibsd" + sys : "sysdefs" + ut : "unixtime" + vs : "varsubs" + spi : "safe_spi_p" + ser : "cserial" + fsrwx : "fsrwx" + +PUB WritePage(SCL, devSel, addrReg, dataPtr, count) : ackbit + ' Write three address bytes + devSel |= $A0 | ((addr >> 15) & %1110) + Start(SCL) + Write(SCL, devSel) + Write(SCL, addr >> 8 & $FF) + Write(SCL, addr & $FF) + ' Write four data bytes + repeat 4 + Write(SCL, byte[addr++]) + Stop(SCL) + ' Wait till the write is done + repeat + Start(SCL) + ackbit := Write(SCL, devSel) + Stop(SCL) + while ackbit +xxxxx + +PUB Initialize(SCL) | SDA ' An I2C device may be left in an + SDA := SCL + 1 ' invalid state and may need to be + outa[SCL] := 1 ' reinitialized. Drive SCL high. + dira[SCL] := 1 + dira[SDA] := 0 ' Set SDA as input + repeat 9 + outa[SCL] := 0 ' Put out up to 9 clock pulses + outa[SCL] := 1 + if ina[SDA] ' Repeat if SDA not driven high + quit ' by the EEPROM + +PUB Start(SCL) | SDA ' SDA goes HIGH to LOW with SCL HIGH + SDA := SCL + 1 + outa[SCL]~~ ' Initially drive SCL HIGH + dira[SCL]~~ + outa[SDA]~~ ' Initially drive SDA HIGH + dira[SDA]~~ + outa[SDA]~ ' Now drive SDA LOW + outa[SCL]~ ' Leave SCL LOW + +PUB Stop(SCL) | SDA ' SDA goes LOW to HIGH with SCL High + SDA := SCL + 1 + outa[SCL]~~ ' Drive SCL HIGH + outa[SDA]~~ ' then SDA HIGH + dira[SCL]~ ' Now let them float + dira[SDA]~ ' If pullups present, they'll stay HIGH + +PUB Write(SCL, data) : ackbit | SDA + SDA := SCL + 1 + ackbit := 0 + data <<= 24 + repeat 8 ' Output data to SDA + outa[SDA] := (data <-= 1) & 1 + outa[SCL]~~ ' Toggle SCL from LOW to HIGH to LOW + outa[SCL]~ + dira[SDA]~ ' Set SDA to input for ACK/NAK + outa[SCL]~~ + ackbit := ina[SDA] ' Sample SDA when SCL is HIGH + outa[SCL]~ + outa[SDA]~ ' Leave SDA driven LOW + dira[SDA]~~ +'' PASM I2C Driver Version 1.0 +'' Copyright (c) 2010 Dave Hein +'' June 6, 2010 +'' See end of file for terms of use + +'' This is a PASM version of Mike Green's Basic I2C Driver. The +'' low level I2C routines have been converted to PASM to increase the +'' I/O speed. These routines use the same calling interface as +'' the Basic I2C Driver Version 1.1 in the OBEX, and should be fully +'' compatible with any existing code that uses the Basic I2C Driver. + +'' Just like the Basic I2C Driver, the PASM I2C Driver assumes +'' that the SDA pin is one higher than the SCL pin. It assumes that +'' neither the SDA nor the SCL pins have pullups, so drives both. + +'' This object uses the Initialize method to start up a cog rather than using +'' the start method. This is done to remain consistent with the Basic I2C +'' Driver routines. Initialize must be called at the beginning of the program. +'' This loads the PASM code in a cog, and clocks the I2C bus to initialize +'' the devices on the bus. Subsequent calls may be made to Initialize, and +'' it will not cause the cog to be stopped or reloaded. + +'' The bus I/O speed is controlled by the constant DELAY_CYCLES. This constant +'' is used in the delay routine. The total delay consists of calling the delay +'' routine, performing a waitcnt of CNT + DELAY_CYCLES, and then returning +'' from the delay routine. Therefore, the total delay will be about +'' 12 + DELAY_CYCLES. + +'' The delay time represents the clock high time, and half the clock low time. +'' It is also used to determine the setup and hold times for the data bit for +'' read, write, start and stop operations. DELAY_CYCLES is defined with a +'' value of 52, which gives a total delay of 64 cycles. At 80 MHz, this is +'' 0.8 usecs, which is about one-third of a 400 KHz cycle time. This value +'' should be modified to provide the optimal speed for a particular application. + +'' Please see Mike Green's Basic I2C Driver object for more information on +'' the I2C routines, and on how EEPROMs are addressed +OBJ + sys : "sysdefs" + +CON + ACK = 0 ' I2C Acknowledge + NAK = 1 ' I2C No Acknowledge + Xmit = 0 ' I2C Direction Transmit + Recv = 1 ' I2C Direction Receive + + CMD_START = 1 ' Issue a start bit + CMD_STOP = 2 ' Issue a stop bit + CMD_READ = 3 ' Transmit a byte to the I2C bus + CMD_WRITE = 4 ' Read a byte from the I2C bus + CMD_INIT = 5 ' Initialize the I2C bus + CMD_READPAGE = 6 ' Read one or more bytes in the page mode + CMD_WRITEPAGE = 7 ' Write one or more bytes in the page mode + + DELAY_CYCLES = 52 ' I2C Delay time. Must be between 12 and 511 + + i2c_cmd = sys#i2c_cmd + i2c_parm = sys#i2c_parm +{ +DAT + cognum long 0 + cmdbuf long 0, 0 + +PUB Initialize(SCL) +'' Start cog if not started, and initialize the devices on the I2C bus + 'ser.dbprintf1(string("i2c initialize %d\n"), SCL) + cmdbuf[1] := @SCL + cmdbuf := CMD_INIT + ifnot cognum + cognum := cognew(@cmdloop, @cmdbuf) + 1 + repeat while cmdbuf +} + +PUB Start(SCL) +'' Issue an I2C start command + long[i2c_parm] := @SCL + long[i2c_cmd] := CMD_START + repeat while long[i2c_cmd] + +PUB Stop(SCL) +'' Issue an I2C stop command + long[i2c_parm] := @SCL + long[i2c_cmd] := CMD_STOP + repeat while long[i2c_cmd] + +PUB Read(SCL, ackbit) +'' Read in i2c data, Data byte is output MSB first, SDA data line is +'' valid only while the SCL line is HIGH. SCL and SDA left in LOW state. + long[i2c_parm] := @SCL + long[i2c_cmd] := CMD_READ + repeat while long[i2c_cmd] + result := long[i2c_parm] + +PUB Write(SCL, data) +'' Write i2c data. Data byte is output MSB first, SDA data line is valid +'' only while the SCL line is HIGH. Data is always 8 bits (+ ACK/NAK). +'' SDA is assumed LOW and SCL and SDA are both left in the LOW state. + long[i2c_parm] := @SCL + long[i2c_cmd] := CMD_WRITE + repeat while long[i2c_cmd] + result := long[i2c_parm] + +PUB ReadPage(SCL, devSel, addrReg, dataPtr, count) : ackbit +'' Read in a block of i2c data. Device select code is devSel. Device starting +'' address is addrReg. Data address is at dataPtr. Number of bytes is count. +'' The device select code is modified using the upper 3 bits of the 19 bit addrReg. +'' Return zero if no errors or the acknowledge bits if an error occurred. + long[i2c_parm] := @SCL + long[i2c_cmd] := CMD_READPAGE + repeat while long[i2c_cmd] + result := long[i2c_parm] + +PUB WritePage(SCL, devSel, addrReg, dataPtr, count) : ackbit +'' Write out a block of i2c data. Device select code is devSel. Device starting +'' address is addrReg. Data address is at dataPtr. Number of bytes is count. +'' The device select code is modified using the upper 3 bits of the 19 bit addrReg. +'' Most devices have a page size of at least 32 bytes, some as large as 256 bytes. +'' Return zero if no errors or the acknowledge bits if an error occurred. If +'' more than 31 bytes are transmitted, the sign bit is "sticky" and is the +'' logical "or" of the acknowledge bits of any bytes past the 31st. + long[i2c_parm] := @SCL + long[i2c_cmd] := CMD_WRITEPAGE + repeat while long[i2c_cmd] + result := long[i2c_parm] + +PUB ReadByte(SCL, devSel, addrReg) : data +'' Read in a single byte of i2c data. Device select code is devSel. Device +'' starting address is addrReg. The device select code is modified using the +'' upper 3 bits of the 19 bit addrReg. This returns true if an error occurred. + if ReadPage(SCL, devSel, addrReg, @data, 1) + return -1 + +PUB ReadWord(SCL, devSel, addrReg) : data +'' Read in a single word of i2c data. Device select code is devSel. Device +'' starting address is addrReg. The device select code is modified using the +'' upper 3 bits of the 19 bit addrReg. This returns true if an error occurred. + if ReadPage(SCL, devSel, addrReg, @data, 2) + return -1 + +PUB ReadLong(SCL, devSel, addrReg) : data +'' Read in a single long of i2c data. Device select code is devSel. Device +'' starting address is addrReg. The device select code is modified using the +'' upper 3 bits of the 19 bit addrReg. This returns true if an error occurred. +'' Note that you can't distinguish between a return value of -1 and true error. + if ReadPage(SCL, devSel, addrReg, @data, 4) + return -1 + +PUB WriteByte(SCL, devSel, addrReg, data) +'' Write out a single byte of i2c data. Device select code is devSel. Device +'' starting address is addrReg. The device select code is modified using the +'' upper 3 bits of the 19 bit addrReg. This returns true if an error occurred. + if WritePage(SCL, devSel, addrReg, @data, 1) + return true + return false + +PUB WriteWord(SCL, devSel, addrReg, data) +'' Write out a single word of i2c data. Device select code is devSel. Device +'' starting address is addrReg. The device select code is modified using the +'' upper 3 bits of the 19 bit addrReg. This returns true if an error occurred. +'' Note that the word value may not span an EEPROM page boundary. + if WritePage(SCL, devSel, addrReg, @data, 2) + return true + return false + +PUB WriteLong(SCL, devSel, addrReg, data) +'' Write out a single long of i2c data. Device select code is devSel. Device +'' starting address is addrReg. The device select code is modified using the +'' upper 3 bits of the 19 bit addrReg. This returns true if an error occurred. +'' Note that the long word value may not span an EEPROM page boundary. + if WritePage(SCL, devSel, addrReg, @data, 4) + return true + return false + +PUB WriteWait(SCL, devSel, addrReg) : ackbit +'' Wait for a previous write to complete. Device select code is devSel. Device +'' starting address is addrReg. The device will not respond if it is busy. +'' The device select code is modified using the upper 3 bits of the 18 bit addrReg. +'' This returns zero if no error occurred or one if the device didn't respond. + devSel |= addrReg >> 15 & %1110 + Start(SCL) + ackbit := Write(SCL, devSel | Xmit) + Stop(SCL) + return ackbit + +{{ + TERMS OF USE: MIT License + + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), to deal + in the Software without restriction, including without limitation the rights + to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + copies of the Software, and to permit persons to whom the Software is + furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in + all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,ARISING FROM, + OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + THE SOFTWARE. +}} diff --git a/src/exit.spin b/src/exit.spin new file mode 100644 index 0000000..dc5053f --- /dev/null +++ b/src/exit.spin @@ -0,0 +1,34 @@ +obj + sys : "sysdefs" + +dat + run_prog + long $a0bc65f0, $08bc6e32, $80fc6404, $08bc7032, $80fc6404, $08bc6032 + long $80fc6404, $08bc6232, $80fc63ff, $28fc6209, $08fc6600, $00fc6804 + long $083c6029, $083c5c2a, $083c582b, $08bc642b, $863c642c, $5c68000f + long $80fc6001, $80fc5d00, $80fc5d00, $e4fc620c, $087c6600, $007c6804 + long $04fc6a08, $04fc6c0a, $a0bc6236, $84bc6235, $28fc6202, $083c5a35 + long $80fc6a04, $e4fc621d, $083c5a36, $80fc6c04, $083c6e36, $80fc6c04 + long $083c7036, $0cfc6401, $60fc6407, $68bc5e32, $0c7c5e02, $00007fd8 + long $00007fdc, $00007fd4, $00000072, $00000000, $00000000, $0007c010 + + arg_list long 0[4] + +pub exit(retval) | i, cogspi + cogspi := long[sys#SPI_engine_cog] - 1 + arg_list[2] := long[sys#shell_sector] + arg_list[3] := long[sys#shell_size] + long[sys#return_value] := retval + + ' Stop all the cogs except this one and the SD SPI cog + repeat i from 0 to 7 + if (i <> cogid and i <> cogspi) + cogstop(i) + + ' Clear and return all the locks + repeat i from 0 to 7 + lockclr(i) + lockret(i) + + ' Start run_prog in this cog + coginit(cogid, @run_prog, @arg_list) diff --git a/src/export.spin b/src/export.spin new file mode 100644 index 0000000..6b2fa87 --- /dev/null +++ b/src/export.spin @@ -0,0 +1,110 @@ +'****************************************************************************** +' Copyright (c) 2013 Dave Hein +' See end of file for terms of use. +'****************************************************************************** +CON + _clkmode = xinput + _xinfreq = $4e495053 ' SPIN + + DOUBLE_QUOTE = $22 + +OBJ + c : "clibsd" + sys : "sysdefs" + vs : "varsubs" + +PUB main(argc, argv) | argi + + c.enter + + ' Print if no parms + if argc < 2 + PrintVars + c.exit(0) + + repeat argi from 1 to argc - 1 + ProcessParm(long[argv][argi]) + + c.exit(0) + +PUB ProcessParm(name) | ptr, len1, len2, space, value, noequal + + ' Check for "=" and get value + value := vs.FindChar(name, "=") + ifnot byte[value] + noequal := 1 + else + noequal := 0 + byte[value++] := 0 + + ' Check if variable already exists + if (ptr := vs.FindVar(name)) + if noequal + byte[ptr] := VS#GLOBAL_FLAG + return + ptr := vs.RemoveEntry(ptr) + else + ptr := vs.FindEnd + + ' Check if space is available + space := sys#environ_vars_end - ptr + 1 + len1 := strsize(name) + len2 := strsize(value) + if space - len1 - len2 - 4 < 50 + c.printf2(string("space = %d, need = %d\n"), space, len1 + len2 + 4) + if (space < len1 + len2 + 4) + c.printf0(string("Not enough variable space\n")) + c.exit(1) + + ' Add variable + byte[ptr++] := VS#GLOBAL_FLAG + c.strcpy(ptr, name) + ptr += len1 + 1 + c.strcpy(ptr, value) + ptr += len2 + 1 + byte[ptr] := 0 + +{ +PUB usage + c.printf0(string("usage: export [var[=[value]]...]\n")) + c.exit(1) +} + +PUB PrintVars | ptr + ptr := sys#environ_vars + repeat while byte[ptr] + ptr := PrintVar(ptr) + +PUB PrintVar(ptr) | vartype, name, value + vartype := byte[ptr++] + name := ptr + ptr += strsize(ptr) + 1 + value := ptr + ptr += strsize(ptr) + 1 + if vartype == VS#GLOBAL_FLAG + c.printf2(string("declare -x %s=", DOUBLE_QUOTE, "%s", DOUBLE_QUOTE, "\n"), name, value) + return ptr + +{{ ++-----------------------------------------------------------------------------+ +| TERMS OF USE: MIT License | ++-----------------------------------------------------------------------------+ +|Permission is hereby granted, free of charge, to any person obtaining a copy | +|of this software and associated documentation files (the "Software"), to deal| +|in the Software without restriction, including without limitation the rights | +|to use, copy, modify, merge, publish, distribute, sublicense, and/or sell | +|copies of the Software, and to permit persons to whom the Software is | +|furnished to do so, subject to the following conditions: | +| | +|The above copyright notice and this permission notice shall be included in | +|all copies or substantial portions of the Software. | +| | +|THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | +|IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | +|FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE | +|AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | +|LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,| +|OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE| +|SOFTWARE. | ++-----------------------------------------------------------------------------+ +}} diff --git a/src/fsrwx.spin b/src/fsrwx.spin new file mode 100644 index 0000000..92b71ee --- /dev/null +++ b/src/fsrwx.spin @@ -0,0 +1,950 @@ +{{ +' fsrw 2.6 Copyright 2009 Tomas Rokicki and Jonathan Dummer +' +' See end of file for terms of use. +' +' This object provides FAT16/32 file read/write access on a block device. +' Only one file open at a time. Open modes are 'r' (read), 'a' (append), +' 'w' (write), and 'd' (delete). Only the root directory is supported. +' No long filenames are supported. We also support traversing the +' root directory. +' +' In general, negative return values are errors; positive return +' values are success. Other than -1 on popen when the file does not +' exist, all negative return values will be "aborted" rather than +' returned. +' +' Changes: +' v1.1 28 December 2006 Fixed offset for ctime +' v1.2 29 December 2006 Made default block driver be fast one +' v1.3 6 January 2007 Added some docs, and a faster asm +' v1.4 4 February 2007 Rearranged vars to save memory; +' eliminated need for adjacent pins; +' reduced idle current consumption; added +' sample code with abort code data +' v1.5 7 April 2007 Fixed problem when directory is larger +' than a cluster. +' v1.6 23 September 2008 Fixed a bug found when mixing pputc +' with pwrite. Also made the assembly +' routines a bit more cautious. +' v2.1 12 July 2009 FAT32, SDHC, multiblock, bug fixes +' v2.4 26 September 2009 Added seek support. Added clustersize. +' v2.4a 6 October 2009 modified setdate to explicitly set year/month/etc. +' v2.5 13 November 2009 fixed a bug on releasing the pins, added a "release" pass through function +' v2.6 11 December 2009: faster transfer hub <=> cog, safe_spi.spin uses 1/2 speed reads, is default +}} +' +' Constants describing FAT volumes. +' +con + SECTORSIZE = 512 + SECTORSHIFT = 9 + DIRSIZE = 32 + DIRSHIFT = 5 +' +' The object that provides the block-level access. +' +obj + sdspi: "safe_spi" + 'debug: "cserial" + +dat +' +' +' Variables concerning the open file. +' + fclust long 0 ' the current cluster number + filesize long 0 ' the total current size of the file + floc long 0 ' the seek position of the file + frem long 0 ' how many bytes remain in this cluster from this file + bufat long 0 ' where in the buffer our current character is + bufend long 0 ' the last valid character (read) or free position (write) + direntry long 0 ' the byte address of the directory entry (if open for write) + writelink long 0 ' the byte offset of the disk location to store a new cluster + fatptr long 0 ' the byte address of the most recently written fat entry + firstcluster long 0 ' the first cluster of this file + pbuf long 0 ' pointer to the main data buffer +' +' Variables used when mounting to describe the FAT layout of the card +' (moved to the end of the file in the Spin version). +' +' +' Variables controlling the caching. +' +' +' Buffering: two sector buffers. These two buffers must be longword +' aligned! To ensure this, make sure they are the first byte variables +' defined in this object. +' + handle0 long 0[handle_longs] ' Handle used for general file operations + buf byte 0[SECTORSIZE] ' main data buffer + 'padding long 0[4] + +pub release +' +' This is just a pass-through function to allow the block layer +' to tristate the I/O pins to the card. +' + sdspi.release +pri writeblock2(n, b) +' +' On metadata writes, if we are updating the FAT region, also update +' the second FAT region. +' + sdspi.writeblock(n, b) + if (n => fat1) + if (n < fat1 + sectorsperfat) + sdspi.writeblock(n+sectorsperfat, b) +pri flushifdirty +' +' If the metadata block is dirty, write it out. +' + if (dirty) + writeblock2(lastread, @buf2) + dirty := 0 +pri readblockc(n) +' +' Read a block into the metadata buffer, if that block is not already +' there. +' + if (n <> lastread) + flushifdirty + sdspi.readblock(n, @buf2) + lastread := n +pri brword(b) +' +' Read a byte-reversed word from a (possibly odd) address. +' + return (byte[b]) + ((byte[b][1]) << 8) +pri brlong(b) +' +' Read a byte-reversed long from a (possibly odd) address. +' + return brword(b) + (brword(b+2) << 16) +pri brclust(b) +' +' Read a cluster entry. +' + if (filesystem == 1) + return brword(b) + else + return brlong(b) +pri brwword(w, v) +' +' Write a byte-reversed word to a (possibly odd) address, and +' mark the metadata buffer as dirty. +' + byte[w++] := v + byte[w] := v >> 8 + dirty := 1 +pri brwlong(w, v) +' +' Write a byte-reversed long to a (possibly odd) address, and +' mark the metadata buffer as dirty. +' + brwword(w, v) + brwword(w+2, v >> 16) +pri brwclust(w, v) +' +' Write a cluster entry. + if (filesystem == 1) + brwword(w, v) + else + brwlong(w, v) +' +' This may do more complicated stuff later. +' +pub unmount + pclose + 'sdspi.stop +pri getfstype : r + if (brlong(pbuf+$36) == constant("F" + ("A" << 8) + ("T" << 16) + ("1" << 24)) and byte[pbuf][$3a]=="6") + return 1 + if (brlong(pbuf+$52) == constant("F" + ("A" << 8) + ("T" << 16) + ("3" << 24)) and byte[pbuf][$56]=="2") + return 2 + ' return r (default return) +pub mount_explicit(DO, CLK, DI, CS) : r | start, sectorspercluster, reserved, rootentries, sectors +{{ +' Mount a volume. The address passed in is passed along to the block +' layer; see the currently used block layer for documentation. If the +' volume mounts, a 0 is returned, else abort is called. +}} + pbuf := @buf ' Initialize pbuf to point at buf + handle0[10] := pbuf + curr_handle := @handle0 ' Initialize curr_handle for the local handle storage + if (pdate == 0) + pdate := constant(((2009-1980) << 25) + (1 << 21) + (27 << 16) + (7 << 11)) + 'unmount + 'sdspi.start_explicit(DO, CLK, DI, CS) + lastread := -1 + dirty := 0 + 'debug.dbprintf0(string("readblock\n")) + sdspi.readblock(0, pbuf) + if (getfstype > 0) + start := 0 + else + start := brlong(pbuf+$1c6) + sdspi.readblock(start, pbuf) + 'debug.dbprintf0(string("readblock\n")) + filesystem := getfstype + 'debug.dbprintf1(string("filesystem = %d\n"), filesystem) + if (filesystem == 0) + abort(-20) ' not a fat16 or fat32 volume + if (brword(pbuf+$0b) <> SECTORSIZE) + abort(-21) ' bad bytes per sector + sectorspercluster := byte[pbuf][$0d] + if (sectorspercluster & (sectorspercluster - 1)) + abort(-22) ' bad sectors per cluster + clustershift := 0 + repeat while (sectorspercluster > 1) + clustershift++ + sectorspercluster >>= 1 + sectorspercluster := 1 << clustershift + clustersize := SECTORSIZE << clustershift + reserved := brword(pbuf+$0e) + if (byte[pbuf][$10] <> 2) + abort(-23) ' not two FATs + sectors := brword(pbuf+$13) + if (sectors == 0) + sectors := brlong(pbuf+$20) + fat1 := start + reserved + if (filesystem == 2) + rootentries := 16 << clustershift + sectorsperfat := brlong(pbuf+$24) + dataregion := (fat1 + 2 * sectorsperfat) - 2 * sectorspercluster + rootdir := (dataregion + (brword(pbuf+$2c) << clustershift)) << SECTORSHIFT + rootdirend := rootdir + (rootentries << DIRSHIFT) + endofchain := $ffffff0 + else + rootentries := brword(pbuf+$11) + sectorsperfat := brword(pbuf+$16) + rootdir := (fat1 + 2 * sectorsperfat) << SECTORSHIFT + rootdirend := rootdir + (rootentries << DIRSHIFT) + dataregion := 1 + ((rootdirend - 1) >> SECTORSHIFT) - 2 * sectorspercluster + endofchain := $fff0 + rootdir0 := rootdir + rootdirend0 := rootdirend + if (brword(pbuf+$1fe) <> $aa55) + abort(-24) ' bad FAT signature + totclusters := ((sectors - dataregion + start) >> clustershift) + ' return r (default return) +' +' For compatibility, a single pin. +' +pub mount(basepin) : r | start, sectorspercluster, reserved, rootentries, sectors + return mount_explicit(basepin, basepin+1, basepin+2, basepin+3) +pri readbytec(byteloc) +' +' Read a byte address from the disk through the metadata buffer and +' return a pointer to that location. +' + readblockc(byteloc >> SECTORSHIFT) + return @buf2 + (byteloc & constant(SECTORSIZE - 1)) +pri readfat(clust) +' +' Read a fat location and return a pointer to the location of that +' entry. +' + fatptr := (fat1 << SECTORSHIFT) + (clust << filesystem) + return readbytec(fatptr) +pri followchain : r +' +' Follow the fat chain and update the writelink. +' + r := brclust(readfat(fclust)) + writelink := fatptr + ' return r (default return) +pri nextcluster : r +' +' Read the next cluster and return it. Set up writelink to +' point to the cluster we just read, for later updating. If the +' cluster number is bad, return a negative number. +' + r := followchain + if (r < 2 or r => totclusters) + abort(-9) ' bad cluster value + ' return r (default return) +pri freeclusters(clust) | bp +' +' Free an entire cluster chain. Used by remove and by overwrite. +' Assumes the pointer has already been cleared/set to end of chain. +' + repeat while (clust < endofchain) + if (clust < 2) + abort(-26) ' bad cluster number") + bp := readfat(clust) + clust := brclust(bp) + brwclust(bp, 0) + flushifdirty +pri datablock +' +' Calculate the block address of the current data location. +' + return (fclust << clustershift) + dataregion + ((floc >> SECTORSHIFT) & ((1 << clustershift) - 1)) +pri uc(c) +' +' Compute the upper case version of a character. +' + if ("a" =< c and c =< "z") + return c - 32 + return c +pri pflushbuf(rcnt, metadata) : r | cluststart, newcluster, count, i, ptr +' +' Flush the current buffer, if we are open for write. This may +' allocate a new cluster if needed. If metadata is true, the +' metadata is written through to disk including any FAT cluster +' allocations and also the file size in the directory entry. +' + if (direntry == 0) + abort(-27) ' not open for writing + if (rcnt > 0) ' must *not* allocate cluster if flushing an empty buffer + if (frem < SECTORSIZE) + ' find a new clustercould be anywhere! If possible, stay on the + ' same page used for the last cluster. + newcluster := -1 + cluststart := fclust & (!((SECTORSIZE >> filesystem) - 1)) + count := 2 + repeat + readfat(cluststart) + repeat i from 0 to SECTORSIZE - 1<> filesystem) + if (newcluster => totclusters) + newcluster := -1 + quit + if (newcluster > 1) + brwclust(@buf2+i, endofchain+$f) + if (writelink == 0) + brwword(readbytec(direntry)+$1a, newcluster) + writelink := (direntry&(SECTORSIZE-filesystem)) + brwlong(@buf2+writelink+$1c, floc+bufat) + if (filesystem == 2) + brwword(@buf2+writelink+$14, newcluster>>16) + else + brwclust(readbytec(writelink), newcluster) + writelink := fatptr + i + fclust := newcluster + frem := clustersize + quit + else + cluststart += (SECTORSIZE >> filesystem) + if (cluststart => totclusters) + cluststart := 0 + count-- + if (rcnt < 0) + rcnt := -5 ' No space left on device + quit + if (frem => SECTORSIZE) + sdspi.writeblock(datablock, pbuf) + if (rcnt == SECTORSIZE) ' full buffer, clear it + floc += rcnt + frem -= rcnt + bufat := 0 + bufend := rcnt + if (rcnt < 0 or metadata) ' update metadata even if error + readblockc(direntry >> SECTORSHIFT) ' flushes unwritten FAT too + 'brwlong(@buf2+(direntry & (SECTORSIZE-filesystem))+$1c, floc+bufat) + ptr := @buf2+(direntry & (SECTORSIZE-filesystem)) + brwlong(ptr+$1c, floc+bufat) + brwlong(ptr+$16, pdate) ' write last modified date and time + flushifdirty + if (rcnt < 0) + abort(rcnt) + return rcnt +pub pflush +{{ +' Call flush with the current data buffer location, and the flush +' metadata flag set. +}} + return pflushbuf(bufat, 1) +pri pfillbuf : r +' +' Get some data into an empty buffer. If no more data is available, +' return -1. Otherwise return the number of bytes read into the +' buffer. +' + if (floc => filesize) + return -1 + if (frem == 0) + fclust := nextcluster + frem := (clustersize) <# (filesize - floc) + sdspi.readblock(datablock, pbuf) + r := SECTORSIZE + if (floc + r => filesize) + r := filesize - floc + floc += r + frem -= r + bufat := 0 + bufend := r + ' return r (default return) +pub pclose : r +{{ +' Flush and close the currently open file if any. Also reset the +' pointers to valid values. If there is no error, 0 will be returned. +}} + if (direntry) + r := pflush + bufat := 0 + bufend := 0 + filesize := 0 + floc := 0 + frem := 0 + writelink := 0 + direntry := 0 + fclust := 0 + firstcluster := 0 + sdspi.release + ' return r (default return) +pub setdate(year, month, day, hour, minute, second) +{{ +' Set the current date and time, as a long, in the format +' required by FAT16. Various limits are not checked. +}} + pdate := ((year-1980) << 25) + (month << 21) + (day << 16) + pdate += (hour << 11) + (minute << 5) + (second >> 1) + +PUB ConvertName(fname1, fname2) | i + i := 0 + repeat while (i<8 and byte[fname1] and byte[fname1] <> ".") + byte[fname2][i++] := uc(byte[fname1++]) + repeat while (i<8) + byte[fname2][i++] := " " + repeat while (byte[fname1] and byte[fname1] <> ".") + fname1++ + if (byte[fname1] == ".") + fname1++ + repeat while (i<11 and byte[fname1]) + byte[fname2][i++] := uc(byte[fname1++]) + repeat while (i < 11) + byte[fname2][i++] := " " + +pub popen(s, mode) : r | i, sentinel, dirptr, freeentry +{{ +' Close any currently open file, and open a new one with the given +' file name and mode. Mode can be "r" "w" "a" or "d" (delete). +' If the file is opened successfully, 0 will be returned. If the +' file did not exist, and the mode was not "w" or "a", -1 will be +' returned. Otherwise abort will be called with a negative error +' code. +}} + pclose + ConvertName(s, @padname) + sentinel := 0 + freeentry := 0 + repeat dirptr from rootdir to rootdirend - DIRSIZE step DIRSIZE + s := readbytec(dirptr) + if (freeentry == 0 and (byte[s] == 0 or byte[s] == $e5)) + freeentry := dirptr + if (byte[s] == 0) + sentinel := dirptr + quit + repeat i from 0 to 10 + if (padname[i] <> byte[s][i]) + quit + if (i == 11 and 0 == (byte[s][$0b] & $8)) '$18)) ' this always returns + fclust := brword(s+$1a) + if (filesystem == 2) + fclust += brword(s+$14) << 16 + firstcluster := fclust + filesize := brlong(s+$1c) + if (mode == "r") + frem := (clustersize) <# (filesize) + direntry0 := dirptr + return 0 + if (byte[s][11] & $d9) + abort(-6) ' no permission to write + if (mode == "d") + brwword(s, $e5) + if (fclust) + freeclusters(fclust) + flushifdirty + return 0 + if (mode == "w") + brwword(s+$1a, 0) + brwword(s+$14, 0) + brwlong(s+$1c, 0) + writelink := 0 + direntry := dirptr + if (fclust) + freeclusters(fclust) + bufend := SECTORSIZE + fclust := 0 + filesize := 0 + frem := 0 + return 0 + elseif (mode == "a") +' this code will eventually be moved to seek + frem := filesize + freeentry := clustersize + if (fclust => endofchain) + fclust := 0 + repeat while (frem > freeentry) + if (fclust < 2) + abort(-7) ' eof repeat while following chain + fclust := nextcluster + frem -= freeentry + floc := filesize & constant(!(SECTORSIZE - 1)) + bufend := SECTORSIZE + bufat := frem & constant(SECTORSIZE - 1) + writelink := 0 + direntry := dirptr + if (bufat) + sdspi.readblock(datablock, pbuf) + frem := freeentry - (floc & (freeentry - 1)) + else + if (fclust < 2 or frem == freeentry) + frem := 0 + else + frem := freeentry - (floc & (freeentry - 1)) + if (fclust => 2) + followchain + return 0 + else + abort(-3) ' bad argument + if (mode <> "w" and mode <> "a") + return -1 ' not found + direntry := freeentry + if (direntry == 0) + abort(-2) ' no empty directory entry + ' write (or new append): create valid directory entry + s := readbytec(direntry) + bytefill(s, 0, DIRSIZE) + bytemove(s, @padname, 11) + brwword(s+$1a, 0) + brwword(s+$14, 0) + i := pdate + brwlong(s+$e, i) ' write create time and date + brwlong(s+$16, i) ' write last modified date and time + if (direntry == sentinel and direntry + DIRSIZE < rootdirend) + brwword(readbytec(direntry+DIRSIZE), 0) + flushifdirty + writelink := 0 + fclust := 0 + bufend := SECTORSIZE + ' return r (default return) +pub get_filesize + return filesize +pub pread(ubuf, count) : r | t +{{ +' Read count bytes into the buffer ubuf. Returns the number of bytes +' successfully read, or a negative number if there is an error. +' The buffer may be as large as you want. +}} + repeat while (count > 0) + if (bufat => bufend) + t := pfillbuf + if (t =< 0) + if (r > 0) +' parens below prevent this from being optimized out + return (r) + return t + t := (bufend - bufat) <# (count) + if ((t | (ubuf) | bufat) & 3) + bytemove(ubuf, pbuf+bufat, t) + else + longmove(ubuf, pbuf+bufat, t>>2) + bufat += t + r += t + ubuf += t + count -= t + ' return r (default return) +pub pgetc | t +{{ +' Read and return a single character. If the end of file is +' reached, -1 will be returned. If an error occurs, a negative +' number will be returned. +}} + if (bufat => bufend) + t := pfillbuf + if (t =< 0) + return -1 + return (byte[pbuf][bufat++]) +pub pwrite(ubuf, count) : r | t +{{ +' Write count bytes from the buffer ubuf. Returns the number of bytes +' successfully written, or a negative number if there is an error. +' The buffer may be as large as you want. +}} + repeat while (count > 0) + if (bufat => bufend) + pflushbuf(bufat, 0) + t := (bufend - bufat) <# (count) + if ((t | (ubuf) | bufat) & 3) + bytemove(pbuf+bufat, ubuf, t) + else + longmove(pbuf+bufat, ubuf, t>>2) + r += t + bufat += t + ubuf += t + count -= t + ' return r (default return) +{{ +' Write a null-terminated string to the file. +}} +pub pputs(b) + return pwrite(b, strsize(b)) +pub pputc(c) : r +{{ +' Write a single character into the file open for write. Returns +' 0 if successful, or a negative number if some error occurred. +}} + if (bufat == SECTORSIZE) + if (pflushbuf(SECTORSIZE, 0) < 0) + return -1 + byte[pbuf][bufat++] := c + ' return r (default return) +{{ +' Seek. Right now will only seek within the current cluster. +' Added for PrEdit so he can debug; do not use with files larger +' than one cluster (and make that cluster size 32K please.) +' +' Returns -1 on failure. Make sure to check this return code! +' +' We only support reads right now (but writes won"t be too hard to +' add). +}} +pub seek(pos) | delta + if (direntry or pos < 0 or pos > filesize) + return -1 + delta := (floc - bufend) & - clustersize + if (pos < delta) + fclust := firstcluster + frem := (clustersize) <# (filesize) + floc := 0 + bufat := 0 + bufend := 0 + delta := 0 + repeat while (pos => delta + clustersize) + fclust := nextcluster + floc += clustersize + delta += clustersize + frem := (clustersize) <# (filesize - floc) + bufat := 0 + bufend := 0 + if (bufend == 0 or pos < floc - bufend or pos => floc - bufend + SECTORSIZE) + ' must change buffer + delta := floc + frem + floc := pos & - SECTORSIZE + frem := delta - floc + pfillbuf + bufat := pos & (SECTORSIZE - 1) + return 0 +pub tell + return floc + bufat - bufend +pub opendir | off +{{ +' Close the currently open file, and set up the read buffer for +' calls to nextfile. +}} + pclose + off := rootdir - (dataregion << SECTORSHIFT) + fclust := off >> (clustershift + SECTORSHIFT) + floc := off - (fclust << (clustershift + SECTORSHIFT)) + frem := rootdirend - rootdir + filesize := floc + frem + return 0 +pub nextfile(fbuf) | i, t, at, lns +{{ +' Find the next file in the root directory and extract its +' (8.3) name into fbuf. Fbuf must be sized to hold at least +' 13 characters (8 + 1 + 3 + 1). If there is no next file, +' -1 will be returned. If there is, 0 will be returned. +}} + repeat + if (bufat => bufend) + t := pfillbuf + if (t < 0) + return t + if (((floc >> SECTORSHIFT) & ((1 << clustershift) - 1)) == 0) + fclust++ + at := pbuf + bufat + if (byte[at] == 0) + return -1 + bufat += DIRSIZE + if (byte[at] <> $e5 and (byte[at][$0b] & $18) == 0) + lns := fbuf + repeat i from 0 to 10 + byte[fbuf] := byte[at][i] + fbuf++ + if (byte[at][i] <> " ") + lns := fbuf + if (i == 7 or i == 10) + fbuf := lns + if (i == 7) + byte[fbuf] := "." + fbuf++ + byte[fbuf] := 0 + return 0 +{{ +' Utility routines; may be removed. +}} +pub getclustersize + return clustersize +pub getclustercount + return totclusters + +{{ +' Permission is hereby granted, free of charge, to any person obtaining +' a copy of this software and associated documentation files +' (the "Software"), to deal in the Software without restriction, +' including without limitation the rights to use, copy, modify, merge, +' publish, distribute, sublicense, and/or sell copies of the Software, +' and to permit persons to whom the Software is furnished to do so, +' subject to the following conditions: +' +' The above copyright notice and this permission notice shall be included +' in all copies or substantial portions of the Software. +' +' THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +' EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +' MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +' IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +' CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +' TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +' SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +}} +DAT +filesystem long 0 ' 0 = unmounted, 1 = fat16, 2 = fat32 +rootdir long 0 ' the byte address of the start of the root directory +rootdirend long 0 ' the byte immediately following the root directory. +dataregion long 0 ' the start of the data region, offset by two sectors +clustershift long 0 ' log base 2 of blocks per cluster +clustersize long 0 ' total size of cluster in bytes +fat1 long 0 ' the block address of the fat1 space +totclusters long 0 ' how many clusters in the volume +sectorsperfat long 0 ' how many sectors per fat +endofchain long 0 ' end of chain marker (with a 0 at the end) +pdate long 0 ' current date +lastread long 0 ' the block address of the buf2 contents +dirty long 0 ' nonzero if buf2 is dirty +buf2 byte 0[SECTORSIZE] ' main metadata buffer +padname byte 0[11] ' filename buffer + +rootdir0 long 0 +rootdirend0 long 0 +direntry0 long 0 + +'============================================================================= +' New methods that support file handles +'============================================================================= +CON + handle_size = 44 + handle_longs = 11 + + ERROR_NULL_HANDLE = -30 + +DAT + curr_handle long 0 ' Current handle pointer + readdir_block long 0[11] ' Arributes, filesize and file name + +PUB handlesize + return handle_size + SECTORSIZE ' Total storage needed for an instance + +PRI loadhandle(handle) + ifnot handle + return + if curr_handle <> handle + if curr_handle + longmove(curr_handle, @fclust, handle_longs) + curr_handle := handle + longmove(@fclust, curr_handle, handle_longs) + result := 1 + +PUB hreaddir(handle) | retval, attrib, fsize, fname[4], ptr1, ptr2, val + ifnot loadhandle(handle) + return ERROR_NULL_HANDLE + + ptr2 := @readdir_block[3] + repeat + retval := \pread(ptr2, 32) ' Read a directory entry + if retval < 0 + return + ifnot byte[ptr2] ' Check for the end of the directory + return + attrib := byte[ptr2 + 11] ' Get the attribute byte + if (byte[ptr2] <> $e5) and ((attrib & $08) == 0) ' Exit loop if a valid file or directory entry + quit + + fsize := readdir_block[10] ' Get the file size + ptr1 := @fname + ' Get up to eight file name characters + repeat 8 + val := byte[ptr2++] + if val =< 32 or val > 126 + quit + byte[ptr1++] := val + ' Check for an extension + ptr2 := @readdir_block[5] + val := byte[ptr2] + if val > 32 and val =< 128 + byte[ptr1++] := "." + ' Get up to 3 extension characters + repeat 3 + val := byte[ptr2++] + if val =< 32 or val > 126 + quit + byte[ptr1++] := val + byte[ptr1] := 0 ' NULL terminate the file name string + readdir_block[0] := fsize ' Put the file size in the first long + readdir_block[1] := attrib ' Put attribute bits in the second long + bytemove(@readdir_block[2], @fname, 13) ' Put the file name starting at the third long + return @readdir_block + +PUB hopen(handle, fname, mode) + if mode == "d" + loadhandle(@handle0) + else + ifnot handle + return ERROR_NULL_HANDLE + longfill(handle, 0, 10) + long[handle][10] := handle + handle_size + loadhandle(handle) + result := \popen(fname, mode) + +PUB hclose(handle) | retval + ifnot loadhandle(handle) + return ERROR_NULL_HANDLE + ifnot \pclose + result := 1 + curr_handle := 0 + +' This routine writes a chunk to a file +PUB hwrite(handle, buffer, num) + ifnot loadhandle(handle) + return ERROR_NULL_HANDLE + result := \pwrite(buffer, num) + +' This routine reads a chunk from a file +PUB hread(handle, buffer, num) + ifnot loadhandle(handle) + return ERROR_NULL_HANDLE + result := \pread(buffer, num) + +PUB hopendir(handle) + ifnot handle + return ERROR_NULL_HANDLE + longfill(handle, 0, 10) + long[handle][10] := handle + handle_size + loadhandle(handle) + \opendir + +PUB hnextfile(fbuf) + loadhandle(@handle0) + result := \nextfile(fbuf) + +PUB hget_filesize(handle) + ifnot loadhandle(handle) + return ERROR_NULL_HANDLE + result := \get_filesize + +PUB hseek(handle, position) + ifnot loadhandle(handle) + return ERROR_NULL_HANDLE + result := \seek(position) + +PUB htell(handle) + ifnot loadhandle(handle) + return ERROR_NULL_HANDLE + result := \tell + +PUB hflush(handle) + ifnot loadhandle(handle) + return ERROR_NULL_HANDLE + result := \pflush + +'======================================================== +PUB hclosedir(handle) + hclose(handle) + +PUB InitRootDirectory + rootdir := rootdir0 + rootdirend := rootdirend0 + +PUB InitSubDirectory(fname) | tempbuf[16] + 'debug.dbprintf1(string("InitSubDirectory: %s\n"), fname) + result := hopen(@handle0, fname, "r") + if result + 'debug.dbprintf1(string("InitSubDirectory: failed %d\n"), result) + return + rootdir := ((fclust << clustershift) + dataregion) << SECTORSHIFT + rootdirend := rootdir + 32768 + hclose(@handle0) + +PUB chdir(path) | nextptr + 'debug.dbprintf1(string("chdir: %s\n"), path) + ' Check if starting from root directory + if byte[path] == "/" + path++ + InitRootDirectory + + ' Loop over sub-directory names in path + repeat while byte[path] + nextptr := path + repeat while byte[nextptr] + if byte[nextptr++] == "/" + byte[nextptr-1] := 0 + quit + if (result := InitSubDirectory(path)) + InitRootDirectory + if byte[nextptr] + byte[nextptr-1] := "/" + if result + quit + path := nextptr + +'PUB format(size) +' +'DAT +' mdstr byte "mkdir: %d\n", 0 + +PUB mkdir(fname) | tempbuf[8] + result := hopen(@handle0, fname, "r") + ifnot result + hclose(@handle0) + return -1 + result := hopen(@handle0, fname, "w") + if result + return + byte[readbytec(direntry)+11] := %110000 + dirty := 1 + longfill(@tempbuf, 0, 8) + hwrite(@handle0, @tempbuf, 32) + hclose(@handle0) + flushifdirty + +PUB chmod(fname, modebits) + result := hopen(@handle0, fname, "r") + ifnot result + hclose(@handle0) + byte[readbytec(direntry0)+11] := modebits + dirty := 1 + flushifdirty + +PUB getmod(fname) + result := hopen(@handle0, fname, "r") + if result + return + hclose(@handle0) + result := byte[readbytec(direntry0)+11] + +PUB rename(fname1, fname2) | ptr + result := hopen(@handle0, fname2, "r") + ifnot result + hclose(@handle0) + return -1 + result := hopen(@handle0, fname1, "r") + if result + return + hclose(@handle0) + ptr := readbytec(direntry0) + ConvertName(fname2, ptr) + dirty := 1 + flushifdirty + result := 0 + +' This routine returns the first sector number +PUB hget_first_sector(handle) + ifnot loadhandle(handle) + return ERROR_NULL_HANDLE + result := (firstcluster << clustershift) + dataregion diff --git a/src/glob.spin b/src/glob.spin new file mode 100644 index 0000000..8e2906d --- /dev/null +++ b/src/glob.spin @@ -0,0 +1,154 @@ +'****************************************************************************** +' Copyright (c) 2011, 2012, Dave Hein +' See end of file for terms of use. +'****************************************************************************** +OBJ + c : "clibsd" + +CON + GLOB_START = 0 + GLOB_ACTIVE = 1 + GLOB_DONE = 2 + +'************************************************************ +' This routine returns a 1 if the passed string contains +' a wildcard character of either "*" or "?. Otherwise, +' it will return a value of zero. +'************************************************************ +PUB iswild(str) | char + repeat while (char := byte[str++]) + if char == "*" or char == "?" + return 1 + +'************************************************************ +' This routine matches a string containing the wildcard +' characters of "*" and "?" to a string. +'************************************************************ +PUB patmatch(pat, str) | pat1, str1 + repeat while byte[str] and byte[pat] <> "*" + if byte[pat] <> byte[str] and byte[pat] <> "?" + return 0 + pat++ + str++ + repeat while byte[str] + if byte[pat] == "*" + ifnot byte[++pat] + return 1 + pat1 := pat + str1 := str + 1 + elseif byte[pat] == byte[str] or byte[pat] == "?" + pat++ + str++ + else + pat := pat1 + str := str1++ + repeat while byte[pat] == "*" + pat++ + return not byte[pat] + +'************************************************************ +' This routine will return matches to the string in pat +' if it contains wildcard characters. It will return pat +' if it does not contain a wildcard character, or the +' directory path does not exist. It returns a NULL pointer +' when there are no more items available. +' +' globptr points to a long that will contains the glob state +' information. This long must be cleared prior to calling +' glob the first time for each pattern. +' +' str is used to hold the return value when a directory path +' is included in the pattern. Wildcard characters cannot +' be used in the directory path, but can only be used in the +' file name located after the last "/". +'************************************************************ +PUB glob(pat, globptr, str) | state, handle, pdirent, i, num, fname + + state := byte[globptr][0] + num := byte[globptr][1] + handle := word[globptr][1] + + if state == GLOB_START + ifnot iswild(pat) + byte[globptr][0] := GLOB_DONE + return pat + + ' Locate the last "/" char + c.strcpy(str, pat) + i := num := 0 + repeat while byte[str][i] + if byte[str][i] == "/" + num := i + i++ + + ' Get the directory path + if num + byte[str][++num] := 0 + if iswild(str) + byte[globptr][0] := GLOB_DONE + return pat + else + byte[str] := 0 + byte[globptr][1] := num + + ' Open the directory + handle := c.opendir(str) + ifnot handle + byte[globptr][0] := GLOB_DONE + return pat + byte[globptr][0] := state := GLOB_ACTIVE + word[globptr][1] := handle + + if state == GLOB_ACTIVE + repeat while (pdirent := c.readdir(handle)) + fname := pdirent + 8 + if strcomp(fname, string(".")) or strcomp(fname, string("..")) + next + ConvertToLower(pdirent+8) + ifnot num + if patmatch(pat, pdirent+8) + return pdirent+8 + else + if patmatch(pat+num, pdirent+8) + c.strcpy(str+num, pdirent+8) + return str + + c.closedir(handle) + byte[globptr][0] := GLOB_DONE + +PUB exit(globptr) | state, handle + state := byte[globptr][0] + handle := word[globptr][1] + if state == GLOB_ACTIVE + c.closedir(handle) + c.exit(1) + +PUB ConvertToLower(ptr) | val + repeat while val := byte[ptr] + if (val => "A") and (val =< "Z") + byte[ptr] := val + 32 + ptr++ + +{{ ++-----------------------------------------------------------------------------+ +| TERMS OF USE: MIT License | ++-----------------------------------------------------------------------------+ +|Permission is hereby granted, free of charge, to any person obtaining a copy | +|of this software and associated documentation files (the "Software"), to deal| +|in the Software without restriction, including without limitation the rights | +|to use, copy, modify, merge, publish, distribute, sublicense, and/or sell | +|copies of the Software, and to permit persons to whom the Software is | +|furnished to do so, subject to the following conditions: | +| | +|The above copyright notice and this permission notice shall be included in | +|all copies or substantial portions of the Software. | +| | +|THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | +|IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | +|FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE | +|AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | +|LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,| +|OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE| +|SOFTWARE. | ++-----------------------------------------------------------------------------+ +}} diff --git a/src/grep.spin b/src/grep.spin new file mode 100644 index 0000000..9273ea5 --- /dev/null +++ b/src/grep.spin @@ -0,0 +1,85 @@ +'****************************************************************************** +' Copyright (c) 2011, 2012, Dave Hein +' See end of file for terms of use. +'****************************************************************************** +CON + _clkmode = xinput + _xinfreq = $4e495053 ' SPIN + + ATTR_DIRECTORY = $10 + +OBJ + c : "clibsd" + g : "glob" + +DAT + buffer long 0[50] + +PUB main(argc, argv) | i, ptr, globdata, attrib, infile, str[25] + + c.enter + + if argc < 3 + c.printf0(string("usage: grep string files...\n")) + c.exit(1) + + repeat i from 2 to argc - 1 + globdata := 0 + repeat while (ptr := g.glob(long[argv][i], @globdata, @str)) + attrib := c.getmod(ptr) + if attrib == -1 or (attrib & ATTR_DIRECTORY) + next + infile := c.fopen(ptr, string("r")) + ifnot infile + next + repeat while c.fgets(@buffer, 199, infile) + RemoveCRLF(@buffer) + if FindString(@buffer, long[argv][1]) + if argc == 3 and not g.iswild(long[argv][i]) + c.printf1(string("%s\n"), @buffer) + else + c.printf2(string("%s:%s\n"), ptr, @buffer) + c.fclose(infile) + + c.exit(0) + +PUB FindString(str1, str2) | len + len := strsize(str2) + repeat while byte[str1] + ifnot c.strncmp(str1, str2, len) + return 1 + str1++ + +PUB RemoveCRLF(str) | len + len := strsize(str) + str += len - 1 + repeat while len + if byte[str] <> 10 and byte[str] <> 13 + quit + str-- + len-- + byte[str][1] := 0 + +{{ ++-----------------------------------------------------------------------------+ +| TERMS OF USE: MIT License | ++-----------------------------------------------------------------------------+ +|Permission is hereby granted, free of charge, to any person obtaining a copy | +|of this software and associated documentation files (the "Software"), to deal| +|in the Software without restriction, including without limitation the rights | +|to use, copy, modify, merge, publish, distribute, sublicense, and/or sell | +|copies of the Software, and to permit persons to whom the Software is | +|furnished to do so, subject to the following conditions: | +| | +|The above copyright notice and this permission notice shall be included in | +|all copies or substantial portions of the Software. | +| | +|THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | +|IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | +|FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE | +|AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | +|LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,| +|OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE| +|SOFTWARE. | ++-----------------------------------------------------------------------------+ +}} diff --git a/src/halt.spin b/src/halt.spin new file mode 100644 index 0000000..01a5e94 --- /dev/null +++ b/src/halt.spin @@ -0,0 +1,53 @@ +'****************************************************************************** +' Copyright (c) 2011, 2012, 2013, Dave Hein +' See end of file for terms of use. +'****************************************************************************** +CON + _clkmode = xinput + _xinfreq = $4e495053 ' SPIN + +OBJ + c : "clibsd" + sys : "sysdefs" + +PUB main(argc, argv) | i + c.enter + c.printf0(string("Halting\n")) + waitcnt(cnt + clkfreq/10) + save_sysparm + repeat i from 0 to 7 + if i <> cogid + cogstop(i) + cogstop(cogid) + +pub save_sysparm | outfile, ptr + outfile := c.fopen(string("/_sysparm"), string("w")) + ptr := sys#unixtime + repeat 3 + c.fprintf1(outfile, string("%d\n"), long[ptr]) + ptr += 4 + c.fclose(outfile) + +{{ ++-----------------------------------------------------------------------------+ +| TERMS OF USE: MIT License | ++-----------------------------------------------------------------------------+ +|Permission is hereby granted, free of charge, to any person obtaining a copy | +|of this software and associated documentation files (the "Software"), to deal| +|in the Software without restriction, including without limitation the rights | +|to use, copy, modify, merge, publish, distribute, sublicense, and/or sell | +|copies of the Software, and to permit persons to whom the Software is | +|furnished to do so, subject to the following conditions: | +| | +|The above copyright notice and this permission notice shall be included in | +|all copies or substantial portions of the Software. | +| | +|THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | +|IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | +|FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE | +|AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | +|LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,| +|OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE| +|SOFTWARE. | ++-----------------------------------------------------------------------------+ +}} diff --git a/src/head.spin b/src/head.spin new file mode 100644 index 0000000..fb417da --- /dev/null +++ b/src/head.spin @@ -0,0 +1,87 @@ +'****************************************************************************** +' Copyright (c) 2012, Dave Hein +' See end of file for terms of use. +'****************************************************************************** +CON + _clkmode = xinput + _xinfreq = $4e495053 ' SPIN + +OBJ + c : "clibsd" + +PUB main(argc, argv) | infile, outfile, numlines, ptr, buffer[25] + + c.enter + + infile := 0 + numlines := 10 + ptr := long[argv][1] + + ' Parse the command-line arguments + if argc < 2 + infile := c.getstdin + elseif argc > 3 + usage + elseif byte[ptr] == "-" + ifnot c.isdigit(byte[++ptr]) + usage + c.sscanf1(ptr, string("%d"), @numlines) + if numlines =< 0 + usage + if argc == 2 + infile := c.getstdin + else + ptr := long[argv][2] + elseif argc == 2 + ptr := long[argv][1] + else + usage + + ' Open the input file if not stdin + ifnot infile + infile := c.fopen(ptr, string("r")) + if (infile == 0) + c.printf1(string("Error opening %s\n"), ptr) + c.exit(0) + + outfile := c.getstdout + + repeat numlines + if c.fgets(@buffer, 100, infile) =< 0 + quit + c.fputs(@buffer, outfile) + + if (infile <> c.getstdin) + c.fclose(infile) + + c.free(buffer) + + c.exit(0) + +PUB usage + c.printf0(string("usage: head [-n] file\n")) + c.exit(0) + +{{ ++-----------------------------------------------------------------------------+ +| TERMS OF USE: MIT License | ++-----------------------------------------------------------------------------+ +|Permission is hereby granted, free of charge, to any person obtaining a copy | +|of this software and associated documentation files (the "Software"), to deal| +|in the Software without restriction, including without limitation the rights | +|to use, copy, modify, merge, publish, distribute, sublicense, and/or sell | +|copies of the Software, and to permit persons to whom the Software is | +|furnished to do so, subject to the following conditions: | +| | +|The above copyright notice and this permission notice shall be included in | +|all copies or substantial portions of the Software. | +| | +|THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | +|IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | +|FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE | +|AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | +|LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,| +|OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE| +|SOFTWARE. | ++-----------------------------------------------------------------------------+ +}} diff --git a/src/hello.c b/src/hello.c new file mode 100644 index 0000000..c8b061a --- /dev/null +++ b/src/hello.c @@ -0,0 +1,18 @@ +#include + +/** + * This is the main hello program file. + */ +int main(void) +{ + double f; + int i; + + waitcnt(CLKFREQ+CNT); + i = 100; + f = (double)i; + //f = (double)i; + printf("Hello World - i = %d, f = %lf\n", i, f); + return 0; +} + diff --git a/src/hello.side b/src/hello.side new file mode 100644 index 0000000..759ca8d --- /dev/null +++ b/src/hello.side @@ -0,0 +1,9 @@ +hello.c +>compiler=C +>memtype=cmm main ram compact +>optimize=-Os +>-m32bit-doubles +>-fno-exceptions +>defs::-std=c99 +>-lm +>BOARD::C3F diff --git a/src/history.spin b/src/history.spin new file mode 100644 index 0000000..9f6eb0b --- /dev/null +++ b/src/history.spin @@ -0,0 +1,81 @@ +'****************************************************************************** +' Copyright (c) 2011, 2012, Dave Hein +' See end of file for terms of use. +'****************************************************************************** +CON + _clkmode = xinput + _xinfreq = $4e495053 ' SPIN + +OBJ + c : "clibsd" + +DAT + buffer byte 0[100] + +PUB main(argc, argv) | num + c.enter + if argc > 2 + usage + if argc < 2 + PrintAll + elseif strcomp(long[argv][1], string("-c")) + c.remove(string("/_history")) + elseif isdigit(byte[long[argv][1]]) + c.sscanf1(long[argv][1], string("%d"), @num) + PrintTail(num) + else + usage + c.exit(0) + +PUB usage + c.printf0(string("usage: history [-c] [n]\n")) + c.exit(0) + +PUB isdigit(val) + return val => "0" and val =< "9" + +PUB PrintAll | i, infile + if (infile := c.fopen(string("/_history"), string("r"))) + i := 1 + repeat while c.fgets(@buffer, 100, infile) + c.printf2(string("%d %s"), i++, @buffer) + c.fclose(infile) + +PUB PrintTail(num) | infile, numlines, linenum, line + if (infile := c.fopen(string("/_history"), string("r"))) + numlines := 0 + repeat while c.fgets(@buffer, 100, infile) + numlines++ + c.fseek(infile, 0, c#SEEK_SET) + linenum := numlines - num + line := 1 + repeat while c.fgets(@buffer, 100, infile) + if line > linenum + c.printf2(string("%d %s"), line, @buffer) + line++ + c.fclose(infile) + + +{{ ++-----------------------------------------------------------------------------+ +| TERMS OF USE: MIT License | ++-----------------------------------------------------------------------------+ +|Permission is hereby granted, free of charge, to any person obtaining a copy | +|of this software and associated documentation files (the "Software"), to deal| +|in the Software without restriction, including without limitation the rights | +|to use, copy, modify, merge, publish, distribute, sublicense, and/or sell | +|copies of the Software, and to permit persons to whom the Software is | +|furnished to do so, subject to the following conditions: | +| | +|The above copyright notice and this permission notice shall be included in | +|all copies or substantial portions of the Software. | +| | +|THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | +|IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | +|FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE | +|AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | +|LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,| +|OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE| +|SOFTWARE. | ++-----------------------------------------------------------------------------+ +}} diff --git a/src/kill.spin b/src/kill.spin new file mode 100644 index 0000000..412d254 --- /dev/null +++ b/src/kill.spin @@ -0,0 +1,60 @@ +'****************************************************************************** +' Copyright (c) 2011, 2012, Dave Hein +' See end of file for terms of use. +'****************************************************************************** +CON + _clkmode = xinput + _xinfreq = $4E505350 ' PSPN + +OBJ + c : "clibsd" + sys : "sysdefs" + +PUB main(argc, argv) | cognum, parmstr, entryptr, type + if argc <> 2 + usage + parmstr := long[argv][1] + cognum := byte[parmstr] - "0" + if cognum < 0 OR cognum > 7 OR byte[parmstr][1] + usage + entryptr := sys#cogtable + (cognum * 24) + type := byte[entryptr] + ifnot type + c.printf1(string("Process %d is not running\n"), cognum) + c.exit(1) + + cogstop(cognum) + if type == 1 ' SPIN + c.free(word[entryptr+20]) + + byte[entryptr] := 0 + + c.exit(0) + +PUB usage + c.printf0(string("usage: kill procnum\n")) + c.exit(1) + +{{ ++-----------------------------------------------------------------------------+ +| TERMS OF USE: MIT License | ++-----------------------------------------------------------------------------+ +|Permission is hereby granted, free of charge, to any person obtaining a copy | +|of this software and associated documentation files (the "Software"), to deal| +|in the Software without restriction, including without limitation the rights | +|to use, copy, modify, merge, publish, distribute, sublicense, and/or sell | +|copies of the Software, and to permit persons to whom the Software is | +|furnished to do so, subject to the following conditions: | +| | +|The above copyright notice and this permission notice shall be included in | +|all copies or substantial portions of the Software. | +| | +|THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | +|IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | +|FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE | +|AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | +|LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,| +|OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE| +|SOFTWARE. | ++-----------------------------------------------------------------------------+ +}} diff --git a/src/let.spin b/src/let.spin new file mode 100644 index 0000000..d1d31ae --- /dev/null +++ b/src/let.spin @@ -0,0 +1,148 @@ +'****************************************************************************** +' Copyright (c) 2013 Dave Hein +' See end of file for terms of use. +'****************************************************************************** +CON + _clkmode = xinput + _xinfreq = $4e495053 ' SPIN + +DAT + nullstr long 0 + tokens long 0[25] + buffer long 0[25] + ops2 byte ">=", "<=", "==", "!=", "&&", "||", ">>", "<<", 0 + +OBJ + c : "clibsd" + vs : "varsubs" + +PUB main(argc, argv) | i, argi, num, val, ptr, numstr[5] + c.enter + val := 0 + argi := 1 + repeat while argi < argc + num := tokenize(long[argv][argi]) +{ + c.printf2(string("argi = %d, num = %d: "), argi, num) + i := 0 + repeat while i < num + c.printf1(string(" <%s>"), tokens[i++]) + c.printf0(string("\n")) +} + if num < 3 + argi++ + next + ifnot strcomp(tokens[1], string("=")) + argi++ + next + val := 0 + repeat i from 2 to num - 1 + ptr := tokens[i] + if strcomp(ptr, string("+")) + val += GetValue(tokens[++i]) + elseif strcomp(ptr, string("-")) + val -= GetValue(tokens[++i]) + elseif strcomp(ptr, string("*")) + val *= GetValue(tokens[++i]) + elseif strcomp(ptr, string("/")) + val /= GetValue(tokens[++i]) + elseif strcomp(ptr, string("&")) + val &= GetValue(tokens[++i]) + elseif strcomp(ptr, string("^")) + val ^= GetValue(tokens[++i]) + elseif strcomp(ptr, string("|")) + val |= GetValue(tokens[++i]) + elseif strcomp(ptr, string("<")) + val := (val < GetValue(tokens[++i])) & 1 + else + if strcomp(ptr, string(">")) + val := (val > GetValue(tokens[++i])) & 1 + elseif strcomp(ptr, string(">=")) + val := (val >= GetValue(tokens[++i])) & 1 + elseif strcomp(ptr, string("<=")) + val := (val <= GetValue(tokens[++i])) & 1 + elseif strcomp(ptr, string("==")) + val := (val == GetValue(tokens[++i])) & 1 + elseif strcomp(ptr, string("!=")) + val := (val <> GetValue(tokens[++i])) & 1 + elseif strcomp(ptr, string("<<")) + val <<= GetValue(tokens[++i]) + elseif strcomp(ptr, string(">>")) + val ~>= GetValue(tokens[++i]) + elseif strcomp(ptr, string("&&")) + val := (val and GetValue(tokens[++i])) & 1 + elseif strcomp(ptr, string("||")) + val := (val or GetValue(tokens[++i])) & 1 + elseif strcomp(ptr, string("%")) + val //= GetValue(tokens[++i]) + else + val := GetValue(tokens[i]) + c.sprintf1(@numstr, string("%d"), val) +{ + c.printf3(string("SaveVar(%s, %s, %c)\n"), tokens[0], @numstr, vs#LOCAL_FLAG) +} + vs.SaveVar(tokens[0], @numstr, vs#LOCAL_FLAG) + argi++ + c.exit((val == 0) & 1) + +PUB GetValue(str) + if c.isdigit(byte[str]) + result := vs.atol(str) + else + result := vs.atol(vs.GetVarVal(str)) + +PUB IsDelimeter(val) | ptr + if val == 0 + return 1 + ptr := vs.FindChar(string(" =+-*/&^|<>%!"), val) + if byte[ptr] + return 1 + +PUB IsOperator2(ptr) | ptr2 + ptr2 := @ops2 + repeat while byte[ptr2] + if byte[ptr] == byte[ptr2] and byte[ptr][1] == byte[ptr2][1] + return 1 + ptr2 += 2 + +PUB tokenize(ptr1) | ptr2, num + num := 0 + ptr2 := @buffer + repeat while byte[ptr1] + ptr1 := vs.SkipChar(ptr1, " ") + ifnot byte[ptr1] + quit + tokens[num++] := ptr2 + if IsDelimeter(byte[ptr1]) + if IsOperator2(ptr1) + byte[ptr2++] := byte[ptr1++] + byte[ptr2++] := byte[ptr1++] + else + repeat until IsDelimeter(byte[ptr1]) + byte[ptr2++] := byte[ptr1++] + byte[ptr2++] := 0 + return num + +{{ ++-----------------------------------------------------------------------------+ +| TERMS OF USE: MIT License | ++-----------------------------------------------------------------------------+ +|Permission is hereby granted, free of charge, to any person obtaining a copy | +|of this software and associated documentation files (the "Software"), to deal| +|in the Software without restriction, including without limitation the rights | +|to use, copy, modify, merge, publish, distribute, sublicense, and/or sell | +|copies of the Software, and to permit persons to whom the Software is | +|furnished to do so, subject to the following conditions: | +| | +|The above copyright notice and this permission notice shall be included in | +|all copies or substantial portions of the Software. | +| | +|THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | +|IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | +|FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE | +|AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | +|LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,| +|OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE| +|SOFTWARE. | ++-----------------------------------------------------------------------------+ +}} diff --git a/src/linefeed.spin b/src/linefeed.spin new file mode 100644 index 0000000..dff13b5 --- /dev/null +++ b/src/linefeed.spin @@ -0,0 +1,52 @@ +'****************************************************************************** +' Copyright (c) 2011, 2012, Dave Hein +' See end of file for terms of use. +'****************************************************************************** +CON + _clkmode = xinput + _xinfreq = $4e495053 ' SPIN + +OBJ + c : "clibsd" + sys : "sysdefs" + +PUB main(argc, argv) | i + c.enter + if argc <> 2 + usage + if strcomp(long[argv][1], string("on")) + long[sys#config] |= 1 + elseif strcomp(long[argv][1], string("off")) + long[sys#config] &= !1 + else + usage + c.exit(0) + +PUB usage + c.printf0(string("usage: linefeed parm\n")) + c.printf0(string(" parm is either on or off\n")) + c.exit(1) + +{{ ++-----------------------------------------------------------------------------+ +| TERMS OF USE: MIT License | ++-----------------------------------------------------------------------------+ +|Permission is hereby granted, free of charge, to any person obtaining a copy | +|of this software and associated documentation files (the "Software"), to deal| +|in the Software without restriction, including without limitation the rights | +|to use, copy, modify, merge, publish, distribute, sublicense, and/or sell | +|copies of the Software, and to permit persons to whom the Software is | +|furnished to do so, subject to the following conditions: | +| | +|The above copyright notice and this permission notice shall be included in | +|all copies or substantial portions of the Software. | +| | +|THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | +|IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | +|FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE | +|AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | +|LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,| +|OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE| +|SOFTWARE. | ++-----------------------------------------------------------------------------+ +}} diff --git a/src/ls.spin b/src/ls.spin new file mode 100644 index 0000000..df6a5fd --- /dev/null +++ b/src/ls.spin @@ -0,0 +1,280 @@ +'****************************************************************************** +' Copyright (c) 2011, 2012, Dave Hein +' See end of file for terms of use. +'****************************************************************************** +CON + _clkmode = xinput + _xinfreq = $4e495053 ' SPIN + + ATTR_DIRECTORY = $10 + +OBJ + c : "clibsd" + g : "glob" + r : "resolve" + +DAT + longlist long 0 + argi long 0 + total long 0 + numfiles long 0 + column long 1 + lastlen long 14 + hide long 1 + globdata long 0 + str long 0[25] + columns long 0 + pdirent long 0 + +PUB main(argc, argv) | path, attrib, argj + c.enter + total := 0 + numfiles := 0 + argi := 1 + longlist := 0 + columns := ((c.getconfig >> 16) & 255) / 14 + ifnot columns + columns := 5 + + ' Parse the option flags + repeat while argi < argc + ifnot byte[long[argv][argi]] == "-" + quit + if strcomp(long[argv][argi], string("-l")) + longlist := 1 + elseif strcomp(long[argv][argi], string("-e")) + longlist := 2 + elseif strcomp(long[argv][argi], string("-a")) + hide := 0 + else + usage + argi++ + + ' List current directory if nothing specified + if argi => argc + listdirectory(string(".")) + c.exit(0) + + ' List a single item + elseif argi == argc - 1 and not g.iswild(long[argv][argi]) + path := long[argv][argi] + r.resolve_path(path, @str) + if strcomp(@str, string("/")) + attrib := ATTR_DIRECTORY + else + attrib := c.getmod(path) + if attrib == -1 + c.printf1(string("%s does not exist\n"), path) + elseifnot attrib & ATTR_DIRECTORY + listfile(path, attrib) + else + listdirectory(path) + c.exit(0) + + ' List multiple items + else + ' list files + argj := argi + repeat while argi < argc + globdata := 0 + repeat while (path := g.glob(long[argv][argi], @globdata, @str)) + 'c.printf1(string("<%s>"), path) + attrib := c.getmod(path) + if attrib == -1 + c.printf1(string("%s does not exist\n"), path) + elseifnot attrib & ATTR_DIRECTORY + listfile(path, attrib) + argi++ + if column <> 1 + c.printf0(string("\n")) + column := 1 + lastlen := 14 + c.printf0(string("\n")) + + ' list directories + argi := argj + repeat while argi < argc + globdata := 0 + repeat while (path := g.glob(long[argv][argi], @globdata, @str)) + 'c.printf1(string("<%s>"), path) + attrib := c.getmod(path) + if attrib <> -1 and (attrib & ATTR_DIRECTORY) + c.printf1(string("%s:\n"), path) + listdirectory(path) + c.printf0(string("\n")) + argi++ + if column <> 1 + c.printf0(string("\n")) + c.exit(0) + +PUB ConvertToLower(ptr) | val + repeat while val := byte[ptr] + if (val => "A") and (val =< "Z") + byte[ptr] := val + 32 + ptr++ + +PUB listfile(path, attrib) | infile, filesize + infile := c.fopen(path, string("r")) + ifnot infile + c.printf1(string("Could not open file %s\n"), path) + return + c.fseek(infile, 0, c#SEEK_END) + filesize := c.ftell(infile) + c.fclose(infile) + printfile(@result, path, filesize, attrib) + +PUB listdirectory(path) | handle, filesize, attrib + handle := c.opendir(path) + ifnot handle + c.printf1(string("Could not open directory %s\n"), path) + return + + repeat while (pdirent := c.readdir(handle)) + if hide and (byte[pdirent + 8] == "." or byte[pdirent + 8] == "_") + next + filesize := long[pdirent][0] + attrib := long[pdirent][1] + total += filesize + printfile(path, pdirent+8, filesize, attrib) + if longlist + c.printf2(STRING("%6d files, %d bytes\n"), numfiles, total) + elseif column <> 1 + c.printf0(string("\n")) + column := 1 + lastlen := 14 + c.closedir(handle) + +PUB printfile(path, fname, filesize, attrib) | astr[2], aptr, ptr, date, time + aptr := @astr + c.strcpy(aptr, string("drwx")) + ifnot attrib & $10 + byte[aptr] := "-" + if attrib & 1 + byte[aptr][2] := "-" + ifnot attrib & $20 + byte[aptr][3] := "-" + ConvertToLower(fname) + if longlist == 1 or longlist == 2 + 'c.printf3(STRING("%s %6d %s\n"), @astr, filesize, fname) + time := word[pdirent][17] + date := word[pdirent][18] + c.printf2(STRING("%s %6d "), @astr, filesize) + c.printf3(string("%2.2d/%2.2d/%2.2d "), (date>>9)+1980, (date >> 5)&15, date&31) + c.printf3(string("%2.2d:%2.2d:%2.2d "), time >> 11, (time >> 5)&63, (time&31)<<1) + if longlist == 2 + ptr := filetype(attrib, path, fname) + c.printf1(STRING("%s "), ptr) + c.printf1(STRING("%s\n"), fname) + else + printcompact(fname) + 'c.printf1(string("%s\n"), fname) + numfiles++ + +PUB filetype(attrib, path, fname) | len, infile, typestr[20] + + if attrib & $10 + return string("Directory") + + if (len := strsize(path)) + c.strcpy(@typestr, path) + if byte[@typestr][len-1] <> "/" + byte[@typestr][len++] := "/" + byte[@typestr][len] := 0 + c.strcat(@typestr, fname) + else + c.strcpy(@typestr, fname) + + infile := c.fopen(@typestr, string("r")) + + ifnot infile + return string(" ") + + longfill(@typestr, 0, 8) + c.fread(@typestr, 1, 32, infile) + c.fclose(infile) + + ifnot c.strncmp(@typestr, string("SPINb"), 5) + return string("Spin App ") + + ifnot c.strncmp(@typestr, string("CAPPb"), 5) + return string("C App ") + + ifnot c.strncmp(@typestr, string("SPROb"), 5) + return string("Spin Prog") + + ifnot c.strncmp(@typestr, string("CPROb"), 5) + return string("C Program") + + if typestr => 80_000_000 and typestr =< 120_000_000 + return string("SAProgram") + + ifnot c.strncmp(@typestr, string("#shell"), 6) + return string("Script ") + + repeat while byte[fname] + if byte[fname] == "." + quit + fname++ + + if strcomp(fname, string(".txt")) + return string("Text file") + + if strcomp(fname, string(".spn")) + return string("Spin src ") + + if strcomp(fname, string(".spa")) + return string("Spasm src") + + if strcomp(fname, string(".c")) + return string("C source ") + + if strcomp(fname, string(".fth")) + return string("Forth src") + + return string(" ") + +PUB printcompact(fname) | len, buffer[4] + + if 14 > lastlen + repeat 14 - lastlen + c.putchar(" ") + elseif column <> 1 + c.putchar(" ") + + if column => columns + c.printf1(string("%s\n"), fname) + column := 1 + lastlen := 14 + + else + c.printf1(string("%s"), fname) + column++ + lastlen := strsize(fname) + +PUB usage + c.printf0(string("ls [-l] [-a] [-e] [file list]\n")) + c.exit(2) + +{{ ++-----------------------------------------------------------------------------+ +| TERMS OF USE: MIT License | ++-----------------------------------------------------------------------------+ +|Permission is hereby granted, free of charge, to any person obtaining a copy | +|of this software and associated documentation files (the "Software"), to deal| +|in the Software without restriction, including without limitation the rights | +|to use, copy, modify, merge, publish, distribute, sublicense, and/or sell | +|copies of the Software, and to permit persons to whom the Software is | +|furnished to do so, subject to the following conditions: | +| | +|The above copyright notice and this permission notice shall be included in | +|all copies or substantial portions of the Software. | +| | +|THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | +|IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | +|FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE | +|AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | +|LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,| +|OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE| +|SOFTWARE. | ++-----------------------------------------------------------------------------+ +}} diff --git a/src/man.spin b/src/man.spin new file mode 100644 index 0000000..2ce6750 --- /dev/null +++ b/src/man.spin @@ -0,0 +1,78 @@ +'****************************************************************************** +' Copyright (c) 2011, 2012, Dave Hein +' See end of file for terms of use. +'****************************************************************************** +CON + _clkmode = xinput + _xinfreq = $4e495053 ' SPIN + +OBJ + c : "clibsd" + +PUB main(argc, argv) | infile, line, char, buffer[25], maxlines + c.enter + if (argc <> 2) + c.printf0(string("usage: man application\n")) + c.exit(0) + else + c.strcpy(@buffer, string("/manpages/")) + c.strcat(@buffer, long[argv][1]) + c.strcat(@buffer, string(".txt")) + infile := c.fopen(@buffer, string("r")) + if (infile == 0) + c.printf1(string("No manual entry for %s\n"), long[argv][1]) + c.exit(0) + + ifnot (maxlines := (c.getconfig >> 8) & 255) + maxlines := 24 + + line := 0 + repeat while c.fgets(@buffer, 100, infile) + RemoveCRLF(@buffer) + c.printf1(string("%s\n"), @buffer) + if ++line => maxlines - 1 + c.printf0(string("")) + char := c.getchar + c.printf0(string("\b\b\b\b\b\b \b\b\b\b\b\b")) + if char == "q" OR char == 3 + quit + elseif char == 13 + line-- + else + line := 0 + c.fclose(infile) + c.exit(0) + +PUB RemoveCRLF(str) | len + len := strsize(str) + str += len - 1 + repeat while len + if byte[str] <> 10 and byte[str] <> 13 + quit + str-- + len-- + byte[str][1] := 0 + +{{ ++-----------------------------------------------------------------------------+ +| TERMS OF USE: MIT License | ++-----------------------------------------------------------------------------+ +|Permission is hereby granted, free of charge, to any person obtaining a copy | +|of this software and associated documentation files (the "Software"), to deal| +|in the Software without restriction, including without limitation the rights | +|to use, copy, modify, merge, publish, distribute, sublicense, and/or sell | +|copies of the Software, and to permit persons to whom the Software is | +|furnished to do so, subject to the following conditions: | +| | +|The above copyright notice and this permission notice shall be included in | +|all copies or substantial portions of the Software. | +| | +|THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | +|IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | +|FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE | +|AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | +|LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,| +|OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE| +|SOFTWARE. | ++-----------------------------------------------------------------------------+ +}} diff --git a/src/mkdir.spin b/src/mkdir.spin new file mode 100644 index 0000000..ee91659 --- /dev/null +++ b/src/mkdir.spin @@ -0,0 +1,39 @@ +'****************************************************************************** +' Copyright (c) 2011, 2012, Dave Hein +' See end of file for terms of use. +'****************************************************************************** +CON + _clkmode = xinput + _xinfreq = $4e495053 ' SPIN + +OBJ + c : "clibsd" + +PUB main(argc, argv) + c.enter + c.mkdir(long[argv][1]) + c.exit(0) + +{{ ++-----------------------------------------------------------------------------+ +| TERMS OF USE: MIT License | ++-----------------------------------------------------------------------------+ +|Permission is hereby granted, free of charge, to any person obtaining a copy | +|of this software and associated documentation files (the "Software"), to deal| +|in the Software without restriction, including without limitation the rights | +|to use, copy, modify, merge, publish, distribute, sublicense, and/or sell | +|copies of the Software, and to permit persons to whom the Software is | +|furnished to do so, subject to the following conditions: | +| | +|The above copyright notice and this permission notice shall be included in | +|all copies or substantial portions of the Software. | +| | +|THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | +|IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | +|FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE | +|AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | +|LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,| +|OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE| +|SOFTWARE. | ++-----------------------------------------------------------------------------+ +}} diff --git a/src/more.spin b/src/more.spin new file mode 100644 index 0000000..b746ce6 --- /dev/null +++ b/src/more.spin @@ -0,0 +1,85 @@ +'****************************************************************************** +' Copyright (c) 2011, 2012, Dave Hein +' See end of file for terms of use. +'****************************************************************************** +CON + _clkmode = xinput + _xinfreq = $4e495053 ' SPIN + +OBJ + c : "clibsd" + +PUB main(argc, argv) | buffer, infile, outfile, line, char, maxlines + c.enter + buffer := c.malloc(100) + ifnot buffer + c.printf0(string("Could not malloc buffer\n")) + + if (argc < 2) + infile := c.getstdin + else + infile := c.fopen(long[argv][1], string("r")) + if (infile == 0) + c.printf1(string("Error opening %s\n"), long[argv][1]) + c.free(buffer) + c.exit(0) + + outfile := c.getstdout + + ifnot (maxlines := (c.getconfig >> 8) & 255) + maxlines := 24 + + line := 0 + repeat while c.fgets(buffer, 100, infile) + RemoveCRLF(buffer) + c.fprintf1(outfile, string("%s\n"), buffer) + if ++line => maxlines - 1 + c.printf0(string("")) + char := c.getchar + c.printf0(string("\b\b\b\b\b\b \b\b\b\b\b\b")) + if char == "q" OR char == 3 + quit + elseif char == 13 + line-- + else + line := 0 + + if (infile <> c.getstdin) + c.fclose(infile) + + c.free(buffer) + c.exit(0) + +PUB RemoveCRLF(str) | len + len := strsize(str) + str += len - 1 + repeat while len + if byte[str] <> 10 and byte[str] <> 13 + quit + str-- + len-- + byte[str][1] := 0 + +{{ ++-----------------------------------------------------------------------------+ +| TERMS OF USE: MIT License | ++-----------------------------------------------------------------------------+ +|Permission is hereby granted, free of charge, to any person obtaining a copy | +|of this software and associated documentation files (the "Software"), to deal| +|in the Software without restriction, including without limitation the rights | +|to use, copy, modify, merge, publish, distribute, sublicense, and/or sell | +|copies of the Software, and to permit persons to whom the Software is | +|furnished to do so, subject to the following conditions: | +| | +|The above copyright notice and this permission notice shall be included in | +|all copies or substantial portions of the Software. | +| | +|THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | +|IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | +|FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE | +|AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | +|LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,| +|OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE| +|SOFTWARE. | ++-----------------------------------------------------------------------------+ +}} diff --git a/src/mv.spin b/src/mv.spin new file mode 100644 index 0000000..3b9a993 --- /dev/null +++ b/src/mv.spin @@ -0,0 +1,138 @@ +'****************************************************************************** +' Copyright (c) 2011, 2012, Dave Hein +' See end of file for terms of use. +'****************************************************************************** +CON + _clkmode = xinput + _xinfreq = $4e495053 ' SPIN + + ATTR_DIRECTORY = $10 + ATTR_READ_ONLY = $01 + +OBJ + c : "clibsd" + r : "resolve" + +DAT + buffer long 0[25] + +PUB main(argc, argv) | fname1, fname2, attrib1, attrib2, ptr + + c.enter + + if argc <> 3 + c.printf0(string("usage: mv fname1 fname2\n")) + c.exit(0) + + fname1 := long[argv][1] + fname2 := long[argv][2] + + r.resolve_path(fname1, @buffer) + if strcomp(@buffer, string("/")) + c.printf0(string("Can't move root directory\n")) + c.exit(1) + attrib1 := c.getmod(@buffer) + + r.resolve_path(fname2, @buffer) + if strcomp(@buffer, string("/")) + attrib2 := $30 + else + attrib2 := c.getmod(@buffer) + + if attrib1 == -1 + c.printf1(string("%s does not exist\n"), fname1) + c.exit(1) + + ' If fname1 is not a directory check if we should use copy + ifnot attrib1 & ATTR_DIRECTORY + ' Use copy if fname1 contains a "/" + ptr := fname1 + repeat while byte[ptr] and byte[ptr] <> "/" + ptr++ + if byte[ptr] + c.exit(Copy(fname1, fname2, attrib1, attrib2)) + + ' Use copy if fname2 contains a "/" + ptr := fname2 + repeat while byte[ptr] and byte[ptr] <> "/" + ptr++ + if byte[ptr] + c.exit(Copy(fname1, fname2, attrib1, attrib2)) + + ' Use copy if fname2 exists, and it is a directory + if attrib2 <> -1 and (attrib2 & ATTR_DIRECTORY) + c.exit(Copy(fname1, fname2, attrib1, attrib2)) + + ' Otherwise, attempt to rename it + result := c.rename(fname1, fname2) + if result == -2 + c.printf1(string("%s already exists\n"), fname2) + elseif result + c.printf1(string("%s does not exist\n"), fname1) + + c.exit(0) + +PUB Copy(fname1, fname2, attrib1, attrib2) | num, infile, outfile + infile := c.fopen(fname1, string("r")) + ifnot infile + c.printf1(string("Could not open input file %s\n"), fname1) + return 1 + if attrib1 & ATTR_DIRECTORY + c.printf1(string("%s is a directory\n"), fname1) + c.fclose(infile) + return 1 + c.strcpy(@buffer, fname2) + if attrib2 & ATTR_DIRECTORY + num := strsize(@buffer) + if num and byte[@buffer][num-1] <> "/" + c.strcat(@buffer, string("/")) + c.strcat(@buffer, GetFileName(fname1)) + outfile := c.fopen(@buffer, string("w")) + ifnot outfile + c.printf1(string("Could not open output file %s\n"), @buffer) + c.fclose(infile) + return 1 + c.chmod(@buffer, attrib1) + repeat while (num := c.fread(@buffer, 1, 100, infile)) > 0 + c.fwrite(@buffer, 1, num, outfile) + c.fclose(infile) + c.fclose(outfile) + if attrib1 & ATTR_READ_ONLY + c.chmod(fname1, 0) + c.remove(fname1) + +PRI GetFileName(path) | len + len := strsize(path) + if len =< 0 + return path + path += len + + repeat while len-- + if byte[--path] == "/" + return path + 1 + + return path + +{{ ++-----------------------------------------------------------------------------+ +| TERMS OF USE: MIT License | ++-----------------------------------------------------------------------------+ +|Permission is hereby granted, free of charge, to any person obtaining a copy | +|of this software and associated documentation files (the "Software"), to deal| +|in the Software without restriction, including without limitation the rights | +|to use, copy, modify, merge, publish, distribute, sublicense, and/or sell | +|copies of the Software, and to permit persons to whom the Software is | +|furnished to do so, subject to the following conditions: | +| | +|The above copyright notice and this permission notice shall be included in | +|all copies or substantial portions of the Software. | +| | +|THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | +|IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | +|FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE | +|AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | +|LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,| +|OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE| +|SOFTWARE. | ++-----------------------------------------------------------------------------+ +}} diff --git a/src/od.spin b/src/od.spin new file mode 100644 index 0000000..abb06d4 --- /dev/null +++ b/src/od.spin @@ -0,0 +1,111 @@ +'****************************************************************************** +' Copyright (c) 2011, 2012, Dave Hein +' See end of file for terms of use. +'****************************************************************************** +CON + _clkmode = xinput + _xinfreq = $4e495053 ' SPIN + +OBJ + c : "clibsd" + +PUB main(argc, argv) | fname, infile, mode + c.enter + mode := "o" + if argc < 2 OR argc > 3 + usage + if argc == 3 + fname := long[argv][2] + if byte[long[argv][1]] <> "-" + usage + mode := byte[long[argv][1]][1] + if mode <> "o" AND mode <> "x" AND mode <> "a" + usage + else + fname := long[argv][1] + infile := c.fopen(fname, STRING("r")) + IF (infile == 0) + c.printf1(STRING("File %s not found\n"), fname) + c.exit(@@0) + if mode == "x" + dumphex(infile) + elseif mode == "o" + dumpoctal(infile) + else + dumpascii(infile) + c.fclose(infile) + c.exit(0) + +PUB dumphex(infile) | num, i, j, buf[4] + j := 0 + repeat while ((num := c.fread(@buf, 1, 16, infile)) > 0) + c.printf1(string("%04x"), j) + j += 16 + i := 0 + repeat while (i < num) + c.printf1(string(" %02x"), byte[@buf][i]) + i++ + c.printf0(string("\n")) + +PUB dumpoctal(infile) | num, i, j, buf[4] + j := 0 + repeat while ((num := c.fread(@buf, 1, 16, infile)) > 0) + c.printf1(string("%06o"), j) + j += 16 + i := 0 + repeat while (i < num) + c.printf1(string(" %03o"), byte[@buf][i]) + i++ + c.printf0(string("\n")) + +PUB dumpascii(infile) | num, i, j, buf[4], char + j := 0 + repeat while ((num := c.fread(@buf, 1, 16, infile)) > 0) + c.printf1(string("%04x"), j) + j += 16 + i := 0 + repeat while (i < num) + char := byte[@buf][i] + if char => 32 AND char =< 126 + c.printf1(string(" %c"), char) + elseif char == 9 + c.printf0(string(" \\t")) + elseif char == 10 + c.printf0(string(" \\n")) + elseif char == 13 + c.printf0(string(" \\r")) + else + c.printf1(string(" %02x"), char) + i++ + c.printf0(string("\n")) + +PUB usage + c.printf0(string("usage: od [flags] filename\n")) + c.printf0(string(" -o Dump in octal (default)\n")) + c.printf0(string(" -x Dump in hex\n")) + c.printf0(string(" -a Dump in ASCII\n")) + c.exit(0) + +{{ ++-----------------------------------------------------------------------------+ +| TERMS OF USE: MIT License | ++-----------------------------------------------------------------------------+ +|Permission is hereby granted, free of charge, to any person obtaining a copy | +|of this software and associated documentation files (the "Software"), to deal| +|in the Software without restriction, including without limitation the rights | +|to use, copy, modify, merge, publish, distribute, sublicense, and/or sell | +|copies of the Software, and to permit persons to whom the Software is | +|furnished to do so, subject to the following conditions: | +| | +|The above copyright notice and this permission notice shall be included in | +|all copies or substantial portions of the Software. | +| | +|THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | +|IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | +|FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE | +|AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | +|LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,| +|OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE| +|SOFTWARE. | ++-----------------------------------------------------------------------------+ +}} diff --git a/src/opsfile.txt b/src/opsfile.txt new file mode 100644 index 0000000..935de60 --- /dev/null +++ b/src/opsfile.txt @@ -0,0 +1,219 @@ + 00 ldfrmr + 01 ldfrm + 02 ldfrmar + 03 ldfrma + E 04 jmp + J 05 call + G 06 callobj + G 07 callobjx + E 08 tjz + E 09 djnz + E 0a jz + E 0b jnz + 0c jmps + H 0d cmpcase + H 0e cmpcaserange + 0f lookabort + 10 lookupcmp + 11 lookdncmp + 12 loopuprcmp + 13 lookdnrcmp + 14 quit + 15 mark + 16 strsize + 17 strcomp + 18 bytefill + 19 wordfill + 1a longfill + 1b waitpeq + 1c bytemove + 1d wordmove + 1e longmove + 1f waitpne + 20 clkset + 21 cogstop + 22 lockret + 23 waitcnt + 24 ldlsx + 25 stlsx +1 26 exlsx + 27 waitvid + 28 coginitret + 29 locknewret + 2a locksetret + 2b lockclrret + 2c coginit + 2d locknew + 2e lockset + 2f lockclr + 30 abort + 31 abortval + 32 ret + 33 retval + 34 ldlim1 + 35 ldli0 + 36 ldli1 + I 37 ldlip + J 38 ldbi + K 39 ldwi + L 3a ldmi + M 3b ldli + 3c lmm + N 3d ldregbit + N 3d stregbit + N 3e ldregbits + N 3e stregbits + O 3f ldreg + F 3f streg + P 40 ldlvc + P 41 stlvc +1P 42 exlvc + P 43 lalvc + Q 60 ldllc + Q 61 stllc +1Q 62 exllc + Q 63 lallc + 80 ldba + 81 stba +1 82 exba + 83 laba + A 84 ldbo + A 85 stbo +1A 86 exbo + A 87 labo + B 88 ldbv + B 89 stbv +1B 8a exbv + B 8b labv + C 8c ldbl + C 8d stbl +1C 8e exbl + C 8f labl + 90 ldbax + 91 stbax +1 92 exbax + 93 labax + A 94 ldbox + A 95 stbox +1A 96 exbox + A 97 labox + B 98 ldbvx + B 99 stbvx +1B 9a exbvx + B 9b labvx + C 9c ldblx + C 9d stblx +1C 9e exblx + C 9f lablx + a0 ldwa + a1 stwa +1 a2 exwa + a3 lawa + A a4 ldwo + A a5 stwo +1A a6 exwo + A a7 lawo + B a8 ldwv + B a9 stwv +1B aa exwv + B ab lawv + C ac ldwl + C ad stwl +1C ae exwl + C af lawl + b0 ldwax + b1 stwax +1 b2 exwax + b3 lawax + A b4 ldwox + A b5 stwox +1A b6 exwox + A b7 lawox + B b8 ldwvx + B b9 stwvx +1B ba exwvx + B bb lawvx + C bc ldwlx + C bd stwlx +1C be exwlx + C bf lawlx + c0 ldla + c1 stla +1 c2 exla + c3 lala + A c4 ldlo + A c5 stlo +1A c6 exlo + A c7 lalo + B c8 ldlv + B c9 stlv +1B ca exlv + B cb lalv + C cc ldll + C cd stll +1C ce exll + C cf lall + d0 ldlax + d1 stlax +1 d2 exlax + d3 lalax + A d4 ldlox + A d5 stlox +1A d6 exlox + A d7 lalox + B d8 ldlvx + B d9 stlvx +1B da exlvx + B db lalvx + C dc ldllx + C dd stllx +1C de exllx + C df lallx +2 e0 ror +2 e1 rol +2 e2 shr +2 e3 shl +2 e4 min +2 e5 max +2 e6 neg +2 e7 com +2 e8 and +2 e9 abs +2 ea or +2 eb xor +2 ec add +2 ed sub +2 ee sar +2 ef rev +2 f0 andl +2 f1 encode +2 f2 orl +2 f3 decode +2 f4 mul +2 f5 mulh +2 f6 div +2 f7 mod +2 f8 sqrt +2 f9 cmplt +2 fa cmpgt +2 fb cmpne +2 fc cmpeq +2 fd cmple +2 fe cmpge +2 ff notl +3 00 null +3 02 repeat +3 06 repeats +3 08 randf +3 0c randr +3 10 sexb +3 14 sexw +3 18 postclr +3 1c postset +3 26 preinc +3 2e postinc +3 36 predec +3 3e postdec + D 04 long + D 02 word + D 01 byte diff --git a/src/pasm_i2c_driver.spin b/src/pasm_i2c_driver.spin new file mode 100644 index 0000000..641d484 --- /dev/null +++ b/src/pasm_i2c_driver.spin @@ -0,0 +1,520 @@ +'' PASM I2C Driver Version 1.0 +'' Copyright (c) 2010 Dave Hein +'' June 6, 2010 +'' See end of file for terms of use + +'' This is a PASM version of Mike Green's Basic I2C Driver. The +'' low level I2C routines have been converted to PASM to increase the +'' I/O speed. These routines use the same calling interface as +'' the Basic I2C Driver Version 1.1 in the OBEX, and should be fully +'' compatible with any existing code that uses the Basic I2C Driver. + +'' Just like the Basic I2C Driver, the PASM I2C Driver assumes +'' that the SDA pin is one higher than the SCL pin. It assumes that +'' neither the SDA nor the SCL pins have pullups, so drives both. + +'' This object uses the Initialize method to start up a cog rather than using +'' the start method. This is done to remain consistent with the Basic I2C +'' Driver routines. Initialize must be called at the beginning of the program. +'' This loads the PASM code in a cog, and clocks the I2C bus to initialize +'' the devices on the bus. Subsequent calls may be made to Initialize, and +'' it will not cause the cog to be stopped or reloaded. + +'' The bus I/O speed is controlled by the constant DELAY_CYCLES. This constant +'' is used in the delay routine. The total delay consists of calling the delay +'' routine, performing a waitcnt of CNT + DELAY_CYCLES, and then returning +'' from the delay routine. Therefore, the total delay will be about +'' 12 + DELAY_CYCLES. + +'' The delay time represents the clock high time, and half the clock low time. +'' It is also used to determine the setup and hold times for the data bit for +'' read, write, start and stop operations. DELAY_CYCLES is defined with a +'' value of 52, which gives a total delay of 64 cycles. At 80 MHz, this is +'' 0.8 usecs, which is about one-third of a 400 KHz cycle time. This value +'' should be modified to provide the optimal speed for a particular application. + +'' Please see Mike Green's Basic I2C Driver object for more information on +'' the I2C routines, and on how EEPROMs are addressed + +CON + ACK = 0 ' I2C Acknowledge + NAK = 1 ' I2C No Acknowledge + Xmit = 0 ' I2C Direction Transmit + Recv = 1 ' I2C Direction Receive + + CMD_START = 1 ' Issue a start bit + CMD_STOP = 2 ' Issue a stop bit + CMD_READ = 3 ' Transmit a byte to the I2C bus + CMD_WRITE = 4 ' Read a byte from the I2C bus + CMD_INIT = 5 ' Initialize the I2C bus + CMD_READPAGE = 6 ' Read one or more bytes in the page mode + CMD_WRITEPAGE = 7 ' Write one or more bytes in the page mode + + DELAY_CYCLES = 52 ' I2C Delay time. Must be between 12 and 511 + +DAT + cognum long 0 + cmdbuf long 0, 0 + +PUB Initialize(SCL) +'' Start cog if not started, and initialize the devices on the I2C bus + 'ser.dbprintf1(string("i2c initialize %d\n"), SCL) + cmdbuf[1] := @SCL + cmdbuf := CMD_INIT + ifnot cognum + cognum := cognew(@cmdloop, @cmdbuf) + 1 + repeat while cmdbuf + result := cognum + +PUB Start(SCL) +'' Issue an I2C start command + cmdbuf[1] := @SCL + cmdbuf := CMD_START + repeat while cmdbuf + +PUB Stop(SCL) +'' Issue an I2C stop command + cmdbuf[1] := @SCL + cmdbuf := CMD_STOP + repeat while cmdbuf + +PUB Read(SCL, ackbit) +'' Read in i2c data, Data byte is output MSB first, SDA data line is +'' valid only while the SCL line is HIGH. SCL and SDA left in LOW state. + cmdbuf[1] := @SCL + cmdbuf := CMD_READ + repeat while cmdbuf + result := cmdbuf[1] + +PUB Write(SCL, data) +'' Write i2c data. Data byte is output MSB first, SDA data line is valid +'' only while the SCL line is HIGH. Data is always 8 bits (+ ACK/NAK). +'' SDA is assumed LOW and SCL and SDA are both left in the LOW state. + cmdbuf[1] := @SCL + cmdbuf := CMD_WRITE + repeat while cmdbuf + result := cmdbuf[1] + +PUB ReadPage(SCL, devSel, addrReg, dataPtr, count) : ackbit +'' Read in a block of i2c data. Device select code is devSel. Device starting +'' address is addrReg. Data address is at dataPtr. Number of bytes is count. +'' The device select code is modified using the upper 3 bits of the 19 bit addrReg. +'' Return zero if no errors or the acknowledge bits if an error occurred. + cmdbuf[1] := @SCL + cmdbuf := CMD_READPAGE + repeat while cmdbuf + ackbit := cmdbuf[1] + +PUB WritePage(SCL, devSel, addrReg, dataPtr, count) : ackbit +'' Write out a block of i2c data. Device select code is devSel. Device starting +'' address is addrReg. Data address is at dataPtr. Number of bytes is count. +'' The device select code is modified using the upper 3 bits of the 19 bit addrReg. +'' Most devices have a page size of at least 32 bytes, some as large as 256 bytes. +'' Return zero if no errors or the acknowledge bits if an error occurred. If +'' more than 31 bytes are transmitted, the sign bit is "sticky" and is the +'' logical "or" of the acknowledge bits of any bytes past the 31st. + cmdbuf[1] := @SCL + cmdbuf := CMD_WRITEPAGE + repeat while cmdbuf + ackbit := cmdbuf[1] + +PUB ReadByte(SCL, devSel, addrReg) : data +'' Read in a single byte of i2c data. Device select code is devSel. Device +'' starting address is addrReg. The device select code is modified using the +'' upper 3 bits of the 19 bit addrReg. This returns true if an error occurred. + if ReadPage(SCL, devSel, addrReg, @data, 1) + return -1 + +PUB ReadWord(SCL, devSel, addrReg) : data +'' Read in a single word of i2c data. Device select code is devSel. Device +'' starting address is addrReg. The device select code is modified using the +'' upper 3 bits of the 19 bit addrReg. This returns true if an error occurred. + if ReadPage(SCL, devSel, addrReg, @data, 2) + return -1 + +PUB ReadLong(SCL, devSel, addrReg) : data +'' Read in a single long of i2c data. Device select code is devSel. Device +'' starting address is addrReg. The device select code is modified using the +'' upper 3 bits of the 19 bit addrReg. This returns true if an error occurred. +'' Note that you can't distinguish between a return value of -1 and true error. + if ReadPage(SCL, devSel, addrReg, @data, 4) + return -1 + +PUB WriteByte(SCL, devSel, addrReg, data) +'' Write out a single byte of i2c data. Device select code is devSel. Device +'' starting address is addrReg. The device select code is modified using the +'' upper 3 bits of the 19 bit addrReg. This returns true if an error occurred. + if WritePage(SCL, devSel, addrReg, @data, 1) + return true + return false + +PUB WriteWord(SCL, devSel, addrReg, data) +'' Write out a single word of i2c data. Device select code is devSel. Device +'' starting address is addrReg. The device select code is modified using the +'' upper 3 bits of the 19 bit addrReg. This returns true if an error occurred. +'' Note that the word value may not span an EEPROM page boundary. + if WritePage(SCL, devSel, addrReg, @data, 2) + return true + return false + +PUB WriteLong(SCL, devSel, addrReg, data) +'' Write out a single long of i2c data. Device select code is devSel. Device +'' starting address is addrReg. The device select code is modified using the +'' upper 3 bits of the 19 bit addrReg. This returns true if an error occurred. +'' Note that the long word value may not span an EEPROM page boundary. + if WritePage(SCL, devSel, addrReg, @data, 4) + return true + return false + +PUB WriteWait(SCL, devSel, addrReg) : ackbit +'' Wait for a previous write to complete. Device select code is devSel. Device +'' starting address is addrReg. The device will not respond if it is busy. +'' The device select code is modified using the upper 3 bits of the 18 bit addrReg. +'' This returns zero if no error occurred or one if the device didn't respond. + devSel |= addrReg >> 15 & %1110 + Start(SCL) + ackbit := Write(SCL, devSel | Xmit) + Stop(SCL) + return ackbit + +DAT + +'*********************************** +'* Assembly language i2c driver * +'*********************************** + + org +' +' +' Entry +' Wait for a non-zero command and process +cmdloop rdlong t1, par wz + if_z jmp #cmdloop + mov parm1, par + add parm1, #4 + rdlong parm1, parm1 ' Get the address of the parameter list + + rdlong t2, parm1 ' SCL is always the first parameter + add parm1, #4 ' Point to the next parameter + mov scl_bit,#1 + shl scl_bit,t2 + mov sda_bit, scl_bit + shl sda_bit, #1 + + cmp t1, #CMD_READPAGE wz + if_z jmp #ReadPage1 + cmp t1, #CMD_WRITEPAGE wz + if_z jmp #WritePage1 + cmp t1, #CMD_READ wz + if_z jmp #read_byte + cmp t1, #CMD_WRITE wz + if_z jmp #write_byte + cmp t1, #CMD_START wz + if_z jmp #start1 + cmp t1, #CMD_STOP wz + if_z jmp #stop1 + cmp t1, #CMD_INIT wz + if_z jmp #initialize1 + neg parm1, #1 + +ReturnParm mov t1, par + add t1, #4 + wrlong parm1, t1 +signal_ready mov t1, #0 + wrbyte t1, par + jmp #cmdloop + +ReadPage1 call #ReadPageFunc + jmp #ReturnParm +WritePage1 call #WritePageFunc + jmp #ReturnParm +read_byte rdlong parm1, parm1 + call #readbytefunc + jmp #ReturnParm +write_byte rdlong parm1, parm1 + call #writebytefunc + jmp #ReturnParm +start1 call #StartFunc + jmp #ReturnParm +stop1 call #StopFunc + jmp #ReturnParm +initialize1 call #InitializeFunc + jmp #ReturnParm + +'' This routine reads a byte and sends the ACK bit. It assumes the clock +'' and data lines have been low for at least the minimum low clock time. +'' It exits with the clock and data low for the minimum low clock time. +readbytefunc mov ackbit1, parm1 ' Get the ACK bit + mov data1, #0 ' Initialize data byte to zero + andn dira, sda_bit ' Set SDA as input + call #delay + mov count1, #8 ' Set loop count for 8 + +:loop call #delay + or outa, scl_bit ' Set SCL HIGH + call #delay + shl data1, #1 ' data byte left one bit + test sda_bit, ina wz + if_nz or data1, #1 ' Set LSB if input bit is HIGH + andn outa, scl_bit ' Set SCL LOW + call #delay + djnz count1, #:loop + + cmp ackbit1, #0 wz + if_z andn outa, sda_bit ' Set SDA LOW if ACK + if_nz or outa, sda_bit ' Set SDA HIGH if NAK + or dira, sda_bit ' Set SDA as output + call #delay + or outa, scl_bit ' Set SCL HIGH + call #delay + andn outa, scl_bit ' Set SCL LOW + call #delay + mov parm1, data1 ' Return the data byte +readbytefunc_ret ret + +'' This routine writes a byte and reads the ACK bit. It assumes that the clock +'' and data are set as outputs, and the clock has been low for at least half the +'' minimum low clock time. It exits with the clock and data set as outputs, and +'' with the clock low for half the minimum low clock time. +writebytefunc mov data1, parm1 ' Get the data byte + mov count1, #8 ' Set loop count for 8 bits + +:loop shl data1, #1 ' Shift left one bit + test data1, #$100 wz ' Check MSB + if_z andn outa, sda_bit ' Set SDA LOW if zero + if_nz or outa, sda_bit ' Set SDA HIGH if not zero + call #delay + or outa, scl_bit ' Set SCL HIGH + call #delay + andn outa, scl_bit ' Set SCL LOW + call #delay + djnz count1, #:loop + + andn dira, sda_bit ' Set SDA as input + call #delay + or outa, scl_bit ' Set SDA HIGH + call #delay + test sda_bit, ina wz ' Check SDA input + if_z mov ackbit1, #0 ' Set to zero if LOW + if_nz mov ackbit1, #1 ' Set to one if HIGH + andn outa, scl_bit ' Set SCL LOW + call #delay + or dira, sda_bit ' Set SDA as output + mov parm1, ackbit1 ' Return the ack bit +writebytefunc_ret ret + +'' This routine transmits the stop sequence, which consists of the data line +'' going from low to high while the clock is high. It assumes that data and +'' clock are set as outputs, and the clock has been low for half the minimum +'' low clock time. It exits with the clock and data floating high for the +'' minimum high clock time. +stopfunc andn outa, sda_bit ' Set SDA LOW + call #delay + or outa, scl_bit ' Set SCL HIGH + call #delay + or outa, sda_bit ' Set SDA HIGH + call #delay + andn dira, scl_bit ' Float SCL HIGH + andn dira, sda_bit ' Float SDA HIGH +stopfunc_ret ret + +'' This routine transmits the start sequence, which consists of the data line +'' going from high to low while the clock is high. It assumes that the clock +'' and data were floating high for the minimum high clock time, and it exits +'' with the clock and data low for half the minimum low clock time. +startfunc or outa, sda_bit ' Set SDA HIGH + or dira, sda_bit ' Set SDA as output + call #delay + or outa, scl_bit ' Set SCL HIGH + or dira, scl_bit ' Set SCL as output + call #delay + andn outa, sda_bit ' Set SDA LOW + call #delay + andn outa, scl_bit ' Set SCL LOW + call #delay +startfunc_ret ret + +'' This routine puts the I2C bus in a known state. It issues up to nine clock +'' pulses waiting for the input to be in a high state. It exits with the clock +'' driven high and the data floating in the high state for the minimum high +'' clock time. +initializefunc andn dira, sda_bit ' Set SDA as input + or outa, scl_bit ' Set SCL HIGH + or dira, scl_bit ' Set SCL as output + call #delay + mov count1, #9 ' Set for up to 9 loops +:loop andn outa, scl_bit ' Set SCL LOW + call #delay + call #delay + or outa, scl_bit ' Set SCL HIGH + call #delay + test sda_bit, ina wz + if_nz jmp #initializefunc_ret ' Quit if input is HIGH + djnz count1, #:loop +initializefunc_ret ret ' Quit after nine clocks + +'' This routine delays for the minimum high clock time, or half the minimum +'' low clock time. This delay routine is also used for the setup and hold +'' times for the start and stop signals, as well as the output data changes. +delay mov delaycnt, cnt + add delaycnt, #DELAY_CYCLES + waitcnt delaycnt, #0 +delay_ret ret + +'PUB ReadPage(SCL, devSel, addrReg, dataPtr, count) : ackbit +readpagefunc rdlong devsel1, parm1 + add parm1, #4 + rdlong addrreg1, parm1 + add parm1, #4 + rdlong dataptr1, parm1 + add parm1, #4 + rdlong count2, parm1 + +'' Read in a block of i2c data. Device select code is devSel. Device starting +'' address is addrReg. Data address is at dataPtr. Number of bytes is count. +'' The device select code is modified using the upper 3 bits of the 19 bit addrReg. +'' Return zero if no errors or the acknowledge bits if an error occurred. +' devSel |= addrReg >> 15 & %1110 + mov t1, addrreg1 + shr t1, #15 + and t1, #%1110 + or devsel1, t1 +' Start(SCL) ' Select the device & send address + call #startfunc +' ackbit := Write(SCL, devSel | Xmit) + mov parm1, devsel1 + or parm1, #Xmit + call #writebytefunc + mov ackbit2, parm1 +' ackbit := (ackbit << 1) | Write(SCL, addrReg >> 8 & $FF) + mov parm1, addrreg1 + shr parm1, #8 + and parm1, #$ff + call #writebytefunc + shl ackbit2, #1 + or ackbit2, parm1 +' ackbit := (ackbit << 1) | Write(SCL, addrReg & $FF) + mov parm1, addrreg1 + and parm1, #$ff + call #writebytefunc + shl ackbit2, #1 + or ackbit2, parm1 +' Start(SCL) ' Reselect the device for reading + call #startfunc +' ackbit := (ackbit << 1) | Write(SCL, devSel | Recv) + mov parm1, devsel1 + or parm1, #Recv + call #writebytefunc + shl ackbit2, #1 + or ackbit2, parm1 +' repeat count - 1 +' byte[dataPtr++] := Read(SCL, ACK) +' byte[dataPtr++] := Read(SCL, NAK) +:loop cmp count2, #1 wz + if_z mov parm1, #NAK + if_nz mov parm1, #ACK + call #readbytefunc + wrbyte parm1, dataptr1 + add dataptr1, #1 + djnz count2, #:loop + +' Stop(SCL) + call #stopfunc +' return ackbit + mov parm1, ackbit2 +readpagefunc_ret ret + +'PUB WritePage(SCL, devSel, addrReg, dataPtr, count) : ackbit +writepagefunc rdlong devsel1, parm1 + add parm1, #4 + rdlong addrreg1, parm1 + add parm1, #4 + rdlong dataptr1, parm1 + add parm1, #4 + rdlong count2, parm1 + +'' Write out a block of i2c data. Device select code is devSel. Device starting +'' address is addrReg. Data address is at dataPtr. Number of bytes is count. +'' The device select code is modified using the upper 3 bits of the 19 bit addrReg. +'' Most devices have a page size of at least 32 bytes, some as large as 256 bytes. +'' Return zero if no errors or the acknowledge bits if an error occurred. If +'' more than 31 bytes are transmitted, the sign bit is "sticky" and is the +'' logical "or" of the acknowledge bits of any bytes past the 31st. +' devSel |= addrReg >> 15 & %1110 + mov t1, addrreg1 + shr t1, #15 + and t1, #%1110 + or devsel1, t1 +' Start(SCL) ' Select the device & send address + call #startfunc +' ackbit := Write(SCL, devSel | Xmit) + mov parm1, devsel1 + or parm1, #Xmit + call #writebytefunc + mov ackbit2, parm1 +' ackbit := (ackbit << 1) | Write(SCL, addrReg >> 8 & $FF) + mov parm1, addrreg1 + shr parm1, #8 + and parm1, #$ff + call #writebytefunc + shl ackbit2, #1 + or ackbit2, parm1 +' ackbit := (ackbit << 1) | Write(SCL, addrReg & $FF) + mov parm1, addrreg1 + and parm1, #$ff + call #writebytefunc + shl ackbit2, #1 + or ackbit2, parm1 +' repeat count ' Now send the data +' ackbit := ackbit << 1 | ackbit & $80000000 ' "Sticky" sign bit +' ackbit |= Write(SCL, byte[dataPtr++]) +:loop shl ackbit2, #1 wc + if_c or ackbit2, signbit + rdbyte parm1, dataptr1 + add dataptr1, #1 + call #writebytefunc + or ackbit2, parm1 + djnz count2, #:loop +' Stop(SCL) + call #stopfunc +' return ackbit + mov parm1, ackbit2 +writepagefunc_ret ret + +signbit long $80000000 +scl_bit res 1 +sda_bit res 1 +count1 res 1 +t1 res 1 +t2 res 1 +data1 res 1 +ackbit1 res 1 +delaycnt res 1 +parm1 res 1 +devsel1 res 1 +addrreg1 res 1 +dataptr1 res 1 +count2 res 1 +ackbit2 res 1 + +{{ + TERMS OF USE: MIT License + + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), to deal + in the Software without restriction, including without limitation the rights + to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + copies of the Software, and to permit persons to whom the Software is + furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in + all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,ARISING FROM, + OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + THE SOFTWARE. +}} diff --git a/src/printenv.spin b/src/printenv.spin new file mode 100644 index 0000000..2febd15 --- /dev/null +++ b/src/printenv.spin @@ -0,0 +1,85 @@ +'****************************************************************************** +' Copyright (c) 2013 Dave Hein +' See end of file for terms of use. +'****************************************************************************** +CON + _clkmode = xinput + _xinfreq = $4e495053 ' SPIN + + DOUBLE_QUOTE = $22 + GLOBAL_FLAG = "G" + +OBJ + c : "clibsd" + sys : "sysdefs" + +PUB main(argc, argv) | ptr, argi + c.enter + + if argc == 1 + PrintVars + c.exit(0) + + argi := 1 + repeat while argi < argc + if (ptr := FindVar(long[argv][argi++])) + if byte[ptr] == GLOBAL_FLAG + ptr += strsize(ptr) + 1 + c.printf1(string("%s\n"), ptr) + + c.exit(0) + +PUB FindVar(str) | ptr + ptr := sys#environ_vars + repeat while byte[ptr] + if strcomp(str, ptr+1) + return ptr + ptr += strsize(ptr) + 1 + ptr += strsize(ptr) + 1 + +PUB PrintVars | ptr + ptr := sys#environ_vars + repeat while byte[ptr] + ptr := PrintVar(ptr) + +PUB PrintVar(ptr) | vartype, name, value + vartype := byte[ptr++] + name := ptr + ptr += strsize(ptr) + 1 + value := ptr + ptr += strsize(ptr) + 1 + if vartype == GLOBAL_FLAG + if ContainsBlank(value) + c.printf2(string("%s=", DOUBLE_QUOTE, "%s", DOUBLE_QUOTE, "\n"), name, value) + else + c.printf2(string("%s=%s\n"), name, value) + return ptr + +PUB ContainsBlank(ptr) + repeat while byte[ptr] + if byte[ptr++] == " " + return 1 + +{{ ++-----------------------------------------------------------------------------+ +| TERMS OF USE: MIT License | ++-----------------------------------------------------------------------------+ +|Permission is hereby granted, free of charge, to any person obtaining a copy | +|of this software and associated documentation files (the "Software"), to deal| +|in the Software without restriction, including without limitation the rights | +|to use, copy, modify, merge, publish, distribute, sublicense, and/or sell | +|copies of the Software, and to permit persons to whom the Software is | +|furnished to do so, subject to the following conditions: | +| | +|The above copyright notice and this permission notice shall be included in | +|all copies or substantial portions of the Software. | +| | +|THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | +|IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | +|FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE | +|AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | +|LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,| +|OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE| +|SOFTWARE. | ++-----------------------------------------------------------------------------+ +}} diff --git a/src/ps.spin b/src/ps.spin new file mode 100644 index 0000000..b48899b --- /dev/null +++ b/src/ps.spin @@ -0,0 +1,67 @@ +'****************************************************************************** +' Copyright (c) 2011, 2012, Dave Hein +' See end of file for terms of use. +'****************************************************************************** +CON + _clkmode = xinput + _xinfreq = $4e495053 ' SPIN + +OBJ + c : "clibsd" + sys : "sysdefs" + +DAT + idle long 0[8] + +PUB main(argc, argv) | spicog, i + c.enter + + spicog := long[sys#spi_engine_cog] - 1 + + repeat + i := cognew(@dummy, 0) + if i => 0 and i =< 7 + idle[i] := 1 + else + quit + + repeat i from 0 to 7 + if i == cogid + c.printf2(string("%d %s\n"), cogid, long[argv]) + elseif i == spicog + c.printf1(string("%d SPI Driver\n"), spicog) + elseif idle[i] + c.printf1(string("%d Idle\n"), i) + 'cogstop(i) + else + c.printf1(string("%d Serial Driver\n"), i) + + c.exit(0) + +DAT + org 0 +dummy jmp #dummy + +{{ ++-----------------------------------------------------------------------------+ +| TERMS OF USE: MIT License | ++-----------------------------------------------------------------------------+ +|Permission is hereby granted, free of charge, to any person obtaining a copy | +|of this software and associated documentation files (the "Software"), to deal| +|in the Software without restriction, including without limitation the rights | +|to use, copy, modify, merge, publish, distribute, sublicense, and/or sell | +|copies of the Software, and to permit persons to whom the Software is | +|furnished to do so, subject to the following conditions: | +| | +|The above copyright notice and this permission notice shall be included in | +|all copies or substantial portions of the Software. | +| | +|THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | +|IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | +|FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE | +|AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | +|LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,| +|OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE| +|SOFTWARE. | ++-----------------------------------------------------------------------------+ +}} diff --git a/src/rb.spin b/src/rb.spin new file mode 100644 index 0000000..94321b9 --- /dev/null +++ b/src/rb.spin @@ -0,0 +1,196 @@ +'****************************************************************************** +' Copyright (c) 2011, 2012, Dave Hein +' See end of file for terms of use. +'****************************************************************************** +CON + _clkmode = xinput + _xinfreq = $4e495053 ' SPIN + + 'XMODEM chars from ymodem.txt + SOH=$01 + STX=$02 + EOT=$04 + ACK=$06 + NAK=$15 + CAN=$18 + Cee=$43 'liberties here + Zee=$1A 'or SUB (DOS EOF?) + + MSG_TIMEOUT = -2 + MSG_EOT = -3 + BYTE_TIMEOUT = -4 + MSG_BADNUM = -5 + MSG_CRCERR = -6 + +OBJ 'Objects Section + c : "clibsd" + ser : "cserial" + +dat + pdata long 0 + + fcstab word + word $0000,$1021,$2042,$3063,$4084,$50a5,$60c6,$70e7 + word $8108,$9129,$a14a,$b16b,$c18c,$d1ad,$e1ce,$f1ef + word $1231,$0210,$3273,$2252,$52b5,$4294,$72f7,$62d6 + word $9339,$8318,$b37b,$a35a,$d3bd,$c39c,$f3ff,$e3de + word $2462,$3443,$0420,$1401,$64e6,$74c7,$44a4,$5485 + word $a56a,$b54b,$8528,$9509,$e5ee,$f5cf,$c5ac,$d58d + word $3653,$2672,$1611,$0630,$76d7,$66f6,$5695,$46b4 + word $b75b,$a77a,$9719,$8738,$f7df,$e7fe,$d79d,$c7bc + word $48c4,$58e5,$6886,$78a7,$0840,$1861,$2802,$3823 + word $c9cc,$d9ed,$e98e,$f9af,$8948,$9969,$a90a,$b92b + word $5af5,$4ad4,$7ab7,$6a96,$1a71,$0a50,$3a33,$2a12 + word $dbfd,$cbdc,$fbbf,$eb9e,$9b79,$8b58,$bb3b,$ab1a + word $6ca6,$7c87,$4ce4,$5cc5,$2c22,$3c03,$0c60,$1c41 + word $edae,$fd8f,$cdec,$ddcd,$ad2a,$bd0b,$8d68,$9d49 + word $7e97,$6eb6,$5ed5,$4ef4,$3e13,$2e32,$1e51,$0e70 + word $ff9f,$efbe,$dfdd,$cffc,$bf1b,$af3a,$9f59,$8f78 + word $9188,$81a9,$b1ca,$a1eb,$d10c,$c12d,$f14e,$e16f + word $1080,$00a1,$30c2,$20e3,$5004,$4025,$7046,$6067 + word $83b9,$9398,$a3fb,$b3da,$c33d,$d31c,$e37f,$f35e + word $02b1,$1290,$22f3,$32d2,$4235,$5214,$6277,$7256 + word $b5ea,$a5cb,$95a8,$8589,$f56e,$e54f,$d52c,$c50d + word $34e2,$24c3,$14a0,$0481,$7466,$6447,$5424,$4405 + word $a7db,$b7fa,$8799,$97b8,$e75f,$f77e,$c71d,$d73c + word $26d3,$36f2,$0691,$16b0,$6657,$7676,$4615,$5634 + word $d94c,$c96d,$f90e,$e92f,$99c8,$89e9,$b98a,$a9ab + word $5844,$4865,$7806,$6827,$18c0,$08e1,$3882,$28a3 + word $cb7d,$db5c,$eb3f,$fb1e,$8bf9,$9bd8,$abbb,$bb9a + word $4a75,$5a54,$6a37,$7a16,$0af1,$1ad0,$2ab3,$3a92 + word $fd2e,$ed0f,$dd6c,$cd4d,$bdaa,$ad8b,$9de8,$8dc9 + word $7c26,$6c07,$5c64,$4c45,$3ca2,$2c83,$1ce0,$0cc1 + word $ef1f,$ff3e,$cf5d,$df7c,$af9b,$bfba,$8fd9,$9ff8 + word $6e17,$7e36,$4e55,$5e74,$2e93,$3eb2,$0ed1,$1ef0 + +PUB ReceiveFile(argc, argv) | packetnum, num, ptr, filesize, fname[10], outfile + c.enter + pdata := c.malloc(1028) + ifnot pdata + ser.dbprintf0(string("Could not malloc 1028 bytes for the data buffer\n")) + c.exit(1) + ser.dbprintf0(string("ymodem receive starts in 15 seconds\n")) + waitcnt(clkfreq*15+cnt) + outfile := 0 + ptr := pdata + packetnum := 0 + repeat + if packetnum < 2 + ser.tx(CEE) + num := ReceivePacket(ptr) + 'c.fprintf2(logfile, string("ReceivePacket returned %d %d\n"), num, byte[pdata][0]) + if num == MSG_TIMEOUT + ser.tx(EOT) + quit + elseif num == MSG_EOT + packetnum := 0 + ser.tx(ACK) + next + elseif num =< 0 + ser.tx(NAK) + next + if packetnum == 0 + if byte[pdata][2] == 0 + ser.tx(ACK) + quit + filesize := DecodeFirstPacket(ptr+2, num, @fname) + 'c.fprintf2(logfile, string("fname = %s, size = %d\n"), @fname, filesize) + outfile := c.fopen(@fname, string("w")) + 'Open file + else + if num > filesize + num := filesize + c.fwrite(@byte[pdata][2], 1, num, outfile) + filesize -= num + ser.tx(ACK) + packetnum++ + if outfile + c.fclose(outfile) + c.free(pdata) + c.exit(1) + +PUB DecodeFirstPacket(ptr, num, fname) | i, val, filesize + ifnot byte[ptr] + byte[fname] := 0 + return -1 + repeat i from 0 to 40 + byte[fname][i] := byte[ptr][i] + ifnot byte[ptr][i] + i++ + quit + filesize := 0 + repeat while i < num + val := byte[ptr][i++] + if val < "0" or val > "9" + quit + filesize := (filesize * 10) + val - "0" + + return filesize + +PUB ReceivePacket(ptr) | num, val, timeout, ptr0 + ptr0 := ptr + timeout := (clkfreq * 10) + cnt + ' Get start of packet + repeat + repeat while (val := ser.rxcheck) == -1 + if cnt - timeout => 0 + return MSG_TIMEOUT + 'c.fprintf1(logfile, string("rx %02x\n"), val) + if val == SOH + num := 128 + quit + elseif val == STX + num := 1024 + quit + elseif val == EOT + return MSG_EOT + + ' Read the packet + repeat num + 4 + timeout := clkfreq + cnt + repeat while (val := ser.rxcheck) == -1 + if cnt - timeout => 0 + return BYTE_TIMEOUT + byte[ptr++] := val + + ' Check for valid packet number pair + if (byte[ptr0] + byte[ptr0][1]) <> 255 + return MSG_BADNUM + + ' Check the CRC + ifnot CheckCRC(ptr0+2, num) + return MSG_CRCERR + + return num + +PUB CheckCRC(ptr, num) | crc + crc := ComputeCRC(ptr, num) + return (byte[ptr][num] == (crc >> 8)) and (byte[ptr][num+1] == (crc & $ff)) + +PUB ComputeCRC(ptr, num) + repeat num + result := ((result << 8) & $ffff) ^ fcstab[(result >> 8) ^ byte[ptr++]] + +{{ ++-----------------------------------------------------------------------------+ +| TERMS OF USE: MIT License | ++-----------------------------------------------------------------------------+ +|Permission is hereby granted, free of charge, to any person obtaining a copy | +|of this software and associated documentation files (the "Software"), to deal| +|in the Software without restriction, including without limitation the rights | +|to use, copy, modify, merge, publish, distribute, sublicense, and/or sell | +|copies of the Software, and to permit persons to whom the Software is | +|furnished to do so, subject to the following conditions: | +| | +|The above copyright notice and this permission notice shall be included in | +|all copies or substantial portions of the Software. | +| | +|THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | +|IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | +|FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE | +|AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | +|LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,| +|OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE| +|SOFTWARE. | ++-----------------------------------------------------------------------------+ +}} diff --git a/src/reboot.spin b/src/reboot.spin new file mode 100644 index 0000000..768e647 --- /dev/null +++ b/src/reboot.spin @@ -0,0 +1,55 @@ +'****************************************************************************** +' Copyright (c) 2011, 2012, 2013, Dave Hein +' See end of file for terms of use. +'****************************************************************************** +CON + _clkmode = xinput + _xinfreq = $4e495053 ' SPIN + +OBJ + c : "clibsd" + sys : "sysdefs" + +PUB main(argc, argv) | i + c.enter + c.printf0(string("Rebooting\n")) + waitcnt(cnt + clkfreq/10) + save_sysparm + reboot + +pub save_sysparm | outfile, ptr + outfile := c.fopen(string("/_sysparm"), string("w")) + ifnot outfile + c.printf0(string("Can't open /_sysparm\n")) + return + c.fprintf1(outfile, string("%d\n"), long[sys#unixtime]) + c.fprintf2(outfile, string("%d %d 0\n"), long[sys#timezone], long[sys#config]) + c.fprintf0(outfile, string("0 0 0\n")) + c.fprintf0(outfile, string("GPWD /\n")) + c.fprintf0(outfile, string("LSCRIPT_FILE /bin/_shellrc\n")) + c.fprintf0(outfile, string("P# 0\n")) + c.fclose(outfile) + +{{ ++-----------------------------------------------------------------------------+ +| TERMS OF USE: MIT License | ++-----------------------------------------------------------------------------+ +|Permission is hereby granted, free of charge, to any person obtaining a copy | +|of this software and associated documentation files (the "Software"), to deal| +|in the Software without restriction, including without limitation the rights | +|to use, copy, modify, merge, publish, distribute, sublicense, and/or sell | +|copies of the Software, and to permit persons to whom the Software is | +|furnished to do so, subject to the following conditions: | +| | +|The above copyright notice and this permission notice shall be included in | +|all copies or substantial portions of the Software. | +| | +|THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | +|IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | +|FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE | +|AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | +|LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,| +|OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE| +|SOFTWARE. | ++-----------------------------------------------------------------------------+ +}} diff --git a/src/resolve.c b/src/resolve.c new file mode 100644 index 0000000..de2c242 --- /dev/null +++ b/src/resolve.c @@ -0,0 +1,60 @@ +void dfs_resolve_path(const char *fname, char *path) +{ + char *ptr; + char *ptr1; + + if (!strcmp(fname, ".")) fname++; + else if (!strncmp(fname, "./",2) || !strncmp(fname, ".\\",2)) fname += 2; + + if (fname[0] == '/' || fname[0] == '\\') + strcpy(path, fname); + else if (fname[0] == 0) + strcpy(path, dfs_currdir); + else + { + strcpy(path, dfs_currdir); + int pathLen = strlen(path); + if (path[pathLen-1] != '/' && path[pathLen-1] != '\\') strcat(path, "/"); + strcat(path, fname); + } + + // Whack DOS paths to UNIX paths + for (ptr = path; *ptr; ptr++) + if (*ptr == '\\') + *ptr = '/'; + + // Process .. + ptr = path; + while (*ptr) + { + if (!strncmp(ptr, "/..", 3) && (ptr[3] == 0 || ptr[3] == '/')) + { + if (ptr == path) + { + movestr(ptr, ptr+3); + } + else + { + ptr1 = ptr - 1; + while (ptr1 != path) + { + if (*ptr1 == '/') break; + ptr1--; + } + movestr(ptr1, ptr+3); + ptr = ptr1; + } + } + else + { + ptr++; + while (*ptr) + { + if (*ptr == '/') break; + ptr++; + } + } + } + + if (path[0] == 0) strcpy(path, "/"); +} diff --git a/src/resolve.spin b/src/resolve.spin new file mode 100644 index 0000000..feff4b0 --- /dev/null +++ b/src/resolve.spin @@ -0,0 +1,52 @@ +OBJ + c : "clibsd" + +PUB resolve_path(fname, path) | ptr, ptr1, pathLen + if strcomp(fname, string(".")) + fname++ + elseifnot c.strncmp(fname, string("./"), 2) + fname += 2 + if byte[fname] == "/" + c.strcpy(path, fname) + elseifnot byte[fname] + c.getcwd(path, 80) + else + c.getcwd(path, 80) + 'c.printf3(string("resolve_path0: %s -> %s + %s\n"), fname, path, fname) + pathLen := strsize(path) + if byte[path][pathLen - 1] <> "/" + c.strcat(path, string("/")) + c.strcat(path, fname) + ' Process .. + 'c.printf2(string("resolve_path1: %s -> %s\n"), fname, path) + ptr := path + repeat while byte[ptr] + if (not c.strncmp(ptr, string("/.."), 3) and(byte[ptr][3] == 0 or byte[ptr][3] == "/")) + if (ptr == path) + movestr(ptr, ptr + 3) + else + ptr1 := ptr - 1 + repeat while (ptr1 <> path) + if (byte[ptr1] == "/") + quit + ptr1-- + movestr(ptr1, ptr + 3) + ptr := ptr1 + elseif not byte[ptr][1] and ptr <> path + byte[ptr] := 0 + elseif byte[ptr][1] == "/" + movestr(ptr, ptr + 1) + else + ptr++ + repeat while (byte[ptr]) + if (byte[ptr] == "/") + quit + ptr++ + ifnot byte[path] + c.strcpy(path, string("/")) + 'c.printf2(string("resolve_path2: %s -> %s\n"), fname, path) + +PUB movestr(ptr1, ptr2) + repeat while byte[ptr2] + byte[ptr1++] := byte[ptr2++] + byte[ptr1] := 0 diff --git a/src/resolve1.spin b/src/resolve1.spin new file mode 100644 index 0000000..69b729d --- /dev/null +++ b/src/resolve1.spin @@ -0,0 +1,35 @@ +PUB resolve_path(fname, path) | ptr, ptr1 + if strcomp(fname, string(".")) + fname++ + elseifnot c.strncmp(fname, string("./"), 2) + fname += 2 + if byte[fname] == "/" + c.strcpy(path, fname) + elseifnot byte[fname] + c.strcpy(path, @currdir) + else + c.strcpy(path, @currdir) + if byte[path][1] + c.strcat(path, string("/")) + c.strcat(path, fname) + ptr := path + repeat while byte[ptr] + if not c.strncmp(ptr, string("/.."), 3) and (byte[ptr][3] == 0 or byte[ptr][3] == "/") + if (ptr == path) + movestr(ptr, ptr + 3) + else + ptr1 := ptr - 1 + repeat while ptr1 <> path + if (byte[ptr1] == "/") + quit + ptr1-- + movestr(ptr1, ptr + 3) + ptr := ptr1 + elseifnot byte[ptr][1] == "/" + movestr(ptr, ptr + 1) + elseif byte[ptr][1] == 0 and ptr <> path + byte[ptr] := 0 + else + repeat while byte[++ptr] + if byte[ptr] == "/" + quit diff --git a/src/rm.spin b/src/rm.spin new file mode 100644 index 0000000..c28f660 --- /dev/null +++ b/src/rm.spin @@ -0,0 +1,57 @@ +'****************************************************************************** +' Copyright (c) 2011, 2012, Dave Hein +' See end of file for terms of use. +'****************************************************************************** +CON + _clkmode = xinput + _xinfreq = $4e495053 ' SPIN + +OBJ + c : "clibsd" + g : "glob" + +PUB main(argc, argv) | attrib, argi, globdata, str[25], fname + c.enter + if argc < 2 + c.printf0(string("usage: rm file1 [file2 ...]\n")) + c.exit(1) + + repeat argi from 1 to argc - 1 + globdata := 0 + repeat while (fname := g.glob(long[argv][argi], @globdata, @str)) + if (result := c.remove(fname)) < 0 + attrib := c.getmod(fname) + if attrib == -1 + c.printf1(string("%s not found\n"), fname) + elseif attrib & $10 + c.printf1(string("%s is a directory\n"), fname) + elseif attrib & 1 + c.printf1(string("%s is write_protected"), fname) + else + c.printf1(string("Could not delete %s"), fname) + + c.exit(0) + +{{ ++-----------------------------------------------------------------------------+ +| TERMS OF USE: MIT License | ++-----------------------------------------------------------------------------+ +|Permission is hereby granted, free of charge, to any person obtaining a copy | +|of this software and associated documentation files (the "Software"), to deal| +|in the Software without restriction, including without limitation the rights | +|to use, copy, modify, merge, publish, distribute, sublicense, and/or sell | +|copies of the Software, and to permit persons to whom the Software is | +|furnished to do so, subject to the following conditions: | +| | +|The above copyright notice and this permission notice shall be included in | +|all copies or substantial portions of the Software. | +| | +|THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | +|IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | +|FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE | +|AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | +|LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,| +|OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE| +|SOFTWARE. | ++-----------------------------------------------------------------------------+ +}} diff --git a/src/runprog.spin b/src/runprog.spin new file mode 100644 index 0000000..f5da29e --- /dev/null +++ b/src/runprog.spin @@ -0,0 +1,110 @@ +OBJ + sys : "sysdefs" + +PUB runprog(argc1, argv1, sector1, size1, type1) + coginit(cogid, @run_prog, @argc1) + +DAT + +run_prog mov temp, par + rdlong argc, temp + add temp, #4 + rdlong argv, temp + add temp, #4 + rdlong sector, temp + add temp, #4 + rdlong count, temp + add count, #511 + shr count, #9 + add temp, #4 + rdlong type, temp + + ' Save clkfreq and clkmode + rdlong clkfreq_, #0 + rdbyte clkmode_, #4 + + ' Read the program into memory +loop1 wrlong sector, block_index + wrlong address, buffer_address + wrlong read_command, command +loop2 rdlong temp, command + cmp temp, read_command wz + if_z jmp #loop2 + add sector, #1 + add address, #256 + add address, #256 + djnz count, #loop1 + + ' Restore clkfreq and clkmode + wrlong clkfreq_, #0 + wrbyte clkmode_, #4 + + ' Check type + cmp type, #0 wz + if_z jmp #spincode + cmp type, #1 wz + if_z jmp #c_code + +stand_alone + wrlong stop_command, command +:loop rdlong temp, command + cmp temp, stop_command wz + if_z jmp #:loop + rdlong temp, engine_cog + sub temp, #1 + cogstop temp + jmp #startcog +c_code + mov parm, cparm + rdlong temp, #32 + (17*4) + add temp, #3*4 + wrlong argv, temp + jmp #startcog + +spincode + ' Get VBASE and DBASE + rdword vbase, #8 + rdword dbase, #10 + + ' Clear VAR area + mov count, dbase + sub count, vbase + shr count, #2 +loop3 wrlong zero, vbase + add vbase, #4 + djnz count, #loop3 + + ' Add parameters + wrlong zero, dbase + add dbase, #4 + wrlong argc, dbase + add dbase, #4 + wrlong argv, dbase + + ' Start cog +startcog cogid temp + and temp, #7 + or parm, temp + coginit parm + +engine_cog long sys#spi_engine_cog +block_index long sys#spi_block_index +buffer_address long sys#spi_buffer_address +command long sys#spi_command +read_command long "r" +stop_command long "z" +zero long 0 +address long 0 +parm long (4 << 16 ) | ($f004 << 2) +cparm long (sys#rendezvous << 16) | ($20 << 2) +sector res 1 +count res 1 +temp res 1 +clkfreq_ res 1 +clkmode_ res 1 +vbase res 1 +dbase res 1 +argc res 1 +argv res 1 +type res 1 + diff --git a/src/safe_spi.spin b/src/safe_spi.spin new file mode 100644 index 0000000..a45be75 --- /dev/null +++ b/src/safe_spi.spin @@ -0,0 +1,106 @@ +'****************************************************************************** +' Copyright (c) 2011, 2012, Dave Hein +' See end of file for terms of use. +'****************************************************************************** +{{ + SPI interface routines for SD & SDHC & MMC cards + + Jonathan "lonesock" Dummer + version 0.3.0 2009 July 19 + + Using multiblock SPI mode exclusively. + + This is the "SAFE" version...uses + * 1 instruction per bit writes + * 2 instructions per bit reads + + For the fsrw project: + fsrw.sf.net +}} +OBJ + sys : "sysdefs" + +CON + ' Rendezvoud locations + pSPI_engine_cog = sys#SPI_engine_cog + pSPI_command = sys#SPI_command + pSPI_block_index = sys#SPI_block_index + pSPI_buffer_address = sys#SPI_buffer_address + + ' Error codes + ERR_BLOCK_NOT_LONG_ALIGNED = -4 + ERR_SPI_ENGINE_NOT_RUNNING = -999 + +PUB readblock( block_index, buffer_address ) + if long[pSPI_engine_cog] == 0 + abort ERR_SPI_ENGINE_NOT_RUNNING + if (buffer_address & 3) + abort ERR_BLOCK_NOT_LONG_ALIGNED + long[pSPI_block_index] := block_index + long[pSPI_buffer_address] := buffer_address + long[pSPI_command] := "r" + repeat while long[pSPI_command] == "r" + if long[pSPI_command] < 0 + abort long[pSPI_command] + +PUB writeblock( block_index, buffer_address ) + if long[pSPI_engine_cog] == 0 + abort ERR_SPI_ENGINE_NOT_RUNNING + if (buffer_address & 3) + abort ERR_BLOCK_NOT_LONG_ALIGNED + long[pSPI_block_index] := block_index + long[pSPI_buffer_address] := buffer_address + long[pSPI_command] := "w" + repeat while long[pSPI_command] == "w" + if long[pSPI_command] < 0 + abort long[pSPI_command] + +PUB get_seconds + if long[pSPI_engine_cog] == 0 + abort ERR_SPI_ENGINE_NOT_RUNNING + long[pSPI_command] := "t" + repeat while long[pSPI_command] == "t" + ' seconds are in SPI_block_index, remainder is in SPI_buffer_address + return long[pSPI_block_index] + +PUB get_milliseconds : ms + if long[pSPI_engine_cog] == 0 + abort ERR_SPI_ENGINE_NOT_RUNNING + long[pSPI_command] := "t" + repeat while long[pSPI_command] == "t" + ' seconds are in SPI_block_index, remainder is in SPI_buffer_address + ms := long[pSPI_block_index] * 1000 + ms += long[pSPI_buffer_address] * 1000 / clkfreq + + +PUB release +{{ + I do not want to abort if the cog is not + running, as this is called from stop, which + is called from start/ [8^) +}} + if long[pSPI_engine_cog] + long[pSPI_command] := "z" + repeat while long[pSPI_command] == "z" + +'' MIT LICENSE +{{ +' Permission is hereby granted, free of charge, to any person obtaining +' a copy of this software and associated documentation files +' (the "Software"), to deal in the Software without restriction, +' including without limitation the rights to use, copy, modify, merge, +' publish, distribute, sublicense, and/or sell copies of the Software, +' and to permit persons to whom the Software is furnished to do so, +' subject to the following conditions: +' +' The above copyright notice and this permission notice shall be included +' in all copies or substantial portions of the Software. +' +' THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +' EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +' MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +' IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +' CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +' TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +' SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +}} diff --git a/src/safe_spi_p.spin b/src/safe_spi_p.spin new file mode 100644 index 0000000..323f8a5 --- /dev/null +++ b/src/safe_spi_p.spin @@ -0,0 +1,934 @@ +'****************************************************************************** +' Copyright (c) 2011, 2012, Dave Hein +' See end of file for terms of use. +'****************************************************************************** +{{ + SPI interface routines for SD & SDHC & MMC cards + + Jonathan "lonesock" Dummer + version 0.3.0 2009 July 19 + + Using multiblock SPI mode exclusively. + + This is the "SAFE" version...uses + * 1 instruction per bit writes + * 2 instructions per bit reads + + For the fsrw project: + fsrw.sf.net +}} +OBJ + sys : "sysdefs" + +CON + pSPI_engine_cog = sys#SPI_engine_cog + pSPI_command = sys#SPI_command ' "t", "r", "w", 0 =>done, <0 => error + pSPI_block_index = sys#SPI_block_index ' which 512-byte block to read/write (cnt at init) + pSPI_buffer_address = sys#SPI_buffer_address ' where to get/put the data in Hub RAM + +CON + ' possible card types + type_MMC = 1 + type_SD = 2 + type_SDHC = 3 + + ' Error codes + ERR_CARD_NOT_RESET = -1 + ERR_3v3_NOT_SUPPORTED = -2 + ERR_OCR_FAILED = -3 + ERR_BLOCK_NOT_LONG_ALIGNED = -4 + '... + ' These errors are for the assembly engine...they are negated inside, and need to be <= 511 + ERR_ASM_NO_READ_TOKEN = 100 + ERR_ASM_BLOCK_NOT_WRITTEN = 101 + ' NOTE: errors -128 to -255 are reserved for reporting R1 response errors + '... + ERR_SPI_ENGINE_NOT_RUNNING = -999 + ERR_CARD_BUSY_TIMEOUT = -1000 + + ' SDHC/SD/MMC command set for SPI + CMD0 = $40+0 ' GO_IDLE_STATE + CMD1 = $40+1 ' SEND_OP_COND (MMC) + ACMD41 = $C0+41 ' SEND_OP_COND (SDC) + CMD8 = $40+8 ' SEND_IF_COND + CMD9 = $40+9 ' SEND_CSD + CMD10 = $40+10 ' SEND_CID + CMD12 = $40+12 ' STOP_TRANSMISSION + CMD13 = $40+13 ' SEND_STATUS + ACMD13 = $C0+13 ' SD_STATUS (SDC) + CMD16 = $40+16 ' SET_BLOCKLEN + CMD17 = $40+17 ' READ_SINGLE_BLOCK + CMD18 = $40+18 ' READ_MULTIPLE_BLOCK + CMD23 = $40+23 ' SET_BLOCK_COUNT (MMC) + ACMD23 = $C0+23 ' SET_WR_BLK_ERASE_COUNT (SDC) + CMD24 = $40+24 ' WRITE_BLOCK + CMD25 = $40+25 ' WRITE_MULTIPLE_BLOCK + CMD55 = $40+55 ' APP_CMD + CMD58 = $40+58 ' READ_OCR + CMD59 = $40+59 ' CRC_ON_OFF + +PUB start( basepin ) +{{ + This is a compatibility wrapper, and requires that the pins be + both consecutive, and in the order DO CLK DI CS. +}} + return start_explicit( basepin, basepin+1, basepin+2, basepin+3 ) + +PUB start_explicit( DO, CLK, DI, CS ) : card_type | tmp, i +{{ + Do all of the card initialization in SPIN, then hand off the pin + information to the assembly cog for hot SPI block R/W action! +}} + ' Start from scratch + stop + ' clear my log buffer + { + bytefill( @log_cmd_resp, 0, LOG_SIZE+1 ) + dbg_ptr := @log_cmd_resp + dbg_end := dbg_ptr + LOG_SIZE + '} + ' wait ~4 milliseconds + waitcnt( 500 + (clkfreq>>8) + cnt ) + ' check for C3 pins + if DO == 10 and CLK == 11 and DI == 9 and CS == 25 + maskINC := 1 << 8 + ' (start with cog variables, _BEFORE_ loading the cog) + pinDO := DO + maskDO := |< DO + pinCLK := CLK + pinDI := DI + maskDI := |< DI + maskCS := |< CS + adrShift := 9 ' block = 512 * index, and 512 = 1<<9 + ' pass the output pin mask via the command register + maskAll := maskCS | (| 74 clocks + 'outa |= maskAll + deselect + outa |= (| 1 + tmp := send_cmd_slow( CMD0, 0, $95 ) + if (tmp & 4) + ' the card said CMD0 ("go idle") was invalid, so we're possibly stuck in read or write mode + if i & 1 + ' exit multiblock read mode + repeat 4 + read_32_slow ' these extra clocks are required for some MMC cards + send_slow( $FD, 8 ) ' stop token + read_32_slow + repeat while read_slow <> $FF + else + ' exit multiblock read mode + send_cmd_slow( CMD12, 0, $61 ) + if tmp <> 1 + ' the reset command failed! + crash( ERR_CARD_NOT_RESET ) + ' Is this a SD type 2 card? + if send_cmd_slow( CMD8, $1AA, $87 ) == 1 + ' Type2 SD, check to see if it's a SDHC card + tmp := read_32_slow + ' check the supported voltage + if (tmp & $1FF) <> $1AA + crash( ERR_3v3_NOT_SUPPORTED ) + ' try to initialize the type 2 card with the High Capacity bit + repeat while send_cmd_slow( ACMD41, |<30, $77 ) + ' the card is initialized, let's read back the High Capacity bit + if send_cmd_slow( CMD58, 0, $FD ) <> 0 + crash( ERR_OCR_FAILED ) + ' get back the data + tmp := read_32_slow + ' check the bit + if tmp & |<30 + card_type := type_SDHC + adrShift := 0 + else + card_type := type_SD + else + ' Either a type 1 SD card, or it's MMC, try SD 1st + if send_cmd_slow( ACMD41, 0, $E5 ) < 2 + ' this is a type 1 SD card (1 means busy, 0 means done initializing) + card_type := type_SD + repeat while send_cmd_slow( ACMD41, 0, $E5 ) + else + ' mark that it's MMC, and try to initialize + card_type := type_MMC + repeat while send_cmd_slow( CMD1, 0, $F9 ) + ' some SD or MMC cards may have the wrong block size, set it here + send_cmd_slow( CMD16, 512, $15 ) + ' card is mounted, make sure the CRC is turned off + send_cmd_slow( CMD59, 0, $91 ) + ' check the status + 'send_cmd_slow( CMD13, 0, $0D ) + ' done with the SPI bus for now + deselect 'outa |= maskCS + ' set my counter modes for super fast SPI operation + ' writing: NCO single-ended mode, output on DI + writeMode := (%00100 << 26) | (DI << 0) + ' reading + 'readMode := (%11000 << 26) | (DO << 0) | (CLK << 9) + ' clock + 'clockLineMode := (%00110 << 26) | (CLK << 0) ' DUTY, 25% duty cycle + ' clock + clockLineMode := (%00100 << 26) | (CLK << 0) ' NCO, 50% duty cycle + ' how many bytes (8 clocks, >>3) fit into 1/2 of a second (>>1), 4 clocks per instruction (>>2)? + N_in8_500ms := clkfreq >> constant(1+2+3) + ' how long should we wait before auto-exiting any multiblock mode? + idle_limit := 125 ' ms, NEVER make this > 1000 + idle_limit := clkfreq / (1000 / idle_limit) ' convert to counts + ' Hand off control to the assembly engine's cog + bufAdr := pSPI_buffer_address + sdAdr := pSPI_block_index + long[pSPI_command] := 0 ' just make sure it's not 1 + ' start my driver cog and wait till I hear back that it's done + long[pSPI_engine_cog] := cognew( @SPI_engine_entry, pSPI_command ) + 1 + if( long[pSPI_engine_cog] == 0 ) + crash( ERR_SPI_ENGINE_NOT_RUNNING ) + repeat while long[pSPI_command] <> -1 + ' and we no longer need to control any pins from here + deselect + dira &= !maskAll + ' the return variable is card_type + +PUB release +{{ + I do not want to abort if the cog is not + running, as this is called from stop, which + is called from start/ [8^) +}} + if long[pSPI_engine_cog] + long[pSPI_command] := "z" + repeat while long[pSPI_command] == "z" + +PUB stop +{{ + kill the assembly driver cog. +}} + release + if long[pSPI_engine_cog] + cogstop( long[pSPI_engine_cog]~ - 1 ) + +PRI crash( abort_code ) +{{ + In case of Bad Things(TM) happening, + exit as gracefully as possible. +}} + ' and we no longer need to control any pins from here + deselect + dira &= !maskAll + ' and report our error + abort abort_code + +PRI send_cmd_slow( cmd, val, crc ) : reply | time_stamp +{{ + Send down a command and return the reply. + Note: slow is an understatement! + Note: this uses the assembly DAT variables for pin IDs, + which means that if you run this multiple times (say for + multiple SD cards), these values will change for each one. + But this is OK as all of these functions will be called + during the initialization only, before the PASM engine is + running. +}} + ' if this is an application specific command, handle it + if (cmd & $80) + ' ACMD is the command sequense of CMD55-CMD + cmd &= $7F + reply := send_cmd_slow( CMD55, 0, $65 ) + if (reply > 1) + return reply + ' the CS line needs to go low during this operation + deselect 'outa |= maskCS + select 'outa &= !maskCS + ' give the card a few cocks to finish whatever it was doing + read_32_slow + ' send the command byte + send_slow( cmd, 8 ) + ' send the value long + send_slow( val, 32 ) + ' send the CRC byte + send_slow( crc, 8 ) + ' is this a CMD12?, if so, stuff byte + if cmd == CMD12 + read_slow + ' read back the response (spec declares 1-8 reads max for SD, MMC is 0-8) + time_stamp := 9 + repeat + reply := read_slow + while( reply & $80 ) and ( time_stamp-- ) + ' done, and 'reply' is already pre-loaded + { + if dbg_ptr < (dbg_end-1) + byte[dbg_ptr++] := cmd + byte[dbg_ptr++] := reply + if (cmd&63) == 13 + ' get the second byte + byte[dbg_ptr++] := cmd + byte[dbg_ptr++] := read_slow + '} + +PRI send_slow( value, bits_to_send ) + value ><= bits_to_send + repeat bits_to_send + outa[pinCLK]~ + outa[pinDI] := value + value >>= 1 + outa[pinCLK]~~ + +PRI read_32_slow : r + repeat 4 + r <<= 8 + r |= read_slow + +PRI read_slow : r +{{ + Read back 8 bits from the card +}} + ' we need the DI line high so a read can occur + outa[pinDI]~~ + ' get 8 bits (remember, r is initialized to 0 by SPIN) + repeat 8 + outa[pinCLK]~ + outa[pinCLK]~~ + r += r + ina[pinDO] + ' error check + if( (cnt - long[pSPI_block_index]) > (clkfreq << 2) ) + crash( ERR_CARD_BUSY_TIMEOUT ) + +PRI deselect + if maskINC + outa &= !maskCS + outa |= maskCS + +PRI select + if maskINC + outa &= !maskCS + outa |= maskCS + repeat 5 + outa |= maskINC + outa &= !maskINC + else + outa &= !maskCS + +DAT +{{ + This is the assembly engine for doing fast block + reads and writes. This is *ALL* it does! +}} +ORG 0 +SPI_engine_entry + ' Counter A drives data out + mov ctra,writeMode + ' Counter B will always drive my clock line + mov ctrb,clockLineMode + ' set our output pins to match the pin mask + mov dira,maskAll + ' handshake that we now control the pins + neg user_request,#1 + wrlong user_request,par + ' start my seconds' counter here + mov last_time,cnt + + ' Check if C3 mode or normal CS mode + cmp maskINC, #0 wz + if_nz jmp #waiting_for_command 'c3_mode + mov _select, jmp_cs_sel + mov _deselect, jmp_cs_des + +waiting_for_command + ' update my seconds counter, but also track the idle + ' time so we can to release the card after timeout. + call #handle_time + call #unix_time_chk + ' read the command, and make sure it's from the user (> 0) + rdlong user_request,par + cmps user_request,#0 wz,wc +if_be jmp #waiting_for_command + ' handle our card based commands + cmp user_request,#"r" wz +if_z jmp #read_ahead + cmp user_request,#"w" wz +if_z jmp #write_behind + cmp user_request,#"z" wz +if_z jmp #release_card + ' time requests are handled differently + cmp user_request,#"t" wz ' time +if_z wrlong seconds,sdAdr ' seconds goes into the SD index register +if_z wrlong dtime,bufAdr ' the remainder goes into the buffer address register + ' in all other cases, clear the user's request + mov user_request,#0 + wrlong user_request,par + jmp #waiting_for_command + + +release_card + mov user_cmd,#"z" ' request a release + neg lastIndexPlus,#1 ' reset the last block index + neg user_idx,#1 ' and make this match it + call #handle_command + mov user_request,user_cmd + wrlong user_request,par + jmp #waiting_for_command + +read_ahead + rdlong user_idx,sdAdr + ' if the correct block is not already loaded, load it + mov tmp1,user_idx + add tmp1,#1 + cmp tmp1,lastIndexPlus wz +if_z cmp lastCommand,#"r" wz +if_z jmp #:get_on_with_it + mov user_cmd,#"r" + call #handle_command +:get_on_with_it + ' copy the data up into Hub RAM + movi transfer_long,#%000010_000 'set to wrlong + call #hub_cog_transfer + ' signify that the data is ready, Spin can continue + mov user_request,user_cmd + wrlong user_request,par + ' request the next block + mov user_cmd,#"r" + add user_idx,#1 + call #handle_command + ' done + jmp #waiting_for_command + +write_behind + rdlong user_idx,sdAdr + ' copy data in from Hub RAM + movi transfer_long,#%000010_001 'set to rdlong + call #hub_cog_transfer + ' signify that we have the data, Spin can continue + mov user_request,user_cmd + wrlong user_request,par + ' write out the block + mov user_cmd,#"w" + call #handle_command + ' done + jmp #waiting_for_command + +{{ + Set user_cmd and user_idx before calling this +}} +handle_command + ' Can we stay in the old mode? (address = old_address+1) && (old mode == new_mode) + cmp lastIndexPlus,user_idx wz +if_z cmp user_cmd,lastCommand wz +if_z jmp #:execute_block_command + ' we fell through, must exit the old mode! (except if the old mode was "release") + cmp lastCommand,#"w" wz +if_z call #stop_mb_write + cmp lastCommand,#"r" wz +if_z call #stop_mb_read + ' and start up the new mode! + cmp user_cmd,#"w" wz +if_z call #start_mb_write + cmp user_cmd,#"r" wz +if_z call #start_mb_read + cmp user_cmd,#"z" wz +if_z call #release_DO +:execute_block_command + ' track the (new) last index and command + mov lastIndexPlus,user_idx + add lastIndexPlus,#1 + mov lastCommand,user_cmd + ' do the block read or write or terminate! + cmp user_cmd,#"w" wz +if_z call #write_single_block + cmp user_cmd,#"r" wz +if_z call #read_single_block + cmp user_cmd,#"z" wz +if_z mov user_cmd,#0 + ' done +handle_command_ret + ret + +{=== these PASM functions get me in and out of multiblock mode ===} +release_DO + ' we're already out of multiblock mode, so + ' deselect the card and send out some clocks + call #_deselect 'or outa,maskCS + call #in8 + call #in8 + ' if you are using pull-up resistors, and need all + ' lines tristated, then uncomment the following line. + ' for Cluso99 + 'mov dira,#0 +release_DO_ret + ret + +start_mb_read + movi block_cmd,#CMD18<<1 + call #send_SPI_command_fast +start_mb_read_ret + ret + +stop_mb_read + movi block_cmd,#CMD12<<1 + call #send_SPI_command_fast + call #busy_fast +stop_mb_read_ret + ret + +start_mb_write + movi block_cmd,#CMD25<<1 + call #send_SPI_command_fast +start_mb_write_ret + ret + +stop_mb_write + call #busy_fast + ' only some cards need these extra clocks + mov tmp1,#16 +:loopity + call #in8 + djnz tmp1,#:loopity + ' done with hack + movi phsa,#$FD<<1 + call #out8 + call #in8 ' stuff byte + call #busy_fast +stop_mb_write_ret + ret + +send_SPI_command_fast + ' make sure we have control of the output lines + mov dira,maskAll + ' make sure the CS line transitions low + call #_deselect 'or outa,maskCS + call #_select 'andn outa,maskCS + ' 8 clocks + call #in8 + ' send the data + mov phsa,block_cmd ' do which ever block command this is (already in the top 8 bits) + call #out8 ' write the byte + mov phsa,user_idx ' read in the desired block index + shl phsa,adrShift ' this will multiply by 512 (bytes/sector) for MMC and SD + call #out8 ' move out the 1st MSB ' + rol phsa,#1 + call #out8 ' move out the 1st MSB ' + rol phsa,#1 + call #out8 ' move out the 1st MSB ' + rol phsa,#1 + call #out8 ' move out the 1st MSB ' + ' bogus CRC value + call #in8 ' in8 looks like out8 with $FF + ' CMD12 requires a stuff byte + shr block_cmd,#24 + cmp block_cmd,#CMD12 wz +if_z call #in8 ' 8 clocks + ' get the response + mov tmp1,#9 +:cmd_response + call #in8 + test readback,#$80 wc,wz +if_c djnz tmp1,#:cmd_response +if_nz neg user_cmd,readback + ' done +send_SPI_command_fast_ret + ret + + +busy_fast + mov tmp1,N_in8_500ms +:still_busy + call #in8 + cmp readback,#$FF wz +if_nz djnz tmp1,#:still_busy +busy_fast_ret + ret + + +out8 + andn outa,maskDI + 'movi phsb,#%11_0000000 + mov phsb,#0 + movi frqb,#%01_0000000 + rol phsa,#1 + rol phsa,#1 + rol phsa,#1 + rol phsa,#1 + rol phsa,#1 + rol phsa,#1 + rol phsa,#1 + mov frqb,#0 + ' don't shift out the final bit...already sent, but be aware + ' of this when sending consecutive bytes (send_cmd, for e.g.) +out8_ret + ret + +{ +in8 + or outa,maskDI + mov ctra,readMode + ' Start my clock + mov frqa,#1<<7 + mov phsa,#0 + movi phsb,#%11_0000000 + movi frqb,#%01_0000000 + ' keep reading in my value, one bit at a time! (Kuneko - "Wh) + shr frqa,#1 + shr frqa,#1 + shr frqa,#1 + shr frqa,#1 + shr frqa,#1 + shr frqa,#1 + shr frqa,#1 + mov frqb,#0 ' stop the clock + mov readback,phsa + mov frqa,#0 + mov ctra,writeMode +in8_ret + ret +} +in8 + neg phsa,#1' DI high + mov readback,#0 + ' set up my clock, and start it + movi phsb,#%011_000000 + movi frqb,#%001_000000 + ' keep reading in my value + test maskDO,ina wc + rcl readback,#1 + test maskDO,ina wc + rcl readback,#1 + test maskDO,ina wc + rcl readback,#1 + test maskDO,ina wc + rcl readback,#1 + test maskDO,ina wc + rcl readback,#1 + test maskDO,ina wc + rcl readback,#1 + test maskDO,ina wc + rcl readback,#1 + test maskDO,ina wc + mov frqb,#0 ' stop the clock + rcl readback,#1 + mov phsa,#0 'DI low +in8_ret + ret + + +' this is called more frequently than 1 Hz, and +' is only called when the user command is 0. +handle_time + mov tmp1,cnt ' get the current timestamp + add idle_time,tmp1 ' add the current time to my idle time counter + sub idle_time,last_time ' subtract the last time from my idle counter (hence delta) + add dtime,tmp1 ' add to my accumulator, + sub dtime,last_time ' and subtract the old (adding delta) + mov last_time,tmp1 ' update my "last timestamp" + rdlong tmp1,#0 ' what is the clock frequency? + cmpsub dtime,tmp1 wc ' if I have more than a second in my accumulator + addx seconds,#0 ' then add it to "seconds" + ' this part is to auto-release the card after a timeout + cmp idle_time,idle_limit wz,wc +if_b jmp #handle_time_ret ' don't clear if we haven't hit the limit + mov user_cmd,#"z" ' we can't overdo it, the command handler makes sure + neg lastIndexPlus,#1 ' reset the last block index + neg user_idx,#1 ' and make this match it + call #handle_command ' release the card, but don't mess with the user's request register +handle_time_ret + ret + +hub_cog_transfer +' setup for all 4 passes + mov ctrb,clockXferMode + mov frqb,#1 + rdlong buf_ptr,bufAdr + mov ops_left,#4 + movd transfer_long,#speed_buf +four_transfer_passes + ' sync to the Hub RAM access + rdlong tmp1,tmp1 + ' how many long to move on this pass? (512 bytes / 4)longs / 4 passes + mov tmp1,#(512 / 4 / 4) + ' get my starting address right (phsb is incremented 1 per clock, so 16 each Hub access) + mov phsb,buf_ptr + ' write the longs, stride 4...low 2 bits of phsb are ignored +transfer_long + rdlong 0-0,phsb + add transfer_long,incDest4 + djnz tmp1,#transfer_long + ' go back to where I started, but advanced 1 long + sub transfer_long,decDestNminus1 + ' offset my Hub pointer by one long per pass + add buf_ptr,#4 + ' do all 4 passes + djnz ops_left,#four_transfer_passes + ' restore the counter mode + mov frqb,#0 + mov phsb,#0 + mov ctrb,clockLineMode +hub_cog_transfer_ret + ret + + +read_single_block + ' where am I sending the data? + movd :store_read_long,#speed_buf + mov ops_left,#128 + ' wait until the card is ready + mov tmp1,N_in8_500ms +:get_resp + call #in8 + cmp readback,#$FE wz +if_nz djnz tmp1,#:get_resp +if_nz neg user_cmd,#ERR_ASM_NO_READ_TOKEN +if_nz jmp #read_single_block_ret + ' set DI high + neg phsa,#1 + ' read the data + mov ops_left,#128 +:read_loop + mov tmp1,#4 + movi phsb,#%011_000000 +:in_byte + ' Start my clock + movi frqb,#%001_000000 + ' keep reading in my value, BACKWARDS! (Brilliant idea by Tom Rokicki!) + test maskDO,ina wc + rcl readback,#8 + test maskDO,ina wc + muxc readback,#2 + test maskDO,ina wc + muxc readback,#4 + test maskDO,ina wc + muxc readback,#8 + test maskDO,ina wc + muxc readback,#16 + test maskDO,ina wc + muxc readback,#32 + test maskDO,ina wc + muxc readback,#64 + test maskDO,ina wc + mov frqb,#0 ' stop the clock + muxc readback,#128 + ' go back for more + djnz tmp1,#:in_byte + ' make it...NOT backwards [8^) + rev readback,#0 +:store_read_long + mov 0-0,readback ' due to some counter weirdness, we need this mov + add :store_read_long,const512 + djnz ops_left,#:read_loop + + ' set DI low + mov phsa,#0 + + ' now read 2 trailing bytes (CRC) + call #in8 ' out8 is 2x faster than in8 + call #in8 ' and I'm not using the CRC anyway + ' give an extra 8 clocks in case we pause for a long time + call #in8 ' in8 looks like out8($FF) + + ' all done successfully + mov idle_time,#0 + mov user_cmd,#0 +read_single_block_ret + ret + +write_single_block + ' where am I getting the data? (all 512 bytes / 128 longs of it?) + movs :write_loop,#speed_buf + ' read in 512 bytes (128 longs) from Hub RAM and write it to the card + mov ops_left,#128 + ' just hold your horses + call #busy_fast + ' $FC for multiblock, $FE for single block + movi phsa,#$FC<<1 + call #out8 + mov phsb,#0 ' make sure my clock accumulator is right + 'movi phsb,#%11_0000000 +:write_loop + ' read 4 bytes + mov phsa,speed_buf + add :write_loop,#1 + ' a long in LE order is DCBA + rol phsa,#24 ' move A7 into position, so I can do the swizzled version + movi frqb,#%010000000 ' start the clock (remember A7 is already in place) + rol phsa,#1 ' A7 is going out, at the end of this instr, A6 is in place + rol phsa,#1 ' A5 + rol phsa,#1 ' A4 + rol phsa,#1 ' A3 + rol phsa,#1 ' A2 + rol phsa,#1 ' A1 + rol phsa,#1 ' A0 + rol phsa,#17 ' B7 + rol phsa,#1 ' B6 + rol phsa,#1 ' B5 + rol phsa,#1 ' B4 + rol phsa,#1 ' B3 + rol phsa,#1 ' B2 + rol phsa,#1 ' B1 + rol phsa,#1 ' B0 + rol phsa,#17 ' C7 + rol phsa,#1 ' C6 + rol phsa,#1 ' C5 + rol phsa,#1 ' C4 + rol phsa,#1 ' C3 + rol phsa,#1 ' C2 + rol phsa,#1 ' C1 + rol phsa,#1 ' C0 + rol phsa,#17 ' D7 + rol phsa,#1 ' D6 + rol phsa,#1 ' D5 + rol phsa,#1 ' D4 + rol phsa,#1 ' D3 + rol phsa,#1 ' D2 + rol phsa,#1 ' D1 + rol phsa,#1 ' D0 will be in place _after_ this instruction + mov frqb,#0 ' shuts the clock off, _after_ this instruction + djnz ops_left,#:write_loop + ' write out my two (bogus, using $FF) CRC bytes + call #in8 + call #in8 + ' now read response (I need this response, so can't spoof using out8) + call #in8 + and readback,#$1F + cmp readback,#5 wz +if_z mov user_cmd,#0 ' great +if_nz neg user_cmd,#ERR_ASM_BLOCK_NOT_WRITTEN ' oops + ' send out another 8 clocks + call #in8 + ' all done + mov idle_time,#0 +write_single_block_ret + ret + +' Select the SD card +_select jmp #c3_sel ' This jump is modified for normal CS mode +c3_sel mov cs_cnt, #5 + andn outa, maskCS + or outa, maskCS +_loop or outa, maskINC + andn outa, maskINC + djnz cs_cnt, #_loop + jmp #_select_ret +cs_sel andn outa, maskCS +_select_ret ret + +' Deselect the SD card +_deselect jmp #c3_des ' This jump is modified for normal CS mode +c3_des andn outa, maskCS +cs_des or outa, maskCS +_deselect_ret ret + +' jump instructions for the single CS mode +jmp_cs_sel jmp #cs_sel +jmp_cs_des jmp #cs_des +maskINC long 0 +cs_cnt long 0 + +' Unix time check +unix_time_chk mov ut_temp1, cnt + rdlong ut_temp2, a_cycle0 + sub ut_temp1, ut_temp2 + rdlong ut_temp3, #0 + cmps ut_temp1, ut_temp3 wz, wc + if_c jmp unix_time_chk_ret + rdlong ut_temp1, a_cycle0 + add ut_temp1, ut_temp3 + wrlong ut_temp1, a_cycle0 + rdlong ut_temp1, a_unixtime + add ut_temp1, #1 + wrlong ut_temp1, a_unixtime +unix_time_chk_ret ret +ut_temp1 long 0 +ut_temp2 long 0 +ut_temp3 long 0 +a_cycle0 long sys#cycle0 +a_unixtime long sys#unixtime + +{=== Assembly Interface Variables ===} +pinDO long 0 ' pin is controlled by a counter +pinCLK long 0 ' pin is controlled by a counter +pinDI long 0 ' pin is controlled by a counter +maskDO long 0 ' mask for reading the DO line from the card +maskDI long 0 ' mask for setting the pin high while reading +maskCS long 0 ' mask = (1< 2) + ser.dbprintf0(string("usage: sb file\n")) + c.exit(0) + pdata := c.malloc(1028) + ifnot pdata + ser.dbprintf0(string("Could not malloc 1028 bytes for the data buffer\n")) + c.exit(1) + fname := long[argv][1] + infile := c.fopen(fname, string("r")) + ifnot infile + ser.dbprintf1(string("Could not open %s\n"), fname) + c.free(pdata) + c.exit(1) + filesize := c.getfilesize(infile) + bytefill(pdata, 0, 130) + byte[pdata][1] := 255 + str := pdata + 2 + len := strsize(fname) + 1 + bytemove(str, fname, len) + str += len + itoa10(filesize, str) + { + c.sprintf2(pdata+2, string("%s %d"), fname, filesize) + len := strsize(fname) + byte[pdata][len+2] := 0 + } + repeat + val := ser.rxtime(30000) + if val == -1 or val == 27 + c.fclose(infile) + c.free(pdata) + c.exit(0) + if val == CEE + quit + + SendPacket(0, pdata, 128) + + repeat + val := ser.rxtime(10000) + if val == -1 or val == 27 + c.fclose(infile) + c.free(pdata) + c.exit(0) + if val == CEE + quit + + packetnum := 1 + repeat while filesize > 0 + len := filesize + if len > 1024 + len := 1024 + filesize -= len + c.fread(pdata + 2, 1, len, infile) + SendPacket(packetnum, pdata, len) + packetnum++ + + SendEOT + byte[pdata][2] := 0 + SendPacket(0, pdata, 128) + + c.fclose(infile) + c.free(pdata) + c.exit(0) + +PUB SendEOT | val + repeat + ser.tx(EOT) + repeat + val := ser.rxtime(10000) + if val == ACK + return 1 + elseif val == NAK + quit + +PUB SendPacket(packetnum, ptr, num) | i, val, firstchar + if (num =< 0) or (num > 1024) + return + elseif (num =< 128) + num := 128 + firstchar := SOH + else + num := 1024 + firstchar := STX + byte[ptr] := packetnum + byte[ptr][1] := 255 - packetnum + AppendCRC(ptr+2, num) + repeat + ser.tx(firstchar) + repeat i from 0 to num + 3 + ser.tx(byte[ptr][i]) + repeat + val := ser.rxtime(10000) + if val == ACK + return num + elseif val == NAK + quit + return num + +PUB AppendCRC(ptr, num) | crc + crc := ComputeCRC(ptr, num) + byte[ptr][num] := crc >> 8 + byte[ptr][num+1] := crc & $ff + +PUB ComputeCRC(ptr, num) + repeat num + result := ((result << 8) & $ffff) ^ fcstab[(result >> 8) ^ byte[ptr++]] + +PRI itoa10(number, str) | divisor, temp +{{ + This private routine is used to convert a signed integer contained in + "number" to a decimal character string. It is called by itoa when the + numeric base parameter has a value of 10. +}} + if (number == 0) + byte[str++] := "0" + byte[str] := 0 + return 1 + divisor := 1_000_000_000 + repeat while (divisor > number) + divisor /= 10 + repeat while (divisor > 0) + temp := number / divisor + byte[str++] := temp + "0" + number -= temp * divisor + divisor /= 10 + byte[str++] := 0 + +{{ ++-----------------------------------------------------------------------------+ +| TERMS OF USE: MIT License | ++-----------------------------------------------------------------------------+ +|Permission is hereby granted, free of charge, to any person obtaining a copy | +|of this software and associated documentation files (the "Software"), to deal| +|in the Software without restriction, including without limitation the rights | +|to use, copy, modify, merge, publish, distribute, sublicense, and/or sell | +|copies of the Software, and to permit persons to whom the Software is | +|furnished to do so, subject to the following conditions: | +| | +|The above copyright notice and this permission notice shall be included in | +|all copies or substantial portions of the Software. | +| | +|THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | +|IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | +|FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE | +|AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | +|LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,| +|OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE| +|SOFTWARE. | ++-----------------------------------------------------------------------------+ +}} diff --git a/src/set.spin b/src/set.spin new file mode 100644 index 0000000..aac9e8f --- /dev/null +++ b/src/set.spin @@ -0,0 +1,69 @@ +'****************************************************************************** +' Copyright (c) 2013 Dave Hein +' See end of file for terms of use. +'****************************************************************************** +CON + _clkmode = xinput + _xinfreq = $4e495053 ' SPIN + + DOUBLE_QUOTE = $22 + GLOBAL_FLAG = "G" + LOCAL_FLAG = "L" + +OBJ + c : "clibsd" + sys : "sysdefs" + +PUB main(argc, argv) | ptr + c.enter + + if argc <> 1 + c.exit(0) + + ptr := sys#environ_vars + repeat while byte[ptr] + ptr := PrintVar(ptr) + + c.exit(0) + +PUB PrintVar(ptr) | vartype, name, value, ptr1 + vartype := byte[ptr++] + name := ptr + ptr += strsize(ptr) + 1 + value := ptr + ptr += strsize(ptr) + 1 + if vartype == GLOBAL_FLAG or vartype == LOCAL_FLAG + if ContainsBlank(value) + c.printf2(string("%s=", DOUBLE_QUOTE, "%s", DOUBLE_QUOTE, "\n"), name, value) + else + c.printf2(string("%s=%s\n"), name, value) + return ptr + +PUB ContainsBlank(ptr) + repeat while byte[ptr] + if byte[ptr++] == " " + return 1 + +{{ ++-----------------------------------------------------------------------------+ +| TERMS OF USE: MIT License | ++-----------------------------------------------------------------------------+ +|Permission is hereby granted, free of charge, to any person obtaining a copy | +|of this software and associated documentation files (the "Software"), to deal| +|in the Software without restriction, including without limitation the rights | +|to use, copy, modify, merge, publish, distribute, sublicense, and/or sell | +|copies of the Software, and to permit persons to whom the Software is | +|furnished to do so, subject to the following conditions: | +| | +|The above copyright notice and this permission notice shall be included in | +|all copies or substantial portions of the Software. | +| | +|THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | +|IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | +|FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE | +|AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | +|LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,| +|OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE| +|SOFTWARE. | ++-----------------------------------------------------------------------------+ +}} diff --git a/src/setret.spin b/src/setret.spin new file mode 100644 index 0000000..ffdcae0 --- /dev/null +++ b/src/setret.spin @@ -0,0 +1,43 @@ +'****************************************************************************** +' Copyright (c) 2011, 2012, Dave Hein +' See end of file for terms of use. +'****************************************************************************** +CON + _clkmode = xinput + _xinfreq = $4e495053 ' SPIN + +OBJ + c : "clibsd" + +PUB main(argc, argv) + c.enter + if argc < 2 + c.exit(1) + c.sscanf1(long[argv][1], string("%d"), @result) + if argc == 2 + c.exit(result) + c.exit(0) + +{{ ++-----------------------------------------------------------------------------+ +| TERMS OF USE: MIT License | ++-----------------------------------------------------------------------------+ +|Permission is hereby granted, free of charge, to any person obtaining a copy | +|of this software and associated documentation files (the "Software"), to deal| +|in the Software without restriction, including without limitation the rights | +|to use, copy, modify, merge, publish, distribute, sublicense, and/or sell | +|copies of the Software, and to permit persons to whom the Software is | +|furnished to do so, subject to the following conditions: | +| | +|The above copyright notice and this permission notice shall be included in | +|all copies or substantial portions of the Software. | +| | +|THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | +|IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | +|FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE | +|AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | +|LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,| +|OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE| +|SOFTWARE. | ++-----------------------------------------------------------------------------+ +}} diff --git a/src/shell.spin b/src/shell.spin new file mode 100644 index 0000000..9fa2147 --- /dev/null +++ b/src/shell.spin @@ -0,0 +1,650 @@ +CON + _clkmode = xinput + _xinfreq = $4e495053 ' SPIN + + DOUBLE_QUOTE = $22 + +OBJ + c : "clibsd" + g : "glob" + sys : "sysdefs" + vs : "varsubs" + +DAT + nullstr long 0 + redirectin long 0 + redirectout long 0 + scriptline long 0 + ifflag long 0 + whileflag long 0 + shell_level long 0 + bootflag long 0 + scriptfile long 0 + +VAR + byte buffer[100] + byte buffer2[100] + byte strbuf[100] + long tokens[25] + long numtokens + +PUB main(argc, argv) | ptr + vs.RemoveVar(string("<")) + vs.RemoveVar(string(">")) + vs.RemoveVar(string(">>")) + c.enter + 'c.printf1(string("$? = %d\n"), long[sys#return_value]) + LoadStateFromRam + repeat + c.CloseRedirect + if ReadLine + next + 'c.printf1(string("Trace %d\n"), 1) + CheckBang + 'c.printf1(string("Trace %d\n"), 2) + ifnot scriptfile + WriteHistory + 'c.printf1(string("Trace %d\n"), 3) + CheckAlias + 'c.printf1(string("Trace %d\n"), 4) + numtokens := tokenize(@buffer, @tokens) + 'c.printf1(string("Trace %d\n"), 5) + expand(numtokens) + 'c.printf1(string("Trace %d\n"), 6) + numtokens := Redirection(numtokens, @tokens) + 'c.printf1(string("Trace %d\n"), 7) + if scriptfile + if CheckScriptCommand + next + ifnot numtokens + next + 'c.printf1(string("Trace %d\n"), 8) + ptr := FindChar(tokens[0], "=") + if numtokens == 1 and byte[ptr] + byte[ptr++] := 0 + vs.SaveVar(tokens[0], ptr, vs#LOCAL_FLAG) + elseif strcomp(tokens, string("pwd")) + PrintWD(numtokens, @tokens) + elseif strcomp(tokens, string("cd")) + ChangeDirectory(numtokens, @tokens) + elseif strcomp(tokens, string("echo")) + Echo(numtokens, @tokens) +{ + elseif strcomp(tokens, string("exit")) + reboot +} + else + Execute(numtokens, @tokens) + +PUB Execute(argc, argv) | target[20] + 'c.printf0(string("Execute\n")) + 'PrintArgv(argc, argv) + 'c.printf1(string("Trace %d\n"), 9) + result := FindExecTarget(long[argv], @target) + 'c.printf1(string("Trace %d\n"), 20) + if result => 2 and result =< 4 + long[argv] := @target + SaveStateToRam + if result == 4 + save_sysparm + c.run(argc, argv, result - 2) + c.printf1(string("Could not run %s\n"), long[argv]) + LoadStateFromRam + elseif result == 1 + long[argv] := @target + OpenScriptFile(argc, argv) + +PUB SkipChar(ptr, val) + repeat while byte[ptr] + if byte[ptr] <> val + quit + ptr++ + return ptr + +PUB FindChar(ptr, val) + repeat while byte[ptr] + if byte[ptr] == val + quit + ptr++ + return ptr + +PUB PrintWD(argc, argv) + if c.OpenRedirect '(argc, argv) + return 1 + c.getcwd(@buffer, 100) + c.printf1(string("%s\n"), @buffer) + +PUB ChangeDirectory(argc, argv) | path[25] + if c.OpenRedirect '(argc, argv) + return 1 + if argc > 2 + c.printf0(string("cd [path]\n")) + return 1 + if argc == 1 + c.strcpy(@path, string("/")) + else + c.resolve_path(long[argv][1], @path) + ifnot strcomp(@path, string("/")) + ifnot c.getmod(@path) & %10000 + c.printf1(string("%s is not a directory\n"), @path) + return 1 + if c.chdir(@path) + c.printf1(string("Could not find path %s\n"), @path) + return 2 + vs.SaveVar(string("PWD"), @path, vs#GLOBAL_FLAG) + +PUB Echo(argc, argv) | i, ptr, space, globdata, str[25] + if c.OpenRedirect '(argc, argv) + return 1 + if argc > 1 + space := 0 + repeat i from 1 to argc - 1 + globdata := 0 + repeat while (ptr := g.glob(long[argv][i], @globdata, @str)) + c.printf2(string("%s%s"), @space, ptr) + space := " " + c.printf0(string("\n")) + +PUB FindExecTarget(fname, pathstr) | temp, infile, typestr[8] + 'c.printf1(string("FindExecTarget %s\n"), fname) + c.strcpy(pathstr, fname) + 'c.printf1(string("pathstr = %s\n"), pathstr) + 'c.CheckStackSpace + 'c.CheckMallocSpace + 'c.printf1(string("Trace %d\n"), 10) + repeat 2 + 'c.printf1(string("Trace %d\n"), 11) + 'c.printf0(string("repeat\n")) + if (infile := c.fopen(pathstr, string("r"))) + 'c.printf1(string("infile nonzero -- %d\n"), infile) + quit + 'c.printf1(string("infile zero -- %d\n"), infile) + temp := FindChar(pathstr, "/") + if byte[temp] + quit + c.strcpy(pathstr, string("/bin/")) + c.strcat(pathstr, fname) + + 'c.printf1(string("infile equals %d\n"), infile) + 'c.printf1(string("Trace %d\n"), 12) + + ifnot infile + c.printf1(string("1Could not open %s\n"), fname) + return + + longfill(@typestr, 0, 8) + c.fread(@typestr, 1, 32, infile) + c.fclose(infile) + + 'c.printf1(string("Trace %d\n"), 13) + ifnot c.strncmp(@typestr, string("SPINb"), 5) + result := 2 + elseifnot c.strncmp(@typestr, string("CAPPb"), 5) + result := 3 + elseif typestr => 80_000_000 and typestr =< 120_000_000 ' Stand-alone + result := 4 + elseifnot c.strncmp(@typestr, string("#shell"), 6) + 'c.printf1(string("%s is a shell script\n"), fname) + result := 1 + else + c.printf1(string("%s is not an executable file\n"), fname) + +PUB Redirection(argc, argv) | i, argc1, ptr, char, outptr, inptr, outstr + if argc =< 0 + return argc + i := 1 + argc1 := 1 + inptr := @nullstr + outptr := @nullstr + repeat while i < argc + ptr := long[argv][i] + char := byte[ptr++] + if char == "<" + if byte[ptr] + inptr := ptr + elseif ++i < argc + inptr := long[argv][i] + vs.SaveVar(string("<"), inptr, vs#LOCAL_FLAG) + elseif char == ">" + if byte[ptr] == ">" + ptr++ + outstr := STRING(">>") + else + outstr := STRING(">") + if byte[ptr] + outptr := ptr + elseif ++i < argc + outptr := long[argv][i] + vs.SaveVar(outstr, outptr, vs#LOCAL_FLAG) + elseif char + long[argv][argc1++] := long[argv][i] + i++ + 'long[argv][argc1] := inptr + 'long[argv][argc1+1] := outptr + return argc1 + +{ +pub save_sysparm | outfile, ptr + outfile := c.fopen(string("/_sysparm"), string("w")) + ptr := sys#unixtime + repeat 3 + c.fprintf1(outfile, string("%d\n"), long[ptr]) + ptr += 4 + c.fclose(outfile) +} + +pub WriteHistory | outfile, ptr + ptr := SkipChar(@buffer, " ") + if byte[ptr] + if (outfile := c.fopen(string("/_history"), string("a"))) + c.fprintf1(outfile, string("%s\n"), @buffer) + c.fclose(outfile) + +PUB RemoveCRLF(str) | len + len := strsize(str) + str += len - 1 + repeat while (len > 0) + if (byte[str] <> 10 and byte[str] <> 13) + quit + byte[str--] := 0 + len-- + +PUB FindBlank(str) | val + val := " " + repeat while byte[str] + if byte[str] == val + if val == " " + quit + val := " " + elseif byte[str] == DOUBLE_QUOTE + val := DOUBLE_QUOTE + str++ + return str + +PUB tokenize(str, tokens_0) | ptr, num + num := 0 + repeat while (byte[str]) + ptr := SkipChar(str, " ") + if (byte[ptr] == 0) + quit + str := FindBlank(ptr) + if (byte[str]) + byte[str++] := 0 + long[tokens_0][num++] := ptr + return num + +PUB GetVarName(ptr1, ptr2) | brace, val + if byte[ptr1] == "#" or byte[ptr1] == "?" + byte[ptr2++] := byte[ptr1++] + byte[ptr2] := 0 + return ptr1 + brace := 0 + if byte[ptr1] == "{" + brace := 1 + ptr1++ + repeat while (val := byte[ptr1]) + ifnot (c.isdigit(val) or vs.IsAlpha(val) or val == "_") + if val == "}" and brace + ptr1++ + quit + byte[ptr2++] := byte[ptr1++] + byte[ptr2] := 0 + return ptr1 + +PUB expand(num) | i, ptr1, ptr2, ptr3, len, varname[20] + i := 0 + ptr2 := @buffer2 + repeat while i < num + ptr1 := tokens[i] + tokens[i] := ptr2 + repeat while byte[ptr1] + if byte[ptr1] == DOUBLE_QUOTE + ptr1++ + next + if byte[ptr1] == "$" + ptr1 := GetVarName(ptr1 + 1, @varname) + ptr3 := vs.GetVarVal(@varname) + len := strsize(ptr3) + bytemove(ptr2, ptr3, len) + ptr2 += len + next + if byte[ptr1] == "\" + ifnot byte[++ptr1] + quit + 'c.printf1(string("Copy %c\n"), byte[ptr1]) + byte[ptr2++] := byte[ptr1++] + byte[ptr2++] := 0 + i++ + +PUB CheckBang | num, i, histfile, ptr + ptr := SkipChar(@buffer, " ") + ifnot byte[ptr] + return + if buffer[0] == "!" + c.strcpy(@strbuf, @buffer + 1) + if buffer[1] == "!" + num := -1 + elseif c.isdigit(buffer[1]) + c.sscanf1(@buffer[1], string("%d"), @num) + else + i := 0 + num := 0 + histfile := c.fopen(string("/_history"), string("r")) + if histfile + repeat while c.fgets(@buffer, 100, histfile) + i++ + ifnot c.strncmp(@buffer, @strbuf, strsize(@strbuf)) + num := i + c.fclose(histfile) + ifnot num + c.printf1(string("Could not find event !%s\n"), @strbuf) + return 1 + + histfile := c.fopen(string("/_history"), string("r")) + i := 0 + if histfile + repeat while c.fgets(@buffer, 100, histfile) + if ++i == num + quit + c.fclose(histfile) + + if i and ((num == -1) or (num == i)) + c.printf1(string("%s"), @buffer) + num := strsize(@buffer) + if num + buffer[num-1] := 0 + else + c.printf1(string("Could not find event !%s\n"), @strbuf) + return 1 +{ + histfile := c.fopen(string("/_history"), string("a")) + if histfile + c.fprintf1(histfile, string("%s\n"), @buffer) + c.fclose(histfile) +} + +PUB CheckAlias | len, ptr, ptr1, ptr2, ptr3 + + ' Get the first token + ptr := SkipChar(@buffer, " ") + ifnot byte[ptr] + return + ptr1 := FindChar(ptr, " ") + len := ptr1 - ptr + bytemove(@strbuf, ptr, len) + strbuf[len] := 0 + + ptr2 := vs.FindAlias(@strbuf) + ifnot ptr2 + return + ptr3 := ptr2 + strsize(ptr2) + 1 + + 'c.printf2(string("CheckAlias: %s %s\n"), ptr2+1, ptr3) + + c.strcpy(@strbuf, ptr3) + c.strcat(@strbuf, ptr1) + c.strcpy(@buffer, @strbuf) + +PUB OpenScriptFile(argc, argv) | fname + fname := long[argv] + 'c.printf1(string("OpenScriptFile: Opening %s\n"), fname) + if scriptfile + c.fclose(scriptfile) + 'c.printf0(string("Closing script file\n")) + SaveStateToFile + if (scriptfile := c.fopen(fname, string("r"))) + scriptline := 0 + ifflag := 0 + whileflag := 0 + 'shell_level := 0 + bootflag := 0 + vs.SaveVar(string("SCRIPT_FILE"), fname, vs#LOCAL_FLAG) + AddParms(argc, argv) + else + c.printf0(string("Open failed\n")) + LoadStateFromFile + +PUB SaveStateToRam + 'c.printf1(string("SaveStateToRam: scriptline = %d\n"), scriptline) + if scriptfile + long[sys#scriptline] := scriptline + long[sys#ifflag] := ifflag + long[sys#whileflag] := whileflag + long[sys#shell_level] := shell_level + long[sys#bootflag] := bootflag + c.fclose(scriptfile) + +PUB LoadStateFromRam | scriptname + 'c.printf0(string("LoadStateFromRam\n")) + scriptname := GetScriptName + if byte[scriptname] + 'c.printf1(string("Opening script file %s\n"), scriptname) + scriptfile := c.fopen(scriptname, string("r")) + if scriptfile + scriptline := long[sys#scriptline] + ifflag := long[sys#ifflag] + whileflag := long[sys#whileflag] + shell_level := long[sys#shell_level] + bootflag := long[sys#bootflag] + 'c.printf1(string("LoadStateFromRam: scriptline = %d\n"), scriptline) + JumpScriptLine + +PUB JumpScriptLine + c.fseek(scriptfile, 0, c#SEEK_SET) + if scriptline > 0 + repeat scriptline + c.fgets(@buffer, 100, scriptfile) + +PUB CheckScriptCommand + 'c.printf0(string("CheckScriptCommand\n")) + + ifnot scriptfile + return + + if (whileflag == - 1) + if (not c.strcmp(tokens[0], string("done"))) + whileflag := 0 + return 1 + if (not c.strcmp(tokens[0], string("else"))) + ifflag := not ifflag + return 1 + elseif (not c.strcmp(tokens[0], string("fi"))) + ifflag := 0 + return 1 + elseif (ifflag) + return 1 + + if (not c.strcmp(tokens[0], string("exit"))) + c.fclose(scriptfile) + LoadStateFromFile + return 1 + elseif (not c.strcmp(tokens[0], string("setlev"))) + shell_level := vs.atol(tokens[1]) + return 1 + elseif (not c.strcmp(tokens[0], string("if"))) + numtokens := CheckTest(numtokens, @tokens) + Execute(numtokens-1, @tokens[1]) + return 1 + elseif (not c.strcmp(tokens[0], string("then"))) + ifflag := long[sys#return_value] + return 1 + elseif (not c.strcmp(tokens[0], string("while"))) + numtokens := CheckTest(numtokens, @tokens) + Execute(numtokens-1, @tokens[1]) + return 1 + elseif (not c.strcmp(tokens[0], string("do"))) + if long[sys#return_value] + whileflag := - 1 + else + whileflag := scriptline - 2 + return 1 + elseif (not c.strcmp(tokens[0], string("done"))) + if whileflag + scriptline := whileflag + whileflag := 0 + JumpScriptLine + return 1 + +PUB PrintArgv(argc, argv) | i + c.printf1(string("PrintArgv %d --"), argc) + i := 0 + repeat while i < argc + c.printf1(string(" <%s>"), long[argv][i++]) + c.printf0(string("\n")) + +PUB CheckTest(argc, argv) + 'c.printf0(string("CheckTest\n")) + 'PrintArgv(argc, argv) + if argc > 1 + if strcomp(long[argv][1], string("[")) + long[argv][1] := string("/bin/test") + if argc > 2 + if strcomp(long[argv][argc-1], string("]")) + argc-- + 'PrintArgv(argc, argv) + 'long[argv][argc] := @nullstr + 'long[argv][argc+1] := @nullstr + return argc + +PUB ReadLine | ptr + if scriptfile + if c.fgets(@buffer, 100, scriptfile) =< 0 + c.fclose(scriptfile) + LoadStateFromFile + return 1 + RemoveCRLF(@buffer) + scriptline++ + else + c.printf0(string("shell> ")) + c.gets(@buffer) + ptr := SkipChar(@buffer, " ") + ifnot byte[ptr] + return 1 + if byte[@buffer][0] == "#" + return 1 + +PUB AddParms(argc, argv) | i, numstr[3] + i := 1 + c.sprintf1(@numstr, string("%d"), argc - 1) + vs.SaveVar(string("#"), @numstr, vs#PARM_FLAG) + repeat while (i < argc) + c.sprintf1(@numstr, string("%d"), i) + vs.SaveVar(@numstr, long[argv][i], vs#PARM_FLAG) + i++ + +PUB GetScriptName + if (result := vs.FindVar(string("SCRIPT_FILE"))) + result += strsize(result) + 1 + else + result := @nullstr + +PUB SaveStateToFile | outfile, fname[5] + c.strcpy(@fname, string("/tmp/_shellva.rsa")) + byte[@fname][16] += shell_level + outfile := c.fopen(@fname, string("w")) + ifnot outfile + c.printf0(string("SaveState: Could not open file\n")) + return + c.fprintf1(outfile, string("%s\n"), GetScriptName) + SaveScriptStuff(outfile) +{ + c.fprintf3(outfile, string("%d %d %d\n"), ifflag, whileflag, scriptline) + ptr := sys#environ_vars + repeat while byte[ptr] + ptr1 := ptr + ptr += strsize(ptr) + 1 + c.fprintf2(outfile, string("%s %s\n"), ptr1, ptr) + ptr += strsize(ptr) + 1 +} + RemoveLocalVars + shell_level++ + c.fclose(outfile) + +PUB save_sysparm | outfile + outfile := c.fopen(string("/_sysparm"), string("w")) + ifnot outfile + c.printf0(string("Can't open /_sysparm\n")) + return + c.fprintf1(outfile, string("%d\n"), long[sys#unixtime]) + c.fprintf3(outfile, string("%d %d %d\n"), long[sys#timezone], long[sys#config], shell_level) + SaveScriptStuff(outfile) + c.fclose(outfile) + +PUB SaveScriptStuff(outfile) | ptr, ptr1 + c.fprintf3(outfile, string("%d %d %d\n"), ifflag, whileflag, scriptline) + ptr := sys#environ_vars + repeat while byte[ptr] + ptr1 := ptr + ptr += strsize(ptr) + 1 + c.fprintf2(outfile, string("%s %s\n"), ptr1, ptr) + ptr += strsize(ptr) + 1 + +PUB RemoveLocalVars | ptr + ptr := sys#environ_vars + repeat while byte[ptr] + if byte[ptr] == VS#GLOBAL_FLAG + ptr += strsize(ptr) + 1 + ptr += strsize(ptr) + 1 + else + vs.RemoveEntry(ptr) + +PUB InitializeState(error_flag) + shell_level := 0 + scriptfile := 0 + if error_flag + RemoveLocalVars + else + vs.RemoveVar(string("SCRIPT_FILE")) + vs.SaveVar(string("#"), string("0"), vs#PARM_FLAG) + +PUB LoadStateFromFile | infile, ptr, fname[5], scriptname, firstline[20] + 'c.printf1(string("LoadStateFromFile: shell_level = %d\n"), shell_level) + ' Check if shell level less than or equal to zero -- zero happens at startup + if shell_level =< 0 + if shell_level + c.printf1(string("LoadStateFromFile: Error, shell_level = %d\n"), shell_level) + InitializeState(1) + else + InitializeState(0) + return + + ' Drop down a level + shell_level-- + c.strcpy(@fname, string("/tmp/_shellva.rsa")) + byte[@fname][16] += shell_level + ifnot (infile := c.fopen(@fname, string("r"))) + c.printf1(string("LoadStateFromFile: Error, could not open %s\n"), @fname) + InitializeState(1) + return + 'c.printf1(string("LoadStateFromFile: opened %s\n"), @fname) + c.fgets(@buffer, 100, infile) + byte[@buffer][strsize(@buffer)-1] := 0 + c.strcpy(@firstline, @buffer) + c.fgets(@buffer, 100, infile) + c.sscanf3(@buffer, string("%d %d %d"), @ifflag, @whileflag, @scriptline) + 'c.printf4(string("LoadStateFromFile: %s %d %d %d\n"), @firstline, ifflag, whileflag, scriptline) + ptr := sys#environ_vars + repeat while c.fgets(ptr, 100, infile) + ptr := vs.FindChar(ptr, " ") + byte[ptr++] := 0 + ptr += strsize(ptr) - 1 + byte[ptr++] := 0 + byte[ptr] := 0 + c.fclose(infile) + + ' Open script file + scriptname := GetScriptName + ifnot strcomp(scriptname, @firstline) + c.printf0(string("LoadStateFromFile: Error, names don't match")) + c.printf2(string("LoadStateFromFile: %s %s\n"), @firstline, scriptname) + if shell_level == 0 and not byte[scriptname] + scriptfile := 0 + elseif shell_level and not byte[scriptname] + c.printf1(string("LoadStateFromFile: Error, no script file at level %d\n"), shell_level) + InitializeState(1) + elseif (scriptfile := c.fopen(scriptname, string("r"))) + JumpScriptLine + else + c.printf1(string("LoadStateFromFile: Error, could not open %s\n"), scriptname) + InitializeState(1) + diff --git a/src/smalloc.spin b/src/smalloc.spin new file mode 100644 index 0000000..800f4c5 --- /dev/null +++ b/src/smalloc.spin @@ -0,0 +1,117 @@ +'********************************************** +'* Spin code generated by cspin version 0.060 * +'********************************************** +'****************************************************************************** +' Copyright (c) 2011, 2012, Dave Hein +' See end of file for terms of use. +'****************************************************************************** + +CON + HDR_SIZE = 4 + + ' typedef struct StringS + s_next = 0 + s_size = 1 + +DAT + smfreelist long 0 + +PUB smallocinit(curr, size) + smfreelist := curr + word[curr]{s_next} := 0 + word[curr][s_size] := size + + +' Allocate a block of "size" bytes. Return a pointer to the block if +' successful, or zero if a large enough memory block could not be found +PUB smalloc(size) | prev, curr, nexx + if (size =< 0) + return 0 + ' Adjust size to nearest long plus the header size + size := ((size + 3) & (!3)) + HDR_SIZE + ' Search for a block of memory + prev := 0 + curr := smfreelist + repeat while (curr) + if (word[curr][s_size] => size) + quit + prev := curr + curr := word[curr]{s_next} + ' Return null if block not found + if (curr == 0) + return 0 + ' Get next pointer + nexx := word[curr]{s_next} + ' Split block if larger than needed + if (word[curr][s_size] => size + HDR_SIZE + 16) + nexx := curr + size + word[nexx]{s_next} := word[curr]{s_next} + word[nexx][s_size] := word[curr][s_size] - size + word[curr]{s_next} := nexx + word[curr][s_size] := size + ' Remove block from the free list + word[curr]{s_next} := 0 + if (prev) + word[prev]{s_next} := nexx + else + smfreelist := nexx + return curr + +' Insert a memory block back into the free list. Merge blocks together if +' the memory block is contiguous with other blocks on the list. +PUB sfree(curr) | prev, nexx + prev := 0 + nexx := smfreelist + ' Find Insertion Point + repeat while (nexx) + if (curr => prev and curr =< nexx) + quit + prev := nexx + nexx := word[nexx]{s_next} + ' Merge with the previous block if contiguous + if (prev and prev + word[prev][s_size] == curr) + word[prev][s_size] += word[curr][s_size] + ' Also merge with next block if contiguous + if (prev + word[prev][s_size] == nexx) + word[prev][s_size] += word[nexx][s_size] + word[prev]{s_next} := word[nexx]{s_next} + ' Merge with the next block if contiguous + elseif (nexx and curr + word[curr][s_size] == nexx) + word[curr][s_size] += word[nexx][s_size] + word[curr]{s_next} := word[nexx]{s_next} + if (prev) + word[prev]{s_next} := curr + else + smfreelist := curr + ' Insert in the middle of the free list if not contiguous + elseif (prev) + word[prev]{s_next} := curr + word[curr]{s_next} := nexx + ' Otherwise, insert at beginning of the free list + else + smfreelist := curr + word[curr]{s_next} := nexx + +{{ ++-----------------------------------------------------------------------------+ +| TERMS OF USE: MIT License | ++-----------------------------------------------------------------------------+ +|Permission is hereby granted, free of charge, to any person obtaining a copy | +|of this software and associated documentation files (the "Software"), to deal| +|in the Software without restriction, including without limitation the rights | +|to use, copy, modify, merge, publish, distribute, sublicense, and/or sell | +|copies of the Software, and to permit persons to whom the Software is | +|furnished to do so, subject to the following conditions: | +| | +|The above copyright notice and this permission notice shall be included in | +|all copies or substantial portions of the Software. | +| | +|THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | +|IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | +|FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE | +|AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | +|LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,| +|OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE| +|SOFTWARE. | ++-----------------------------------------------------------------------------+ +}} diff --git a/src/spasm.spin b/src/spasm.spin new file mode 100644 index 0000000..dca93e2 --- /dev/null +++ b/src/spasm.spin @@ -0,0 +1,967 @@ +'********************************************** +'* Spin code generated by cspin version 0.68 * +'********************************************** + + +CON + _clkmode = xinput + _xinfreq = $4e495053 ' SPIN + +OBJ + c : "clibsd" + strsub : "strsubs" + +' Special Characters + +CON + DOUBLE_QUOTE = $22 + SINGLE_QUOTE = $27 + TAB_CHAR = 9 + +DAT + pins byte 0, 0, 0, 0 + currentwd long 0 + locaddr1 long 0 + +PUB start(argc, argv) + c.enter + strsub.start + main(argc, argv) + waitcnt(clkfreq/10+cnt) + c.exit(0) + +CON + TYPE_GLOBAL = 1 + TYPE_LOCAL = 2 + TYPE_CONSTANT = 3 + TYPE_DAT = 4 + TYPE_VAR = 5 + TYPE_OBJECT = 6 + TYPE_PUB = 7 + TAB = 9 + OBJECT_OFFSET = 16 + OP_NONE = 0 + OP_UNSIGNED_OBJ_OFFSET = 1 + OP_UNSIGNED_VAR_OFFSET = 2 + OP_UNSIGNED_LOC_OFFSET = 3 + OP_DATA = 4 + OP_SIGNED_JMP_OFFSET = 5 + OP_MEMORY_OPCODE_WRITE = 6 + OP_OBJ_CALL_PAIR = 7 + OP_SIGNED_OFFSET = 8 + OP_PACKED_LITERAL = 9 + OP_BYTE_LITERAL = 10 + OP_WORD_LITERAL = 11 + OP_NEAR_LONG_LITERAL = 12 + OP_LONG_LITERAL = 13 + OP_MEMORY_OPCODE = 14 + OP_MEMORY_OPCODE_READ = 15 + OP_COMPACT_VAR_OFFSET = 16 + OP_COMPACT_LOC_OFFSET = 17 + MAX_TOKENS = 40 + MAX_LINE_SIZE = 100 + MAX_SYMBOLS = 140 + SYM_BUF_SIZE = MAX_SYMBOLS * 9 + MAX_PUBS = 40 + +DAT + checksum long 0 + pc long 0 + pc0 long 0 + +VAR + long infile + long outfile + +DAT + listflag long 0 + verbose long 1 + numsym long 0 + linenum long 0 + +VAR + long bigptr + +DAT + zero_char byte 0 + numobj long 0 + objnum long 0 + +CON +' typedef struct symbolS +name = 0 +value = 4 +type = 8 + +VAR + byte ops[3000] + byte bigbuf[SYM_BUF_SIZE] + long symbol[420] + byte buffer[MAX_LINE_SIZE] + byte buffer1[MAX_LINE_SIZE] + byte buffer2[MAX_LINE_SIZE + MAX_TOKENS] + word header[MAX_PUBS * 2 + 10] +' Memory access pseudo-ops + +DAT + datstr byte "ldb", 0, "stb", 0, "exb", 0, "lab", 0 + byte "ldbx", 0, "stbx", 0, "exbx", 0, "labx", 0, "ldw", 0 + byte "stw", 0, "exw", 0, "law", 0, "ldwx", 0, "stwx", 0 + byte "exwx", 0, "lawx", 0, "ldl", 0, "stl", 0, "exl", 0 + byte "lal", 0, "ldlx", 0, "stlx", 0, "exlx", 0, "lalx", 0 + pseudo_ops long @datstr + 16, @datstr + 20, @datstr + 24 + long @datstr + 28, @datstr + 32, @datstr + 37, @datstr + 42 + long @datstr + 47, @datstr + 52, @datstr + 56, @datstr + 60 + long @datstr + 64, @datstr + 68, @datstr + 73, @datstr + 78 + long @datstr + 83, @datstr + 88, @datstr + 92, @datstr + 96 + long @datstr + 100, @datstr + 104, @datstr + 109, @datstr + 114 + long @datstr + 119, 0 + +PUB ReadOpsFile | ptr, i + ptr := @ops + i := 0 + infile := c.fopen(string("/bin/opsfile.txt"), string("r")) + if (not infile) + c.printf0(string("Could not open /bin/opsfile.txt\n")) + c.exit(1) + repeat while (c.fgets(ptr, 100, infile)) + strsub.RemoveCRLF(ptr) + 'printf("ops %d: %s\n", i, ptr); + ptr += strsize(ptr) + 1 + i++ + byte[ptr] := 0 + c.fclose(infile) + +PUB IsConstant(str) | j + j := byte[str] + if (j == "$" or j == DOUBLE_QUOTE or j == "%") + return 1 + if (j == "-" or(j => "0" and j =< "9")) + return 1 + if ((j := FindSymbol(str)) => 0) + j := long[@symbol +(12 * j) + type] + if (j == TYPE_GLOBAL or j == TYPE_CONSTANT) + return 1 + return 0 + +PUB GetDecVal(str) | signflag, val, retval + retval := 0 + if (byte[str] == "-") + signflag := 1 + str++ + else + signflag := 0 + repeat while (val := byte[str++]) + if (val == "_") + next + if (val => "0" and val =< "9") + retval :=(retval * 10) + val - "0" + else + c.printf0(string("Invalid number\n")) + ErrorExit + if (signflag) + retval := - retval + return retval + +PUB GetHexVal(str) | val, retval + retval := 0 + repeat while (val := byte[str++]) + if (val == "_") + next + if (val => "0" and val =< "9") + val -= "0" + elseif (val => "a" and val =< "f") + val -= "a" - 10 + elseif (val => "A" and val =< "F") + val -= "A" - 10 + else + c.printf0(string("Invalid number\n")) + ErrorExit + retval :=(retval << 4) + val + return retval + +PUB GetBinVal(str) | val, retval + retval := 0 + repeat while (val := byte[str++]) + if (val == "_") + next + if ((val & $FE) == "0") + retval :=(retval << 1) |(val & 1) + else + c.printf0(string("Invalid number\n")) + ErrorExit + return retval + +PUB GetVal(str, pass) | val + if (byte[str] == "$") + val := GetHexVal(str + 1) + elseif (byte[str] == DOUBLE_QUOTE) + val := byte[str][1] + elseif (byte[str] == "%") + val := GetBinVal(str + 1) + elseif (byte[str] == "-" or(byte[str] => "0" and byte[str] =< "9")) + val := GetDecVal(str) + else + val := GetSymbolValue(str, pass) + return val + +PUB WriteByte(value_0, pass) + if (pass <> 2) + return + if (listflag) + c.printf1(string(" %02x"), value_0 & 255) + c.fwrite(@value_0, 1, 1, outfile) + checksum :=(checksum + value_0) & 255 + +PUB GetOpsFormat(str) | j + j := byte[str][1] + if (j == " ") + j := 0 + else + j -= "@" + if (byte[str][0] <> " ") + j |=(byte[str][0] - "0") << 5 + return j + +PUB FindOp(str, ops_format, ops_code, pass) | i, ops_str + i := 0 + ops_str := @ops + repeat while (byte[ops_str]) + if (c.strcmp(ops_str + 6, str) == 0) + quit + i++ + ops_str += strsize(ops_str) + 1 + if (not(byte[ops_str])) + if (pass == 2) + c.printf1(string("ERROR: Invalid opcode %s\n"), @buffer1) + else + long[ops_format] := GetOpsFormat(ops_str) + c.sscanf1(ops_str + 2, string("%x"), ops_code) + return ops_str + +PUB FindOpcode(ops_code, ops_format) | temp, ops_str + ops_str := @ops + if (ops_code => $40 and ops_code < $80) + ops_code &= $e3 + repeat while (byte[ops_str]) + c.sscanf1(ops_str + 2, string("%x"), @temp) + if (ops_code == temp) + quit + ops_str += strsize(ops_str) + 1 + if (byte[ops_str]) + long[ops_format] := GetOpsFormat(ops_str) + return ops_str + +PUB ProcessEffect(tokens, tokenum, pass, num) | ops_str, ops_format, ops_code + ops_str := FindOp(long[tokens][tokenum], @ops_format, @ops_code, pass) + if (not(byte[ops_str])) + return 0 + if (ops_code => $e0) + ops_code :=(ops_code & $1f) + $40 + elseif ((ops_format >> 5) <> 3) + if (pass == 2) + c.printf1(string("ERROR: Invalid opcode %s\n"), @buffer1) + return 0 + if (c.strcmp(long[tokens][tokenum + 1], string("load")) == 0) + ops_code |= $80 + WriteByte(ops_code, pass) + return num + 1 + +PUB ProcessDat(tokens, numtokens, pass) | ptr, i, j, val, num, mode, listflag1 + listflag1 := listflag and pass == 2 + ' Look for byte, word or long keyword + if (strsub.StrCompNoCase(long[tokens][0], string("byte"))) + num := 1 + elseif (strsub.StrCompNoCase(long[tokens][0], string("word"))) + num := 2 + elseif (strsub.StrCompNoCase(long[tokens][0], string("long"))) + num := 4 + else + return + if (listflag1) + c.printf1(string("%04x "), pc + OBJECT_OFFSET) + ' Add zero bytes for alignment + if (num == 2) + if (pc & 1) + WriteByte(0, pass) + pc++ + if (listflag1) + c.printf1(string("\n%04x "), pc + OBJECT_OFFSET) + elseif (num == 4) + if (pc & 3) + repeat while (pc & 3) + WriteByte(0, pass) + pc++ + if (listflag1) + c.printf1(string("\n%04x "), pc + OBJECT_OFFSET) + ' Generate the output bytes + j := 0 + mode := 0 + repeat while (1) + if (mode == 0) + j++ + if (j => 10) + quit + if (byte[long[tokens][j]][0] == 0) + quit + if (byte[long[tokens][j]][0] == ",") + next + if (byte[long[tokens][j]][0] == DOUBLE_QUOTE) + ptr := @byte[long[tokens][j]][1] + mode := 1 + else + val := GetVal(long[tokens][j], pass) + if (mode == 1) + if (byte[ptr] == 0 or byte[ptr] == DOUBLE_QUOTE) + mode := 0 + next + else + val := byte[ptr++] + if (num == 1) + WriteByte(val & 255, pass) + elseif (num == 2) + WriteByte(val & 255, pass) + WriteByte((val >> 8) & 255, pass) + else + WriteByte(val & 255, pass) + WriteByte((val >> 8) & 255, pass) + WriteByte((val >> 16) & 255, pass) + WriteByte((val >> 24) & 255, pass) + pc += num + if (listflag1) + i := num + repeat while (i < 6) + c.printf0(string(" ")) + i++ + c.printf1(string(" %s\n"), @buffer1) + +PUB Process(tokens, numtokens, numpub, pass) | i, j, val, val2, num, listflag1, ptr, ops_code, ops_format, ops_str, tokenum, index + i := 0 + num := 0 + listflag1 := listflag and pass == 2 + tokenum := 0 + 'ops_str = ops; + 'printf("Process -%s- -%s-\n", tokens[0], tokens[1]); + ' Check for a pseudo-op + index := strsub.FindListNoCase(long[tokens][0], @pseudo_ops) + if (index => 0) + if (IsConstant(long[tokens][1])) + val := GetVal(long[tokens][1], pass) + if (val == - 1) + ops_code := $34 + elseif (val == 0) + ops_code := $35 + elseif (val == 1) + ops_code := $36 + elseif (val < 0 or val => $01000000) + ops_code := $3b + elseif (val => $00010000) + ops_code := $3a + elseif (val => $00000100) + ops_code := $39 + else + ops_code := $38 + elseif (byte[long[tokens][1]][0] == 0) + ops_code := $80 |((index & $1c) << 2) |(index & 3) + else + j := FindSymbol(long[tokens][1]) + if (j < 0) + c.printf1(string("Symbol %s is undefined\n"), long[tokens][1]) + ErrorExit + val := long[@symbol +(12 * j) + value] + if (long[@symbol +(12 * j) + type] == TYPE_LOCAL) + if (val < $20 and(index & $1c) == $10) + ops_code := $60 | val |(index & 3) + 'printf("..llc $%2.2x $%2.2x\n", val, ops_code); + else + ops_code := $8c |((index & $1c) << 2) |(index & 3) + elseif (long[@symbol +(12 * j) + type] == TYPE_DAT) + ops_code := $84 |((index & $1c) << 2) |(index & 3) + elseif (long[@symbol +(12 * j) + type] == TYPE_VAR) + if (val < $20 and(index & $1c) == $10) + ops_code := $40 | val |(index & 3) + else + ops_code := $88 |((index & $1c) << 2) |(index & 3) + else + c.printf0(string("Invalid type\n")) + ErrorExit + ops_str := FindOpcode(ops_code, @ops_format) + else + ops_str := FindOp(long[tokens][0], @ops_format, @ops_code, pass) + if (not(byte[ops_str])) + return + if ((ops_format & $1f) == OP_DATA) + ProcessDat(tokens, numtokens, pass) + return + if (listflag1) + c.printf1(string("%04x "), pc + OBJECT_OFFSET) + case(ops_format & $1f) + OP_NONE : + WriteByte(ops_code, pass) + num := 1 + OP_BYTE_LITERAL : + val := GetVal(long[tokens][1], pass) + WriteByte(ops_code, pass) + WriteByte(val & 255, pass) + num := 2 + OP_WORD_LITERAL : + val := GetVal(long[tokens][1], pass) + WriteByte(ops_code, pass) + WriteByte((val >> 8) & 255, pass) + WriteByte(val & 255, pass) + num := 3 + OP_NEAR_LONG_LITERAL : + val := GetVal(long[tokens][1], pass) + WriteByte(ops_code, pass) + WriteByte((val >> 16) & 255, pass) + WriteByte((val >> 8) & 255, pass) + WriteByte(val & 255, pass) + num := 4 + OP_LONG_LITERAL : + val := GetVal(long[tokens][1], pass) + WriteByte(ops_code, pass) + WriteByte((val >> 24) & 255, pass) + WriteByte((val >> 16) & 255, pass) + WriteByte((val >> 8) & 255, pass) + WriteByte(val & 255, pass) + num := 5 + OP_COMPACT_VAR_OFFSET, OP_COMPACT_LOC_OFFSET : + val := GetVal(long[tokens][1], pass) + WriteByte(ops_code |(val & $1c), pass) + num := 1 + tokenum++ + OP_SIGNED_JMP_OFFSET : + val := GetVal(long[tokens][1], pass) + val -= pc + 3 + WriteByte(ops_code, pass) + WriteByte(((val >> 8) & 255) | 128, pass) + WriteByte(val & 255, pass) + num := 3 + OP_SIGNED_OFFSET, OP_UNSIGNED_OBJ_OFFSET, OP_UNSIGNED_VAR_OFFSET, OP_UNSIGNED_LOC_OFFSET : + val := GetVal(long[tokens][1], pass) + 'val -= 16; + WriteByte(ops_code, pass) + WriteByte(((val >> 8) & 255) | 128, pass) + WriteByte(val & 255, pass) + num := 3 + tokenum++ + OP_OBJ_CALL_PAIR : + if (numtokens == 3) + val := GetVal(long[tokens][1], pass) + val2 := GetVal(long[tokens][2], pass) + else + c.strcpy(@buffer, long[tokens][1]) + ptr := strsub.FindChar(@buffer, ".") + byte[ptr] := 0 + val := GetVal(@buffer, pass) + numpub + val2 := GetVal(long[tokens][1], pass) + WriteByte(ops_code, pass) + WriteByte(val & 255, pass) + WriteByte(val2 & 255, pass) + num := 3 + OP_MEMORY_OPCODE_READ : + val := GetVal(long[tokens][1], pass) + WriteByte(ops_code, pass) + WriteByte($80 |(val & 31), pass) + num := 2 + OP_MEMORY_OPCODE_WRITE : + val := GetVal(long[tokens][1], pass) + WriteByte(ops_code, pass) + WriteByte($A0 |(val & 31), pass) + num := 2 + 'case OP_PACKED_LITERAL: + 'case OP_MEMORY_OPCODE: + OTHER : + if (pass == 2) + c.printf0(string("NOT IMPLEMENTED")) + if ((ops_format >> 5) == 1) + num := ProcessEffect(tokens, tokenum + 1, pass, num) + if (num == 0) + return + if (listflag1) + i := num + repeat while (i < 6) + c.printf0(string(" ")) + i++ + c.printf1(string(" %s\n"), @buffer1) + pc += num + +PUB FindSymbol(str) | i + i := 0 + repeat while (i < numsym) + if (long[@symbol +(12 * i) + type] == 0) + i++ + next + if (strsub.StrCompNoCase(long[@symbol +(12 * i) + name], str)) + quit + i++ + if (i < numsym) + return i + return - 1 + +PUB GetSymbolValue(str, pass) | i + i := 0 + repeat while (i < numsym) + if (long[@symbol +(12 * i) + type] == 0) + i++ + next + if (c.strcmp(long[@symbol +(12 * i) + name], str) == 0) + quit + i++ + if (i < numsym) + return long[@symbol +(12 * i) + value] + if (pass == 2) + c.printf1(string("%s is undefined\n"), str) + ErrorExit + return 0 + +PUB AddSymbol(str, value_0, type_0) | i + 'printf("AddSymbol: %s, %x, %d\n", str, value, type); + ' Re-use symbol if previously used + i := 0 + repeat while (i < numsym) + if (long[@symbol +(12 * i) + type] == 0) + if (not c.strcmp(str, long[@symbol +(12 * i) + name])) + quit + i++ + ' Otherwise, allocate new symbol entry + if (i == numsym) + if (numsym => MAX_SYMBOLS) + c.printf0(string("Too many symbols\n")) + ErrorExit + if (bigptr + strsize(str) + 1 > @bigbuf + SYM_BUF_SIZE) + c.printf0(string("Symbol buffer is full\n")) + ErrorExit + c.strcpy(bigptr, str) + 'c.printf1(string("Adding to position %d\n"), i) + long[@symbol +(12 * i) + name] := bigptr + bigptr += strsize(bigptr) + 1 + numsym++ + long[@symbol +(12 * i) + value] := value_0 + long[@symbol +(12 * i) + type] := type_0 + return i + +PUB RemoveLocalSymbols | i + i := 0 + repeat while (i < numsym) + if (long[@symbol +(12 * i) + type] == TYPE_LOCAL) + long[@symbol +(12 * i) + type] := 0 + i++ + +PUB PrintSymbolTable | i + c.printf0(string("\nSymbol Table\n")) + i := 0 + repeat while (i < numsym) + c.printf4(string("%s $%x (%d) %d\n"), long[@symbol +(12 * i) + name], long[@symbol +(12 * i) + value], long[@symbol +(12 * i) + value], long[@symbol +(12 * i) + type]) + i++ + +PUB ProcessConstants(tokens, numtokens, type_0) + AddSymbol(long[tokens][0], GetVal(long[tokens][2], 0), type_0) + +PUB usage + c.printf0(string("usage: spasm [-l] [-d] file\n")) + c.exit(1) + +PUB PrintHeader(pubnum, numobj_0) | i + checksum := 0 + pc :=(pc + 3 + 1) & $fffc + i := GetSymbolValue(string("_clkfreq"), 0) + if (verbose => 2) + c.printf1(string("PrintHeader: _clkfreq = %d\n"), i) + header[0] := i & $ffff + header[1] :=(i >> 16) & $ffff + header[2] := GetSymbolValue(string("_clkmode"), 0) ' sum-clk + header[3] := OBJECT_OFFSET ' object offset + header[4] := pc + OBJECT_OFFSET + header[5] := pc + 8 + OBJECT_OFFSET + header[6] := header[10] + OBJECT_OFFSET ' pc0; + header[7] := pc + 8 + OBJECT_OFFSET + locaddr1 + header[8] := pc + header[9] :=(numobj_0 << 8) | pubnum + if (verbose => 2) + c.printf1(string("PrintHeader: numobj = %d\n"), numobj_0) + i := 0 + repeat while (i < 8 +((pubnum + numobj_0) * 2)) + if (listflag and i == 8) + c.printf0(string("\n")) + if (listflag) + c.printf1(string("%04x "), i << 1) + WriteByte(header[i], 2) + WriteByte(header[i] >> 8, 2) + if (listflag) + c.printf0(string("\n")) + i++ + +PUB PrintTail | i, num + num := 3 + if (listflag) + c.printf1(string("\n%04x "), pc + OBJECT_OFFSET) + repeat while ((pc & 3) <> 3) + WriteByte(0, 2) + pc++ + num-- + WriteByte(($14 - checksum) & 255, 2) + if (listflag) + i := 0 + repeat while (i < num) + c.printf0(string(" ")) + i++ + c.printf0(string(" Trailing bytes\n")) + +PUB AddStandardSymbols + AddSymbol(string("codid"), $1e9, TYPE_CONSTANT) + AddSymbol(string("par"), $1f0, TYPE_CONSTANT) + AddSymbol(string("cnt"), $1f1, TYPE_CONSTANT) + AddSymbol(string("ina"), $1f2, TYPE_CONSTANT) + AddSymbol(string("inb"), $1f3, TYPE_CONSTANT) + AddSymbol(string("outa"), $1f4, TYPE_CONSTANT) + AddSymbol(string("outb"), $1f5, TYPE_CONSTANT) + AddSymbol(string("dira"), $1f6, TYPE_CONSTANT) + AddSymbol(string("dirb"), $1f7, TYPE_CONSTANT) + AddSymbol(string("ctra"), $1f8, TYPE_CONSTANT) + AddSymbol(string("ctrb"), $1f9, TYPE_CONSTANT) + AddSymbol(string("frqa"), $1fa, TYPE_CONSTANT) + AddSymbol(string("frqb"), $1fb, TYPE_CONSTANT) + AddSymbol(string("phsa"), $1fc, TYPE_CONSTANT) + AddSymbol(string("phsb"), $1fd, TYPE_CONSTANT) + AddSymbol(string("vcfg"), $1fe, TYPE_CONSTANT) + AddSymbol(string("vscl"), $1ff, TYPE_CONSTANT) + AddSymbol(string("xtal1_pll16x"), $6f, TYPE_CONSTANT) + AddSymbol(string("xinput"), $22, TYPE_CONSTANT) + AddSymbol(string("negx"), $80000000, TYPE_CONSTANT) + AddSymbol(string("posx"), $7fffffff, TYPE_CONSTANT) + +PUB ProcessPub(tokens, numtokens, pubnum, pass) | i, locmode, locspace, locaddr + 'printf("ProcessPub: %s\n", tokens[1]); + RemoveLocalSymbols + AddSymbol(string("result"), 0, TYPE_LOCAL) + if (pass == 1) + AddSymbol(long[tokens][1], pubnum, TYPE_GLOBAL) + header[8 +(pubnum << 1)] := pc + locmode := locspace := 0 + locaddr := 4 + i := 2 + repeat while (i < numtokens) + if (byte[long[tokens][i]][0] == 0) + quit + if (byte[long[tokens][i]][0] == SINGLE_QUOTE) + quit + if (byte[long[tokens][i]][0] == "|") + locmode := 1 + elseif (byte[long[tokens][i]][0] <> "(" and byte[long[tokens][i]][0] <> ")" and byte[long[tokens][i]][0] <> ",") + AddSymbol(long[tokens][i], locaddr, TYPE_LOCAL) + locaddr += 4 + if (locmode) + locspace += 4 + i++ + if (pass == 1) + header[9 +(pubnum << 1)] := locspace + if (pubnum == 1) + locaddr1 := locaddr + +PUB ErrorExit + c.printf2(string("%d: %s\n"), linenum, @buffer) + DumpSymbols + c.fclose(infile) + c.exit(1) + +PUB AddExternals(tokens, numtokens) | i, ptr + i := 0 + repeat while (i < numtokens) + if (byte[long[tokens][i]][0] == SINGLE_QUOTE) + i++ + next + if (byte[long[tokens][i]][0] == DOUBLE_QUOTE) + i++ + next + ptr := strsub.FindChar(long[tokens][i], ".") + if (byte[ptr]) + if (FindSymbol(long[tokens][i]) < 0) + AddSymbol(long[tokens][i], - 1, TYPE_PUB) + i++ + next + ptr := strsub.FindChar(long[tokens][i], "#") + if (byte[ptr]) + if (FindSymbol(long[tokens][i]) < 0) + AddSymbol(long[tokens][i], - 1, TYPE_CONSTANT) + i++ + next + i++ + +PUB main(argc, argv) | tokens[MAX_TOKENS], mode, pubnum, numpub, i, fname, numtokens, tokenptr, ptr + mode := 1 + pubnum := 1 + numpub := 0 + fname := 0 + bigptr := @bigbuf + ReadOpsFile + i := 1 + repeat while (i < argc) + if (not c.strcmp(long[argv][i], string("-l"))) + listflag := 1 + elseif (not c.strcmp(long[argv][i], string("-d"))) + verbose := 2 + elseif (fname == 0) + fname := long[argv][i] + else + usage + i++ + if (fname == 0) + usage + c.strcpy(@buffer, fname) + ptr := strsub.FindChar(@buffer, ".") + if (not(byte[ptr])) + c.strcpy(ptr, string(".spa")) + infile := c.fopen(@buffer, string("r")) + if (not infile) + c.printf1(string("Could not open input file %s\n"), @buffer) + usage + c.strcpy(ptr, string(".bin")) + outfile := c.fopen(@buffer, string("wb")) + if (not outfile) + c.printf1(string("Could not open output file %s\n"), @buffer) + usage + AddStandardSymbols + ' Pass 1 - Compute starting address by counting pubs and objects + if (verbose) + c.printf0(string("Pass 1: Counting methods and objects\n")) + pc0 := 4 + linenum := 0 + repeat while (c.fgets(@buffer, 100, infile)) + strsub.RemoveCRLF(@buffer) + numtokens := strsub.GetTokens(@buffer, @buffer2, @tokens) + AddExternals(@tokens, numtokens) + linenum++ + if (numtokens < 1) + next + if (strsub.StrCompNoCase(tokens[0], string("pri"))) + mode := TYPE_PUB + elseif (strsub.StrCompNoCase(tokens[0], string("obj"))) + mode := TYPE_OBJECT + elseif (strsub.StrCompNoCase(tokens[0], string("dat"))) + mode := TYPE_DAT + elseif (strsub.StrCompNoCase(tokens[0], string("var"))) + mode := TYPE_VAR + elseif (strsub.StrCompNoCase(tokens[0], string("con"))) + mode := TYPE_CONSTANT + elseif (strsub.StrCompNoCase(tokens[0], string("pub"))) + mode := TYPE_PUB + numpub++ + elseif (mode == TYPE_OBJECT) + ptr := strsub.FindChar(@buffer, ":") + if (byte[ptr]) + numobj++ + if (verbose => 2) + c.printf1(string("Count object - %s\n"), @buffer) + pc0 :=((numpub + numobj) << 2) + 4 + if (verbose => 2) + c.printf1(string("pc0 = %d\n"), pc0) + c.printf1(string("%d methods found\n"), numpub) + c.printf1(string("%d objects found\n"), numobj) + c.fseek(infile, 0, c#SEEK_SET) + ' Pass 2 - Compile Symbols + if (verbose) + c.printf0(string("Pass 2: Compiling Symbols\n")) + pc := pc0 + mode := 1 + linenum := 0 + repeat while (c.fgets(@buffer, 100, infile)) + linenum++ + strsub.RemoveCRLF(@buffer) + 'tokenize(buffer, buffer2, tokens); + numtokens := strsub.GetTokens(@buffer, @buffer2, @tokens) + i := numtokens + repeat while (i < MAX_TOKENS) + tokens[i] := @zero_char + i++ + tokenptr := @tokens + if (byte[tokens[0]][0] == 0) + next + if (byte[tokens[0]][0] == SINGLE_QUOTE) + next + if (buffer[0] <> " " and buffer[0] <> TAB) + if (strsub.StrCompNoCase(tokens[0], string("con"))) + mode := 0 + elseif (strsub.StrCompNoCase(tokens[0], string("dat"))) + mode := 2 + next + elseif (strsub.StrCompNoCase(tokens[0], string("var"))) + mode := 3 + next + elseif (strsub.StrCompNoCase(tokens[0], string("pub"))) + ProcessPub(@tokens, numtokens, pubnum, 1) + pubnum++ + mode := 1 + elseif (strsub.StrCompNoCase(tokens[0], string("obj"))) + mode := 4 + next + elseif (mode == 2) + if (numtokens => 2 and strsub.StrCompNoCase(tokens[1], string("long"))) + AddSymbol(tokens[0],(((pc + 3) >> 2) << 2), TYPE_DAT) + elseif (numtokens => 2 and strsub.StrCompNoCase(tokens[1], string("word"))) + AddSymbol(tokens[0],(((pc + 1) >> 1) << 1), TYPE_DAT) + else + AddSymbol(tokens[0], pc, TYPE_DAT) + elseif (mode == 3) + if (strsub.StrCompNoCase(tokens[0], string("long"))) + AddSymbol(tokens[1],(((pc + 3) >> 2) << 2), TYPE_VAR) + elseif (strsub.StrCompNoCase(tokens[0], string("word"))) + AddSymbol(tokens[1],(((pc + 1) >> 1) << 1), TYPE_VAR) + else + AddSymbol(tokens[1], pc, TYPE_VAR) + else + AddSymbol(tokens[0], pc, TYPE_GLOBAL) + if (numtokens == 1) + next + numtokens-- + tokenptr += 4 + if (mode == 1) + Process(tokenptr, numtokens, numpub, 1) + elseif (mode == 2) + ProcessDat(tokenptr, numtokens, 1) + elseif (mode == 0) + ProcessConstants(tokenptr, numtokens, 1) + elseif (mode == 4) + ProcessObject(numtokens, tokenptr) + ' Pass 3 + if (verbose) + c.printf1(string("%d symbols compiled\n"), numsym) + c.printf0(string("Pass 3: Generating bytecodes\n")) + PrintHeader(pubnum, numobj) + 'PrintSymbolTable(); + c.fseek(infile, 0, c#SEEK_SET) + pc := pc0 + mode := 1 + linenum := 0 + repeat while (c.fgets(@buffer, 100, infile)) + linenum++ + strsub.RemoveCRLF(@buffer) + c.strcpy(@buffer1, @buffer) + 'tokenize(buffer, buffer2, tokens); + numtokens := strsub.GetTokens(@buffer, @buffer2, @tokens) + tokenptr := @tokens + i := numtokens + repeat while (i < MAX_TOKENS) + tokens[i] := @zero_char + i++ + if (byte[tokens[0]][0] == SINGLE_QUOTE) + if (listflag) + c.printf1(string(" %s\n"), @buffer1) + next + if (buffer[0] <> " " and buffer[0] <> TAB) + if (strsub.StrCompNoCase(tokens[0], string("con"))) + mode := 0 + if (listflag) + c.printf1(string(" %s\n"), @buffer1) + next + if (strsub.StrCompNoCase(tokens[0], string("pub"))) + if (listflag) + c.printf1(string(" %s\n"), @buffer1) + ProcessPub(@tokens, numtokens, pubnum, 2) + mode := 1 + next + if (strsub.StrCompNoCase(tokens[0], string("dat"))) + mode := 2 + if (listflag) + c.printf1(string(" %s\n"), @buffer1) + next + if (strsub.StrCompNoCase(tokens[0], string("obj"))) + mode := 4 + if (listflag) + c.printf1(string(" %s\n"), @buffer1) + next + if (byte[tokens[1]][0] == 0) + if (listflag) + c.printf1(string(" %s\n"), @buffer1) + next + numtokens-- + tokenptr += 4 + if (mode == 1) + Process(tokenptr, numtokens, numpub, 2) + elseif (mode == 2) + ProcessDat(tokenptr, numtokens, 2) + elseif (listflag) + c.printf1(string(" %s\n"), @buffer1) + PrintTail + c.fclose(infile) + c.fclose(outfile) + if (verbose) + c.printf1(string("%d bytes generated\n"), pc + OBJECT_OFFSET + 1) + if (verbose => 2) + DumpSymbols +' ********************************************************************* +' This routine is used during the first passed to extract +' the constants and public methods from an external object +' and add them to the symbol table. +' ********************************************************************* + +PUB ProcessObject(num, tokens) | len, val, entrynum, entrynum1, objfile, objname[5], mode, numpub + mode := TYPE_CONSTANT + numpub := 1 + if (num < 1 or byte[long[tokens][0]][0] == SINGLE_QUOTE) + return + if (num <> 3 or c.strcmp(long[tokens][1], string(":"))) + c.printf0(string("Error in object line\n")) + ErrorExit + if (verbose => 2) + c.printf1(string("ProcessObject: AddSymbol %s\n"), long[tokens][0]) + entrynum := AddSymbol(long[tokens][0],++objnum, TYPE_GLOBAL) + c.strcpy(@objname, long[tokens][2] + 1) + len := strsize(@objname) + if (len => 1) + byte[@objname][len - 1] := 0 + c.strcat(@objname, string(".spa")) + if (verbose => 2) + c.printf3(string("ProcessObject %s %s %s\n"), long[tokens][0], long[tokens][1], long[tokens][2]) + objfile := c.fopen(@objname, string("r")) + if (not objfile) + c.printf1(string("Could not open object file %s\n"), long[tokens][2]) + ErrorExit + repeat while (c.fgets(@buffer, 100, objfile)) + strsub.RemoveCRLF(@buffer) + num := strsub.GetTokens(@buffer, @buffer2, tokens) + if (strsub.StrCompNoCase(long[tokens][0], string("pub"))) + mode := TYPE_PUB + if (num => 2) + c.strcpy(@objname, long[@symbol +(12 * entrynum) + name]) + c.strcat(@objname, string(".")) + c.strcat(@objname, long[tokens][1]) + entrynum1 := FindSymbol(@objname) + if (entrynum1 => 0) + if (verbose => 2) + c.printf2(string("ProcessObject: Set value of %s to %d\n"), @objname, numpub) + long[@symbol +(12 * entrynum1) + value] := numpub + else + 'printf("ProcessObject: Skip adding %s\n", objname); + numpub++ + elseif (strsub.StrCompNoCase(long[tokens][0], string("pri"))) + mode := TYPE_PUB + elseif (strsub.StrCompNoCase(long[tokens][0], string("obj"))) + mode := TYPE_OBJECT + elseif (strsub.StrCompNoCase(long[tokens][0], string("dat"))) + mode := TYPE_DAT + elseif (strsub.StrCompNoCase(long[tokens][0], string("var"))) + mode := TYPE_VAR + elseif (strsub.StrCompNoCase(long[tokens][0], string("con"))) + mode := TYPE_CONSTANT + elseif (mode == TYPE_CONSTANT) + if (num => 2 and c.strcmp(long[tokens][1], string("=")) == 0) + c.strcpy(@objname, long[@symbol +(12 * entrynum) + name]) + c.strcat(@objname, string("#")) + c.strcat(@objname, long[tokens][0]) + entrynum1 := FindSymbol(@objname) + if (entrynum1 => 0) + val := GetVal(long[tokens][2], 0) + if (verbose => 2) + c.printf2(string("ProcessObject: Set value of %s to %d\n"), @objname, val) + long[@symbol +(12 * entrynum1) + value] := val + else + 'printf("ProcessObject: Skip adding %s\n", objname); + c.fclose(objfile) + +PUB DumpSymbols | i + c.printf0(string("Symbol Table\n")) + i := 0 + repeat while (i < numsym) + c.printf4(string("Symbol %d: %s %d %d\n"), i, long[@symbol +(12 * i) + name], long[@symbol +(12 * i) + value], long[@symbol +(12 * i) + type]) + i++ diff --git a/src/spc b/src/spc new file mode 100644 index 0000000..98ac6b6 --- /dev/null +++ b/src/spc @@ -0,0 +1,20 @@ +#shell +if [ $# -ne 1 ] +then +echo "usage: spc file" +exit +fi +echo spinit $1 +spinit $1 +if [ $? -ne 0 ] +then +exit +fi +echo spasm $1 +spasm $1 +if [ $? -ne 0 ] +then +exit +fi +echo splink $1.bin clibsd.bin $1 +splink $1.bin clibsd.bin $1 diff --git a/src/spinit.spin b/src/spinit.spin new file mode 100644 index 0000000..020f5c9 --- /dev/null +++ b/src/spinit.spin @@ -0,0 +1,1328 @@ +'********************************************** +'* Spin code generated by cspin version 0.68 * +'********************************************** + + +CON + _clkmode = xinput + _xinfreq = $4e495053 ' SPIN + +OBJ + c : "clibsd" + strsub : "strsubs" + sys : "sysdefs" + +' ********************************************************************* +' Include files +' ********************************************************************* +' Special Characters + +CON + DOUBLE_QUOTE = $22 + SINGLE_QUOTE = $27 + TAB_CHAR = 9 +' ********************************************************************* +' Function prototypes +' ********************************************************************* +' ********************************************************************* +' Function indices +' ********************************************************************* + fProcessValue = 0 + fProcessClkfreq = 1 + fProcessAccess = 2 + fProcessRepeat = 3 + fProcessKey = 4 + fProcessCase = 5 + fProcessIf = 6 + fProcessElseIf = 7 + fProcessElse = 8 + fProcessQuit = 9 + fProcessNext = 10 + fProcessReturn = 11 + fProcessBinaryOp = 12 + fProcessBinaryEqOp = 13 + fProcessOp = 14 + fProcessUnaryOp = 15 + fProcessAt = 16 + fProcessExtraOp = 17 + fProcessLeftParen = 18 + fProcessRightBracket = 19 + fProcessPub = 20 + fProcessIntrinsic = 21 + fProcessCallObj = 22 + fCatchObject = 23 + fProcessString = 24 +' ********************************************************************* +' Defines +' ********************************************************************* +' Symbol Types + TYPE_PUB = 1 + TYPE_DAT = 2 + TYPE_LOCAL = 3 + TYPE_CON = 4 + TYPE_KEY = 5 + TYPE_OP = $100 + TYPE_NUM = 7 + TYPE_ACCESS = 8 + TYPE_VAR = 9 + TYPE_REG = 10 + TYPE_POSTOP = $200 + TYPE_OBJECT = 11 +' Expected type for next symbol - i.e., value or operator + EXPECT_VAL = 0 + EXPECT_OP = 1 +' Actions that are performed on a value + ACTION_NONE = 0 + ACTION_LOAD = 1 + ACTION_STORE = 2 + ACTION_ADDR = 3 + ACTION_EXEC = 4 +' Types of Spin blocks + LOOP_PUB = 1 + LOOP_REPEAT = 2 + LOOP_IF = 3 + LOOP_ELSE = 4 + LOOP_ELSEIF = 5 + LOOP_CASE = 6 + LOOP_CASE1 = 7 +' Size of the symbol table and string pool + MAX_LEVELS = 5 + MAX_SYMBOLS = 250 + STRING_POOL_SIZE = MAX_SYMBOLS * 8 +' Debug print flag +'#define PRINT_PROCESS +' ********************************************************************* +' Typedefs +' ********************************************************************* +' Function pointer used by a symbol +' Symbol struct +{ +' typedef struct SymbolS +name = 0 +type = 4 +func = 8 +parm = 12 +size = 16 +} + +name = 0 +type = 2 +func = 4 +parm = 6 +size = 8 + +' ********************************************************************* +' Global variables +' ********************************************************************* +' Input line buffer and token buffers + +VAR + byte buffer[100] + byte buf2[200] + long tokens[50] + long commentflag + long linenum +' Variables used for each block level +' PUBs and PRIs start at level zero + +DAT + maxstackaddr long 0 + stacksize long 0 + indent2 long 0 + level long 0 + +VAR + long indent[MAX_LEVELS] + long labelstart[MAX_LEVELS] + long labelend[MAX_LEVELS] + long looplevel[MAX_LEVELS] + long looptype[MAX_LEVELS] +' This variable is used to generate unique labels + +DAT + labelnum long 1 +' Symbol table and string storage buffer + symnum long 0 + pubnum long 0 + +VAR + long symnum_save + +DAT + strindex long 0 + +VAR + long symtable[10*MAX_SYMBOLS/4] + byte strpool[STRING_POOL_SIZE] +' Keyword lists + +DAT + AccessChar byte "b", "w", "l" + datstr byte "coginit", 0, "cognew", 0, "lockset", 0 + byte "lockclr", 0 + ret_list long @datstr + 16, @datstr + 24, @datstr + 31 + long @datstr + 39, 0 + datstr_0 byte "pub", 0, "pri", 0, "dat", 0, "con", 0 + byte "var", 0, "obj", 0 + section_keyword long @datstr_0 + 16, @datstr_0 + 20, @datstr_0 + 24 + long @datstr_0 + 28, @datstr_0 + 32, @datstr_0 + 36, 0 + datstr_1 byte "else", 0, "elseif", 0, "elseifnot", 0 + elselist long @datstr_1 + 16, @datstr_1 + 21, @datstr_1 + 28 + long 0 + datstr_2 byte "byte", 0, "word", 0, "long", 0 + type_keyword long @datstr_2 + 16, @datstr_2 + 21, @datstr_2 + 26 + long 0 + nullstr byte 0 +' Input and output files + +' Debug flag + +DAT + infile long 0 + outfile long 0 + debugflag long 0 +' Spin startup code + +PUB start(argc, argv) + maxstackaddr := @result + 48 + stacksize := (sys#rendezvous - maxstackaddr - 2000) / 4 + c.enter1(stacksize) + 'c.start1(31, 30, %10000, 115_200, result) + 'c.printf1(string("Starting stack address = %4.4x\n"), maxstackaddr) + 'c.printf1(string("Selecting stack size of %d longs\n"), result) + strsub.start + main(argc, argv) + c.exit(0) + +' ********************************************************************* +' This program compiles a Spin file to a Spasm file. It appends +' a ".spin" extension to the specified file name and opens it for +' input. It appends a ".spasm" extension for the output file name. +' Note: Currently writing the Spasm code to the standard output +' The compiler uses two passes though the source code. The first +' pass is used to gather all the global symbols, which include the +' method names, constants, DAT variables and VAR variables. +' During the second pass, the local variables are extracted for each +' method, and the Spasm code is generated. +' ********************************************************************* + +PUB main(argc, argv) | ptr, fname + if (argc == 2) + fname := long[argv][1] + elseif (argc == 3 and not c.strcmp(long[argv][1], string("-d"))) + debugflag := 1 + fname := long[argv][2] + else + usage + if (debugflag) + c.printf1(string("Selecting stack size of %d longs\n"), stacksize) + ' Initialize the symbol table with the Spin keywords + AddKeywords + ' Open the input file + c.strcpy(@buffer, fname) + ptr := strsub.FindChar(@buffer, ".") + if (not(byte[ptr])) + c.strcpy(ptr, string(".spn")) + infile := c.fopen(@buffer, string("r")) + if (not infile) + usage + 'c.printf2(string("infile = %4.4x, outfile = %4.4x\n"), infile, outfile) + 'c.printf1(string("symnum = %d after AddKeywords\n"), symnum) + ' Perform the first pass and get all the global symbols + GetGlobalSymbols + 'c.printf1(string("symnum = %d after GetGlobalSymbols\n"), symnum) + ' Rewind the file and perform the second pass + c.fseek(infile, 0, c#SEEK_SET) + ' Open the output file + c.strcpy(@buffer, fname) + ptr := strsub.FindChar(@buffer, ".") + c.strcpy(ptr, string(".spa")) + outfile := c.fopen(@buffer, string("w")) + if (not outfile) + usage + GenerateSpasm + 'c.printf1(string("symnum = %d after GenerateSpasm\n"), symnum) + if (debugflag) + c.printf1(string("maxstackaddr = %4.4x\n"), maxstackaddr) + c.CheckStackSpace + c.fclose(infile) + c.fclose(outfile) + return 0 +' ********************************************************************* +' Print the usage information and exit +' ********************************************************************* + +PUB usage + c.printf0(string("usage: spinit [-d] file\n")) + c.printf0(string(" -d - Enable debug prints\n")) + c.exit(1) +' ********************************************************************* +' This routine allocates a string buffer from the buffer pool +' ********************************************************************* + +PUB StrAlloc(size_0) | ptr + ptr := @strpool[strindex] + if (strindex + size_0 > STRING_POOL_SIZE) + c.printf0(string("String pool is out of space\n")) + ErrorExit + strindex += size_0 + return ptr +' ********************************************************************* +' This routine adds a symbol to the symbol table +' ********************************************************************* + +PUB AddSymbol(str, type_0, func_0, parm_0) | symptr + symptr := StrAlloc(strsize(str) + 1) + if (symnum => MAX_SYMBOLS) + c.printf0(string("Symbol table is full\n")) + ErrorExit + 'c.printf2(string("AddSymbol: %s, %d\n"), str, type) + word[@symtable +(10 * symnum) + name] := symptr + word[@symtable +(10 * symnum) + type] := type_0 + word[@symtable +(10 * symnum) + func] := func_0 + word[@symtable +(10 * symnum) + parm] := parm_0 + word[@symtable +(10 * symnum) + size] := 4 + c.strcpy(symptr, str) + symnum++ + return symnum - 1 +' ********************************************************************* +' This routine finds a symbol in the table and returns +' it's index, or -1 if not found. +' ********************************************************************* + +PUB FindSymbol(str) | i + i := 0 + repeat while (i < symnum) + 'if (strcmp(str, symtable[i].name) == 0) + if (strsub.StrCompNoCase(str, word[@symtable +(10 * i) + name])) + 'printf("FindSymbol: found %s\n", str); + quit + i++ + if (i == symnum) + i := - 1 + return i +' ********************************************************************* +' This routine adds a symbol to the symbol table if it is not +' already in the table. If a duplicate exists, and error message +' is printed and the program exits. +' ********************************************************************* + +PUB AddSymbolCheck(str, type_0, func_0, parm_0) + if (FindSymbol(str) => 0) + c.printf1(string("Duplicate symbol - %s\n"), str) + ErrorExit + return AddSymbol(str, type_0, func_0, parm_0) +' ********************************************************************* +' This routine determines if a token is a number or +' a symbol in the symbol table. Numbers use symbol +' table entry zero. If the token is undefined an error +' message is printed, and the program exits. +' ********************************************************************* + +PUB FindSymbolType(str) | j, val + j := FindSymbol(str) + if (j < 0) + val := byte[str][0] + if (val == "$" or val == DOUBLE_QUOTE or(val => "0" and val =< "9")) + j := 0 + else + c.printf1(string("Undefined symbol %s\n"), str) + ErrorExit + return j +' ********************************************************************* +' This routine returns the precedence of a symbol if its +' an operator, or zero if not. +' ********************************************************************* + +PUB GetSymbolPrecedence(str) | precedence, j + precedence := 0 + j := FindSymbolType(str) + if (word[@symtable +(10 * j) + type] & TYPE_OP) + precedence := word[@symtable +(10 * j) + type] & 15 + return precedence +' ********************************************************************* +' This routine locates a token in the symbol table and executes +' it's function. It combines the expected type, precedence and +' action value into a single state variable. +' ********************************************************************* + +PUB ExecuteSymbol(i, num, tokens_0, expect, prec, action) | state, j + state :=(expect << 8) |(prec << 4) | action + j := FindSymbolType(long[tokens_0][i]) + 'printf("ExecuteSymbol %s %d %d %d %8.8x\n", tokens[i], expect, prec, action, state); + return ExecuteFunction(word[@symtable +(10 * j) + func], i, num, tokens_0, state) +' ********************************************************************* +' This routine checks if the expected type passed in the state +' variable matches the current token type. +' ********************************************************************* + +PUB CheckExpect(state, current, str) | expect + expect := state >> 8 + if (expect <> current) + c.printf3(string("expect = %d, current = %d, %s\n"), expect, current, str) + ErrorExit +' ********************************************************************* +' This is a default keyword function that should never be called. +' ********************************************************************* + +PUB ProcessKey(i, num, tokens_0, state) + c.printf0(string("KEY\n")) + return i + 1 +' ********************************************************************* +' This routine handles the repeat statement +' ********************************************************************* + +PUB ProcessRepeat(i, num, tokens_0, state) + labelstart[++level] := labelnum + c.fprintf1(outfile, string("label%4.4d\n"), labelnum++) + labelend[level] := labelnum + 'printf("ProcessRepeat: level = %d, labelend = %d\n", level, labelnum); + indent[level] := indent2 + looplevel[level] := level + looptype[level] := LOOP_REPEAT + i++ + if (i => num or byte[long[tokens_0][i]][0] == SINGLE_QUOTE) + labelnum++ + return i + if (not strsub.StrCompNoCase(long[tokens_0][i], string("while"))) + c.printf1(string("Expected while or EOL - %s\n"), long[tokens_0][i]) + ErrorExit + if (++i => num) + c.printf0(string("Encountered EOL\n")) + ErrorExit + 'i = ExecuteSymbol(i, num, tokens, EXPECT_VAL, 12, ACTION_LOAD); + i := ProcessExpression(i, num, tokens_0, 12, ACTION_LOAD) + c.fprintf1(outfile, string(" jz label%4.4d\n"), labelnum++) + return i +' ********************************************************************* +' This routine handles the string function +' ********************************************************************* + +PUB ProcessString(i, num, tokens_0, state) + if (++i => num) + c.printf0(string("Encountered EOL\n")) + ErrorExit + if (c.strcmp(long[tokens_0][i], string("("))) + c.printf0(string("Expected '('\n")) + ErrorExit + if (++i => num) + c.printf0(string("Encountered EOL\n")) + ErrorExit + c.fprintf1(outfile, string(" jmp label%4.4d\n"), labelnum + 1) + c.fprintf1(outfile, string("label%4.4d\n"), labelnum++) + c.fprintf0(outfile, string(" byte ")) + repeat while (i < num and c.strcmp(long[tokens_0][i], string(")"))) + c.fprintf1(outfile, string("%s"), long[tokens_0][i++]) + c.fprintf0(outfile, string(",0\n")) + if (i++=> num) + c.printf0(string("Encountered EOL\n")) + ErrorExit + c.fprintf1(outfile, string("label%4.4d\n"), labelnum++) + c.fprintf1(outfile, string(" labo label%4.4d\n"), labelnum - 2) + return i +' ********************************************************************* +' This routine handles case statements +' ********************************************************************* + +PUB ProcessCase(i, num, tokens_0, state) + c.fprintf1(outfile, string(" ldl label%4.4d\n"), labelnum) + labelstart[++level] := 0 + labelend[level] := labelnum++ + labelstart[level] := 0 + indent[level] := indent2 + looplevel[level] := looplevel[level - 1] + looptype[level] := LOOP_CASE + i++ + if (i => num) + c.printf1(string("Expected expression - %s\n"), long[tokens_0][i]) + ErrorExit + i := ProcessExpression(i, num, tokens_0, 12, ACTION_LOAD) + return i +' ********************************************************************* +' This routine handles if and ifnot statements +' ********************************************************************* + +PUB ProcessIf(i, num, tokens_0, state) | ifnotflag + ifnotflag := strsub.StrCompNoCase(long[tokens_0][i], string("ifnot")) + labelstart[++level] := 0 + labelend[level] := labelnum + labelstart[level] := 0 + indent[level] := indent2 + looplevel[level] := looplevel[level - 1] + looptype[level] := LOOP_IF + i++ + if (i => num or byte[long[tokens_0][i]][0] == SINGLE_QUOTE) + c.printf1(string("Expected expression - %s\n"), long[tokens_0][i]) + ErrorExit + i := ProcessExpression(i, num, tokens_0, 12, ACTION_LOAD) + if (ifnotflag) + c.fprintf1(outfile, string(" jnz label%4.4d\n"), labelnum++) + else + c.fprintf1(outfile, string(" jz label%4.4d\n"), labelnum++) + return i +' ********************************************************************* +' This routine handles elseif and elseifnot statements +' ********************************************************************* + +PUB ProcessElseIf(i, num, tokens_0, state) | ifnotflag + ifnotflag := strsub.StrCompNoCase(long[tokens_0][i], string("elseifnot")) + if ((looptype[level] <> LOOP_IF and looptype[level] <> LOOP_ELSEIF) or indent[level] <> indent2) + c.printf1(string("%s statement without an if\n"), long[tokens_0][0]) + ErrorExit + if (looptype[level] == LOOP_IF) + labelstart[level] := labelnum++ + c.fprintf1(outfile, string(" jmp label%4.4d\n"), labelstart[level]) + c.fprintf1(outfile, string("label%4.4d\n"), labelend[level]) + looptype[level] := LOOP_ELSEIF + labelend[level] := labelnum + if (++i => num or byte[long[tokens_0][i]][0] == SINGLE_QUOTE) + c.printf1(string("Expected expression - %s\n"), long[tokens_0][i]) + ErrorExit + i := ProcessExpression(i, num, tokens_0, 12, ACTION_LOAD) + if (ifnotflag) + c.fprintf1(outfile, string(" jnz label%4.4d\n"), labelnum++) + else + c.fprintf1(outfile, string(" jz label%4.4d\n"), labelnum++) + return i +' ********************************************************************* +' This routine handles else statements +' ********************************************************************* + +PUB ProcessElse(i, num, tokens_0, state) + if ((looptype[level] <> LOOP_IF and looptype[level] <> LOOP_ELSEIF) or indent[level] <> indent2) + c.printf1(string("%s statement without an if\n"), long[tokens_0][0]) + ErrorExit + if (looptype[level] == LOOP_IF) + c.fprintf1(outfile, string(" jmp label%4.4d\n"), labelnum) + c.fprintf1(outfile, string("label%4.4d\n"), labelend[level]) + labelend[level] := labelnum++ + else + c.fprintf1(outfile, string(" jmp label%4.4d\n"), labelstart[level]) + c.fprintf1(outfile, string("label%4.4d\n"), labelend[level]) + labelend[level] := labelstart[level] + labelstart[level] := 0 + looptype[level] := LOOP_ELSE + return i + 1 +' ********************************************************************* +' This routine handles the quit statement +' ********************************************************************* + +PUB ProcessQuit(i, num, tokens_0, state) | j + j := looplevel[level] + 'printf("ProcessQuit: level = %d, labelend = %d\n", j, labelend[j]); + if (labelend[j]) + c.fprintf1(outfile, string(" jmp label%4.4d\n"), labelend[j]) + return i + 1 +' ********************************************************************* +' This routine handles the next statement +' ********************************************************************* + +PUB ProcessNext(i, num, tokens_0, state) | j + j := looplevel[level] + if (labelstart[j]) + c.fprintf1(outfile, string(" jmp label%4.4d\n"), labelstart[j]) + return i + 1 +' ********************************************************************* +' This routine handles return statements +' ********************************************************************* + +PUB ProcessReturn(i, num, tokens_0, state) + i++ + if (i => num or byte[long[tokens_0][i]][0] == SINGLE_QUOTE) + c.fprintf0(outfile, string(" ret\n")) + return i + i := ProcessExpression(i, num, tokens_0, 12, ACTION_LOAD) + c.fprintf0(outfile, string(" retval\n")) + return i +' ********************************************************************* +' This routine handles the byte[], word[] and long[] operator +' ********************************************************************* + +PUB ProcessAccess(i, num, tokens_0, state) | index, action, xstr, postop + action := state & 15 + xstr := @nullstr + postop := - 1 + ' Determine the size + if (strsub.StrCompNoCase(long[tokens_0][i], string("byte"))) + index := 0 + elseif (strsub.StrCompNoCase(long[tokens_0][i], string("word"))) + index := 1 + else + index := 2 + CheckExpect(state, EXPECT_VAL, long[tokens_0][i]) + if (++i => num) + c.printf0(string("Encountered EOL\n")) + ErrorExit + if (c.strcmp(long[tokens_0][i], string("["))) + c.printf1(string("Expected '[' - %s\n"), long[tokens_0][i]) + ErrorExit + if (++i => num) + c.printf0(string("Encountered EOL\n")) + ErrorExit + ' Get the value between the brackets + i := ProcessExpression(i, num, tokens_0, 12, ACTION_LOAD) + if (c.strcmp(long[tokens_0][i], string("]"))) + c.printf1(string("Expected ']' - %s\n"), long[tokens_0][i]) + ErrorExit + ' Check for index + if (++i < num and byte[long[tokens_0][i]][0] == "[") + i := ProcessExpression(i + 1, num, tokens_0, 12, ACTION_LOAD) + if (c.strcmp(long[tokens_0][i], string("]"))) + c.printf1(string("Expected ']' - %s\n"), long[tokens_0][i]) + ErrorExit + i++ + xstr := string("x") + ' Check for post operator + if (i < num) + postop := FindSymbolType(long[tokens_0][i]) + if (word[@symtable +(10 * postop) + type] & TYPE_POSTOP) + action := ACTION_EXEC + else + postop := - 1 + ' Peform the memory operation + if (action == ACTION_LOAD) + c.fprintf2(outfile, string(" ld%ca%s\n"), AccessChar[index], xstr) + elseif (action == ACTION_STORE) + c.fprintf2(outfile, string(" st%ca%s\n"), AccessChar[index], xstr) + elseif (action == ACTION_ADDR) + c.fprintf2(outfile, string(" la%ca%s\n"), AccessChar[index], xstr) + elseif (action == ACTION_EXEC) + c.fprintf2(outfile, string(" ex%ca%s"), AccessChar[index], xstr) + if (postop <> - 1) + i := ExecuteFunction(word[@symtable +(10 * postop) + func], i, num, tokens_0,(EXPECT_OP << 8) |(state & 255)) + return i +' ********************************************************************* +' This routine should never be called +' ********************************************************************* + +PUB ProcessOp(i, num, tokens_0, state) + c.printf0(string("OP\n")) + return i + 1 +' ********************************************************************* +' This routine handles the binary operators. It will return if +' the current operator has greater than or equal precedence than +' the previous opertor. Otherwise, is will call ProcessExpression +' to recursively process the next part of the expression. +' ********************************************************************* + +PUB ProcessBinaryOp(i, num, tokens_0, state) | j, prec1, prec2 + j := FindSymbolType(long[tokens_0][i]) + prec1 :=(state >> 4) & 15 + prec2 := word[@symtable +(10 * j) + type] & 15 + CheckExpect(state, EXPECT_OP, long[tokens_0][i]) + ' This test is not needed, and it should never be true. + ' However, I'm keeping it for now for debug purposes. + if (prec1 =< prec2) + c.printf0(string("XXXXXXXXXX ProcessBinaryOp XXXXXXXXXX\n")) + return i + if (++i => num) + c.printf0(string("Expected value, found EOL\n")) + ErrorExit + i := ProcessExpression(i, num, tokens_0, prec2, ACTION_LOAD) + c.fprintf1(outfile, string(" %s\n"), word[@symtable +(10 * j) + parm]) + return i +' ********************************************************************* +' This routine handles the binary assignment operators +' ********************************************************************* + +PUB ProcessBinaryEqOp(i, num, tokens_0, state) | j + j := FindSymbolType(long[tokens_0][i]) + 'int prec1 = (state >> 4) & 15; + 'int prec2 = symtable[j].type & 15; + CheckExpect(state, EXPECT_OP, long[tokens_0][i]) + 'if (prec1 <= prec2) return i; + if (i + 1 => num) + c.printf0(string("Expected value, found EOL\n")) + ErrorExit + 'i = ExecuteSymbol(i, num, tokens, EXPECT_VAL, prec2, ACTION_LOAD); + c.fprintf1(outfile, string(" %s\n"), word[@symtable +(10 * j) + parm]) + return i +' ********************************************************************* +' This routine handles the non-assignment unary operators +' ********************************************************************* + +PUB ProcessUnaryOp(i, num, tokens_0, state) | j, prec2 + j := FindSymbolType(long[tokens_0][i]) + prec2 := word[@symtable +(10 * j) + type] & 15 + CheckExpect(state, EXPECT_VAL, long[tokens_0][i]) + if (++i => num) + c.printf0(string("Expected value, found EOL\n")) + ErrorExit + i := ExecuteSymbol(i, num, tokens_0, EXPECT_VAL, prec2, ACTION_LOAD) + c.fprintf1(outfile, string(" %s\n"), word[@symtable +(10 * j) + parm]) + return i +' ********************************************************************* +' This routine handles the extra unary assignment operators +' ********************************************************************* + +PUB ProcessExtraOp(i, num, tokens_0, state) | j, prec, action, expect, opstr[5], ptr1, ptr2, lstr, len + j := FindSymbolType(long[tokens_0][i]) + prec :=(state >> 4) & 15 + action := state & 15 + expect := state >> 8 + ptr1 := word[@symtable +(10 * j) + parm] + ptr2 := strsub.FindChar(ptr1, "-") + lstr := @nullstr + if (action == ACTION_LOAD) + lstr := string(" load") + ' Check if it is a pre-op + if (expect == EXPECT_VAL) + len := ptr2 - ptr1 + c.memcpy(@opstr, ptr1, len) + byte[@opstr][len] := 0 + ' Otherwise, it's a post-op + else + c.fprintf2(outfile, string(" %s%s\n"), ptr2 + 1, lstr) + return i + 1 + if (++i => num) + c.printf0(string("Expected value, found EOL\n")) + ErrorExit + i := ExecuteSymbol(i, num, tokens_0, EXPECT_VAL, prec, ACTION_EXEC) + c.fprintf2(outfile, string(" %s%s\n"), @opstr, lstr) + return i +' ********************************************************************* +' This routine handles the "@" address operator +' ********************************************************************* + +PUB ProcessAt(i, num, tokens_0, state) | j, action + action := state & 15 + 'printf("ProcessAt %d %8.8x\n", action, state); + if (action <> ACTION_LOAD) + c.printf1(string("Invalid action for address operator - %d\n"), action) + ErrorExit + if (++i => num) + c.printf0(string("Expected variable - EOL\n")) + ErrorExit + j := FindSymbolType(long[tokens_0][i]) + state :=(state & $ffffff00) | ACTION_ADDR + i := ExecuteFunction(word[@symtable +(10 * j) + func], i, num, tokens_0, state) + return i +' ********************************************************************* +' This routine handles intrinsic functions and calling methods +' ********************************************************************* + +PUB ProcessFunction(i, num, tokens_0, state, intrinsic) | first, pubname, action, prec + first := 1 + pubname := long[tokens_0][i] + action := state & 15 + prec :=(state >> 4) & 15 + if (intrinsic <> 1) + if (action == ACTION_LOAD) + c.fprintf0(outfile, string(" ldfrmr\n")) + else + c.fprintf0(outfile, string(" ldfrm\n")) + elseif (strsub.StrCompNoCase(pubname, string("cognew"))) + pubname := string("coginit") + c.fprintf0(outfile, string(" ldl 8\n")) + if (++i < num and byte[long[tokens_0][i]][0] == "(") + i++ + repeat while (i < num and byte[long[tokens_0][i]][0] <> ")") + if (first) + first := 0 + else + if (c.strcmp(long[tokens_0][i], string(","))) + c.printf1(string("Expected ',' or ')' - %s\n"), long[tokens_0][i]) + ErrorExit + i++ + i := ProcessExpression(i, num, tokens_0, 12, ACTION_LOAD) + if (intrinsic == 1) + if (action == ACTION_LOAD and strsub.FindListNoCase(pubname, @ret_list) => 0) + c.fprintf1(outfile, string(" %sret\n"), pubname) + else + c.fprintf1(outfile, string(" %s\n"), pubname) + elseif (intrinsic == 0) + c.fprintf1(outfile, string(" call %s\n"), pubname) + else + c.fprintf1(outfile, string(" callobj %s\n"), pubname) + if (++i < num) + i := ExecuteSymbol(i, num, tokens_0, EXPECT_OP, prec, action) + return i +' ********************************************************************* +' This is used to call methods in external objects +' ********************************************************************* + +PUB ProcessCallObj(i, num, tokens_0, state) + return ProcessFunction(i, num, tokens_0, state, 3) +' ********************************************************************* +' This is used to call local methods +' ********************************************************************* + +PUB ProcessPub(i, num, tokens_0, state) + return ProcessFunction(i, num, tokens_0, state, 0) +' ********************************************************************* +' This handles intrinsic functions +' ********************************************************************* + +PUB ProcessIntrinsic(i, num, tokens_0, state) + return ProcessFunction(i, num, tokens_0, state, 1) +' ********************************************************************* +' This handles tokens that are variables, numbers or constants +' ********************************************************************* + +PUB ProcessValue(i, num, tokens_0, state) | j, xstr, vstr, sstr, action, action1, prec, postop + xstr := @nullstr + vstr := long[tokens_0][i] + action := state & 15 + action1 := action + prec :=(state >> 4) & 15 + postop := - 1 + ' Determine the variable size + j := FindSymbolType(long[tokens_0][i]) + if (word[@symtable +(10 * j) + type] == TYPE_REG) + sstr := string("reg") + elseif (word[@symtable +(10 * j) + size] == 1) + sstr := string("b") + elseif (word[@symtable +(10 * j) + size] == 2) + sstr := string("w") + else + sstr := string("l") + 'printf("ProcessValue %s\n", tokens[i]); + CheckExpect(state, EXPECT_VAL, long[tokens_0][i]) + ' Check for index + if (++i < num and byte[long[tokens_0][i]][0] == "[") + i := ProcessExpression(i + 1, num, tokens_0, 12, ACTION_LOAD) + if (c.strcmp(long[tokens_0][i], string("]"))) + c.printf1(string("Expected ']' - %s\n"), long[tokens_0][i]) + ErrorExit + i++ + xstr := string("x") + ' Check for post operator + if (i < num) + postop := FindSymbolType(long[tokens_0][i]) + if (word[@symtable +(10 * postop) + type] & TYPE_POSTOP) + action := ACTION_EXEC + else + postop := - 1 + ' Perform action + if (action == ACTION_LOAD) + c.fprintf3(outfile, string(" ld%s%s %s\n"), sstr, xstr, vstr) + elseif (action == ACTION_STORE) + c.fprintf3(outfile, string(" st%s%s %s\n"), sstr, xstr, vstr) + elseif (action == ACTION_ADDR) + c.fprintf3(outfile, string(" la%s%s %s\n"), sstr, xstr, vstr) + elseif (action == ACTION_EXEC) + c.fprintf3(outfile, string(" ex%s%s %s"), sstr, xstr, vstr) + if (postop <> - 1) + i := ExecuteFunction(word[@symtable +(10 * postop) + func], i, num, tokens_0,(EXPECT_OP << 8) |(prec << 4) | action1) + return i +' ********************************************************************* +' This handles the right parenthesis and bracket and the comma +' ********************************************************************* + +PUB ProcessRightBracket(i, num, tokens_0, state) + CheckExpect(state, EXPECT_OP, long[tokens_0][i]) + return i +' ********************************************************************* +' This handles the clkfreq variable +' ********************************************************************* + +PUB ProcessClkfreq(i, num, tokens_0, state) | action + action := state & 15 + c.fprintf0(outfile, string(" ldli0\n")) + if (action == ACTION_LOAD) + c.fprintf0(outfile, string(" ldla\n")) + elseif (action == ACTION_STORE) + c.fprintf0(outfile, string(" stla\n")) + return i + 1 +' ********************************************************************* +' This routine initializes the symbol table with +' the standard Spin tokens. +' ********************************************************************* + +PUB AddKeywords | i, j, k, ptr + if (debugflag) + c.printf0(string("Adding Spin keywords to the symbol table\n")) + infile := c.fopen(string("/bin/spinsymb.txt"), string("r")) + if (not infile) + c.printf0(string("Could not open /bin/spinsymb.txt\n")) + c.exit(1) + repeat while (c.fgets(@buffer, 100, infile)) + strsub.RemoveCRLF(@buffer) + tokens[0] := ptr := @buffer + i := 1 + repeat while (i < 4) + ptr := strsub.FindChar(ptr, " ") + byte[ptr++] := 0 + tokens[i] := ptr + i++ + i := atol(tokens[0]) + j := atol(tokens[1]) + if (c.isdigit(byte[tokens[3]][0])) + k := atol(tokens[3]) + else + k := StrAlloc(strsize(tokens[3]) + 1) + if (symnum => MAX_SYMBOLS) + c.printf0(string("Symbol table is full\n")) + ErrorExit + c.strcpy(k, tokens[3]) + AddSymbol(tokens[2], i, j, k) + c.fclose(infile) + infile := 0 + 'DumpSymbols(); + +PUB atol(ptr) | val + val := 0 + repeat while (byte[ptr]) + val :=(val * 10) + byte[ptr++] - "0" + return val +' ********************************************************************* +' This routine handles lines that use the { or {{ comment delimeter. +' Currently, the entire line is treated as a comment. +' ********************************************************************* + +PUB CommentLine(str) | retval + retval := commentflag + if (commentflag == 1) ' Search for a '}' character + str := strsub.FindChar(str, "}") + if (byte[str] == "}") + commentflag := 0 + elseif (commentflag == 2) ' Search for a "}}" string + str := strsub.FindStr(str, string("}}")) + if (byte[str][0] == "}") + commentflag := 0 + else ' Check first nonblank character for a '{' + str := strsub.SkipChars(str, string(" \t")) + if (byte[str][0] == "{") + if (byte[str][1] == "{") + commentflag := 2 + else + commentflag := 1 + retval := commentflag + return retval +' ********************************************************************* +' This routine executes the first pass through the input +' file. It extracts the global methods, variables and +' constants and adds them to the symbol table. Every +' symbol that is used by the Spin prgram should be in +' the symbol table after this pass, except for the local +' variables, which are added at the beginning of each +' method. +' ********************************************************************* + +PUB GetGlobalSymbols | i, num, ptr, mode + mode := TYPE_CON + commentflag := 0 + linenum := 0 + c.printf0(string("Pass 1: Compiling Global Symbols\n")) + repeat while (c.fgets(@buffer, 100, infile)) + linenum++ + 'c.printf1(string("1: %s\n"), @buffer) + strsub.RemoveCRLF(@buffer) + if (CommentLine(@buffer)) + next + num := strsub.GetTokens(@buffer, @buf2, @tokens) + if (num == 0) + next + if (byte[tokens[0]][0] == SINGLE_QUOTE) + next + if (strsub.StrCompNoCase(tokens[0], string("pub"))) + mode := TYPE_PUB + AddSymbolCheck(tokens[1], mode, fProcessPub, 0) + elseif (strsub.StrCompNoCase(tokens[0], string("con"))) + mode := TYPE_CON + elseif (strsub.StrCompNoCase(tokens[0], string("dat"))) + mode := TYPE_DAT + elseif (strsub.StrCompNoCase(tokens[0], string("var"))) + mode := TYPE_VAR + elseif (strsub.StrCompNoCase(tokens[0], string("obj"))) + mode := TYPE_OBJECT + elseif (mode == TYPE_CON) + if (num => 3 and c.strcmp(tokens[1], string("=")) == 0) + AddSymbolCheck(tokens[0], mode, fProcessValue, 0) + elseif (mode == TYPE_DAT) + if (not strsub.StrCompNoCase(tokens[0], string("byte")) and not strsub.StrCompNoCase(tokens[0], string("word")) and not strsub.StrCompNoCase(tokens[0], string("long"))) + AddSymbolCheck(tokens[0], mode, fProcessValue, 0) + if (num > 1) + if (strsub.StrCompNoCase(tokens[1], string("byte"))) + word[@symtable +(10 *(symnum - 1)) + size] := 1 + elseif (strsub.StrCompNoCase(tokens[1], string("word"))) + word[@symtable +(10 *(symnum - 1)) + size] := 2 + elseif (mode == TYPE_VAR) + if (num => 2 and(strsub.StrCompNoCase(tokens[0], string("byte")) or strsub.StrCompNoCase(tokens[0], string("word")) or strsub.StrCompNoCase(tokens[0], string("long")))) + AddSymbolCheck(tokens[1], mode, fProcessValue, 0) + if (strsub.StrCompNoCase(tokens[0], string("byte"))) + word[@symtable +(10 *(symnum - 1)) + size] := 1 + elseif (strsub.StrCompNoCase(tokens[0], string("word"))) + word[@symtable +(10 *(symnum - 1)) + size] := 2 + elseif (mode == TYPE_OBJECT) + AddObject(num, @tokens) + elseif (mode == TYPE_PUB) + i := 0 + repeat while (i < num) + if (byte[tokens[i]][0] == SINGLE_QUOTE) + i++ + next + if (byte[tokens[i]][0] == DOUBLE_QUOTE) + i++ + next + ptr := strsub.FindChar(tokens[i], ".") + if (byte[ptr]) + if (debugflag) + c.printf1(string("Found external method %s\n"), tokens[i]) + if (FindSymbol(tokens[i]) < 0) + AddSymbolCheck(tokens[i], TYPE_PUB, fProcessCallObj, 0) + i++ + next + ptr := strsub.FindChar(tokens[i], "#") + if (byte[ptr]) + c.printf1(string("Found external constant %s\n"), tokens[i]) + if (FindSymbol(tokens[i]) < 0) + AddSymbolCheck(tokens[i], TYPE_CON, fProcessValue, 0) + i++ + next + i++ + ' Save the index for the start of the local symbols + symnum_save := symnum +' ********************************************************************* +' This routine processes the PUB statement. It sets the level +' to zero and initializes the level variables. It also adds +' the local variables to the symbol table. +' ********************************************************************* + +PUB ProcessPubStatement(i, num, tokens_0) | ptr, skipflag + skipflag := 0 + symnum := symnum_save + ' Initialize level zero + level := 0 + indent[level] := 0 + labelstart[level] := 0 + labelend[level] := 0 + looplevel[level] := 0 + looptype[level] := LOOP_PUB + ' Add the local variables to the symbol table + + repeat while (i < num) + ptr := strsub.FindChar(string("(),|:"), byte[long[tokens_0][i]][0]) + if (byte[ptr]) + i++ + next + if (skipflag) + if (byte[long[tokens_0][i]][0] == "]") + skipflag := 0 + elseif (byte[long[tokens_0][i]][0] == "[") + skipflag := 1 + else + AddSymbolCheck(long[tokens_0][i], TYPE_LOCAL, fProcessValue, 0) + i++ + return i +' ********************************************************************* +' This routine should never be called +' ********************************************************************* + +PUB CatchObject(i, num, tokens_0, state) + return i + 1 +' ********************************************************************* +' This routine is used during the first pass to add the object prefixes +' to the symbol table. To save space, the objects themselves are not +' examined, and their globals are not added to the symbol table. +' ********************************************************************* + +PUB AddObject(num, tokens_0) + 'char *ptr; + if (num < 1 or byte[long[tokens_0][0]][0] == SINGLE_QUOTE) + return + if (num <> 3 or c.strcmp(long[tokens_0][1], string(":"))) + c.printf0(string("Error in object line\n")) + ErrorExit + 'ptr = StrAlloc(strlen(tokens[2]) + 1); + 'strcpy(ptr, tokens[2]); + 'printf("ProcessObject: AddSymbol %s\n", tokens[0]); + 'AddSymbolCheck(tokens[0], TYPE_OBJECT, fCatchObject, (int)ptr); + AddSymbolCheck(long[tokens_0][0], TYPE_OBJECT, fCatchObject, 0) +' ********************************************************************* +' This routine handles the left parenthesis by calling +' ProcessExpression with the highest operator precedence +' and an action of "load". ProcessExpression must return +' pointing at a right parenthesis. +' ********************************************************************* + +PUB ProcessLeftParen(i, num, tokens_0, state) + i := ProcessExpression(i + 1, num, tokens_0, 12, ACTION_LOAD) + if (c.strcmp(long[tokens_0][i], string(")"))) + c.printf1(string("Expected ')' - %s\n"), long[tokens_0][i]) + ErrorExit + return i + 1 +' ********************************************************************* +' This routine processes an expression by first processing a value, +' and then processing operators in a loop. The operator functions +' will recursively call this routine to execute higher precedent +' operations. The loop terminates when we hit the end of the line, +' a right parenthesis or bracket, a comma, or an operator with greater +' or equal precedence. +' ********************************************************************* + +PUB ProcessExpression(i, num, tokens_0, prec, action) + i := ExecuteSymbol(i, num, tokens_0, EXPECT_VAL, prec, action) + repeat while (i < num) + if (prec =< GetSymbolPrecedence(long[tokens_0][i])) + quit + if (byte[long[tokens_0][i]][0] == ":") + quit + if (byte[long[tokens_0][i]][0] == ")") + quit + if (byte[long[tokens_0][i]][0] == "]") + quit + if (byte[long[tokens_0][i]][0] == ",") + quit + i := ExecuteSymbol(i, num, tokens_0, EXPECT_OP, prec, ACTION_LOAD) + return i +' ********************************************************************* +' This routine process a case item statement +' ********************************************************************* + +PUB ProcessCaseItem(i, num, tokens_0) | j + ' Process items to the left of the colon + repeat while (i < num) + i := ProcessExpression(i, num, tokens_0, 12, ACTION_LOAD) + if (i < num) + if (byte[long[tokens_0][i]][0] == ",") + i++ + else + quit + ' Loop must have exited at a colon + if (i => num or byte[long[tokens_0][i]][0] <> ":") + c.printf0(string("Expected a colon, but encountered EOL\n")) + ErrorExit + i++ + ' Increment the level + labelstart[++level] := 0 + labelend[level] := labelnum + labelstart[level] := 0 + indent[level] := indent2 + looplevel[level] := looplevel[level - 1] + looptype[level] := 0 + ' Process inline statement + if (i < num) + j := LocateAssignment(num, tokens_0) + if (j < num) + ProcessStore(0, num, tokens_0, j) + else + ProcessNonStore(0, num, tokens_0) + return i +' ********************************************************************* +' This routine process a statement that contains an assignment +' operator. +' ********************************************************************* + +PUB ProcessStore(i, num, tokens_0, assignpos) | l + if (assignpos + 1 => num) + c.printf0(string("Expected value, found EOL\n")) + ErrorExit + ' Process the right side of the statement + l := ProcessExpression(assignpos + 1, num, tokens_0, 12, ACTION_LOAD) + ' Process the left side + i := ExecuteSymbol(i, num, tokens_0, EXPECT_VAL, 12, ACTION_STORE) + if (i <> assignpos) + c.printf1(string("Expected assignment op, found %s\n"), long[tokens_0][i]) + ErrorExit + return l +' ********************************************************************* +' This routine processes a statement that does not contain an +' assignment operator. +' ********************************************************************* + +PUB ProcessNonStore(i, num, tokens_0) + i := ProcessExpression(i, num, tokens_0, 12, ACTION_NONE) + if (i < num) + c.printf1(string("Expected EOL, found %s\n"), long[tokens_0][i]) + ErrorExit + return i +' ********************************************************************* +' This routine determines the indention of a line. +' ********************************************************************* + +PUB GetIndent(str) + indent2 := 0 + repeat while (byte[str]) + if (byte[str] == " ") + indent2++ + elseif (byte[str] == TAB_CHAR) + indent2 :=(indent2 + 8) & ! 7 + else + quit + str++ +' ********************************************************************* +' This routine drops down to lower levels until the block +' indention is less than the current statement's indention. +' It prints the labels and jmps for each level depding on +' the label values that were set. The ELSEIF block must be +' handled differently. +' ********************************************************************* + +PUB DropLevels(indent1) + repeat while (level => 0 and indent[level] => indent1) + if (looptype[level] == LOOP_ELSEIF) + c.fprintf1(outfile, string("label%4.4d\n"), labelend[level]) + c.fprintf1(outfile, string("label%4.4d\n"), labelstart[level]) + elseif (looptype[level] == LOOP_CASE) + c.fprintf1(outfile, string("label%4.4d\n"), labelend[level]) + c.fprintf0(outfile, string(" casedone\n")) + else + if (labelstart[level]) + c.fprintf1(outfile, string(" jmp label%4.4d\n"), labelstart[level]) + if (labelend[level]) + c.fprintf1(outfile, string("label%4.4d\n"), labelend[level]) + level-- +' ********************************************************************* +' This routine handles the end of method by dropping to +' level zero and printing a return at the end. +' ********************************************************************* + +PUB PrintEndOfPub + DropLevels(- 1) + c.fprintf0(outfile, string(" ret\n\n")) +' ********************************************************************* +' This routine searches for an assignment operator, and +' returns it's token index if found. +' ********************************************************************* + +PUB LocateAssignment(num, tokens_0) | i, len + i := 0 + repeat while (i < num) + len := strsize(long[tokens_0][i]) + 'printf("%d, %d, %s\n", i, len, tokens[i]); + if (len > 0 and byte[long[tokens_0][i]][len - 1] == "=" and c.strcmp(long[tokens_0][i], string("=="))) + quit + i++ + 'if (i < num) printf("Found %s\n", tokens[i]); + return i +' ********************************************************************* +' This routine performs the second pass through the input file. +' It converts the statements within the Spin methods to Spasm code. +' ********************************************************************* + +PUB GenerateSpasm | index, i, j, num, sectiontype + c.printf0(string("Pass 2: Generating Spasm Code\n")) + sectiontype := 3 + commentflag := 0 + linenum := 0 + repeat while (c.fgets(@buffer, 100, infile)) + linenum++ + strsub.RemoveCRLF(@buffer) + if (CommentLine(@buffer)) + c.fprintf1(outfile, string("%s\n"), @buffer) + next + num := strsub.GetTokens(@buffer, @buf2, @tokens) + if (num == 0) + if (sectiontype <> 0 and sectiontype <> 1) + c.fprintf0(outfile, string("\n")) + next + elseif (byte[tokens[0]][0] == SINGLE_QUOTE) + c.fprintf1(outfile, string("%s\n"), @buffer) + next + GetIndent(@buffer) + ' Search for a start of a Spin section + index := strsub.FindListNoCase(tokens[0], @section_keyword) + ' Check if in a current pub + if (sectiontype < 2) + if (index => 0) + PrintEndOfPub + sectiontype := index + c.fprintf1(outfile, string("%s\n"), @buffer) + ' pub or pri + if (index < 2) + ProcessPubStatement(2, num, @tokens) + else + if (strsub.FindListNoCase(tokens[0], @elselist) => 0) + DropLevels(indent2 + 1) + else + DropLevels(indent2) + c.fprintf1(outfile, string("\n'%s\n"), @buffer) + if (looptype[level] == LOOP_CASE and indent2 > indent[level]) + ProcessCaseItem(0, num, @tokens) + else + j := LocateAssignment(num, @tokens) + if (j < num) + ProcessStore(0, num, @tokens, j) + else + ProcessNonStore(0, num, @tokens) + ' Otherwise, just print the line and check for a pub + else + i := 0 + ' Ensure that labels start in the first colunm in a DAT section + if (sectiontype == 2 and strsub.FindListNoCase(tokens[0], @type_keyword) < 0) + repeat while (buffer[i] == " " or buffer[i] == TAB_CHAR) + i++ + c.fprintf1(outfile, string("%s\n"), @buffer[i]) + if (index => 0) + ' Check if pub or pri statement + if (index < 2) + ProcessPubStatement(2, num, @tokens) + sectiontype := index + ' Print the end of the pub + if (sectiontype < 2) + PrintEndOfPub + +PUB CloseFiles + if infile + c.fclose(infile) + if outfile + c.fclose(outfile) + +PUB ErrorExit + c.printf2(string("%d: %s\n"), linenum, @buffer) + CloseFiles + c.exit(1) + +PUB ExecuteFunction(fFunction, i, num, tokens_0, state) | retval + 'c.printf6(string("ExecuteFunction(%d, %d, %d, %s, %d, %4.4x)\n"), fFunction, i, num, long[tokens][i], state, @retval) + if (@retval > maxstackaddr) + maxstackaddr := @retval + if (maxstackaddr > $767a) + c.printf0(string("Out of stack space\n")) + ErrorExit + case(fFunction) + fProcessValue : + retval := ProcessValue(i, num, tokens_0, state) + fProcessClkfreq : + retval := ProcessClkfreq(i, num, tokens_0, state) + fProcessAccess : + retval := ProcessAccess(i, num, tokens_0, state) + fProcessRepeat : + retval := ProcessRepeat(i, num, tokens_0, state) + fProcessKey : + retval := ProcessKey(i, num, tokens_0, state) + fProcessCase : + retval := ProcessCase(i, num, tokens_0, state) + fProcessIf : + retval := ProcessIf(i, num, tokens_0, state) + fProcessElseIf : + retval := ProcessElseIf(i, num, tokens_0, state) + fProcessElse : + retval := ProcessElse(i, num, tokens_0, state) + fProcessQuit : + retval := ProcessQuit(i, num, tokens_0, state) + fProcessNext : + retval := ProcessNext(i, num, tokens_0, state) + fProcessReturn : + retval := ProcessReturn(i, num, tokens_0, state) + fProcessBinaryOp : + retval := ProcessBinaryOp(i, num, tokens_0, state) + fProcessBinaryEqOp : + retval := ProcessBinaryEqOp(i, num, tokens_0, state) + fProcessOp : + retval := ProcessOp(i, num, tokens_0, state) + fProcessUnaryOp : + retval := ProcessUnaryOp(i, num, tokens_0, state) + fProcessAt : + retval := ProcessAt(i, num, tokens_0, state) + fProcessExtraOp : + retval := ProcessExtraOp(i, num, tokens_0, state) + fProcessLeftParen : + retval := ProcessLeftParen(i, num, tokens_0, state) + fProcessRightBracket : + retval := ProcessRightBracket(i, num, tokens_0, state) + fProcessPub : + retval := ProcessPub(i, num, tokens_0, state) + fProcessIntrinsic : + retval := ProcessIntrinsic(i, num, tokens_0, state) + fProcessCallObj : + retval := ProcessCallObj(i, num, tokens_0, state) + fCatchObject : + retval := CatchObject(i, num, tokens_0, state) + fProcessString : + retval := ProcessString(i, num, tokens_0, state) + OTHER : + c.printf1(string("ProcessFunction: Invalid function number - %d\n"), fFunction) + ErrorExit + return retval diff --git a/src/spinix.c b/src/spinix.c new file mode 100644 index 0000000..f1c06f3 --- /dev/null +++ b/src/spinix.c @@ -0,0 +1,45 @@ +#include +#include "spinix.h" + +static int run_prog[] = { + 0xa0bc65f0, 0x08bc6e32, 0x80fc6404, 0x08bc7032, 0x80fc6404, 0x08bc6032, + 0x80fc6404, 0x08bc6232, 0x80fc63ff, 0x28fc6209, 0x08fc6600, 0x00fc6804, + 0x083c6029, 0x083c5c2a, 0x083c582b, 0x08bc642b, 0x863c642c, 0x5c68000f, + 0x80fc6001, 0x80fc5d00, 0x80fc5d00, 0xe4fc620c, 0x087c6600, 0x007c6804, + 0x04fc6a08, 0x04fc6c0a, 0xa0bc6236, 0x84bc6235, 0x28fc6202, 0x083c5a35, + 0x80fc6a04, 0xe4fc621d, 0x083c5a36, 0x80fc6c04, 0x083c6e36, 0x80fc6c04, + 0x083c7036, 0x0cfc6401, 0x60fc6407, 0x68bc5e32, 0x0c7c5e02, 0x00007fd8, + 0x00007fdc, 0x00007fd4, 0x00000072, 0x00000000, 0x00000000, 0x0007c010}; + +static int arg_list[] = {0, 0, 0, 0}; + +void spinix_enter(int argc, char **argv) +{ +} + +void spinix_exit(int retval) +{ + int i; + int cogspi = (*(int *)spinix_spi_engine_cog) - 1; + arg_list[2] = *(int *)spinix_shell_sector; + arg_list[3] = *(int *)spinix_shell_size; + + *(int *)spinix_return_value = retval; + + // Stop all the cogs except this one and the SD SPI cog + for (i = 0; i < 8; i++) + { + if (i != cogid() && i != cogspi) + cogstop(i); + } + + // Clear and return all the locks + for (i = 0; i < 8; i++) + { + lockclr(i); + lockret(i); + } + + // Start run_prog in this cog + coginit(cogid(), run_prog, arg_list); +} diff --git a/src/spinix.h b/src/spinix.h new file mode 100644 index 0000000..ba7d835 --- /dev/null +++ b/src/spinix.h @@ -0,0 +1,45 @@ +#define spinix_start 0x7c00 // Extra space for the stand-alone loader +#define spinix_rendezvous 0x7e50 +#define spinix_environ_vars 0x7e50 +#define spinix_environ_vars_end 0x7ed3 +#define spinix_argv_parms 0x7ed4 +#define spinix_return_value 0x7f94 +#define spinix_vga_cog 0x7f98 +#define spinix_vga_handle 0x7f9c +#define spinix_sd_pins 0x7fa0 +#define spinix_config 0x7fa4 +#define spinix_shell_sector 0x7fa8 +#define spinix_unixtime 0x7fac +#define spinix_cycle0 0x7fb0 +#define spinix_timezone 0x7fb4 +#define spinix_scriptline 0x7fb8 +#define spinix_ifflag 0x7fbc +#define spinix_whileflag 0x7fc0 +#define spinix_shell_size 0x7fc4 +#define spinix_shell_level 0x7fc8 +#define spinix_bootflag 0x7fcc +#define spinix_spi_engine_cog 0x7fd0 +#define spinix_spi_command 0x7fd4 +#define spinix_spi_block_index 0x7fd8 +#define spinix_spi_buffer_address 0x7fdc +#define spinix_serial 0x7fe0 +#define spinix_stdio 0x7fe4 +#define spinix_stdin 0x7fe8 +#define spinix_stdout 0x7fec +#define spinix_memlocknum 0x7ff0 +#define spinix_memfreelist 0x7ff4 +#define spinix_malloclist 0x7ff8 +#define spinix_laststackaddr 0x7ffc +#define spinix_checkword 0xdead1eaf +#define spinix_proc_type_spin 1 +#define spinix_proc_type_pasm 2 +#define spinix_proc_type_capp 3 +#define spinix_proc_type_driver 0x80 +#define spinix_run_shell_wait 0x00 +#define spinix_run_shell_nowait 0x08 +#define spinix_run_kill_caller 0x10 +#define spinix_run_at_address0 0x20 +#define spinix_run_c_program 0x40 +#define spinix_run_spin_program 0x80 +#define spinix_run_stand_alone 0x100 + diff --git a/src/spinsymb.txt b/src/spinsymb.txt new file mode 100644 index 0000000..6ebebd1 --- /dev/null +++ b/src/spinsymb.txt @@ -0,0 +1,139 @@ +7 0 0 0 +3 0 result 0 +0 1 clkfreq 0 +8 2 byte 0 +8 2 word 0 +8 2 long 0 +5 3 repeat 0 +5 4 while 0 +5 5 case 0 +5 6 if 0 +5 6 ifnot 0 +5 7 elseif 0 +5 7 elseifnot 0 +5 8 else 0 +5 9 quit 0 +5 10 next 0 +5 11 return 0 +262 12 - sub +262 12 + add +261 12 / div +261 12 * mul +261 12 ** mulh +261 12 // mod +263 12 #> minlim +263 12 <# maxlim +258 12 ~> sar +258 12 << shl +258 12 >> shr +258 12 -> ror +258 12 <- rol +258 12 >< rev +259 12 & and +260 12 | or +260 12 ^ xor +266 12 and andl +267 12 or orl +264 12 == cmpeq +264 12 <> cmpne +264 12 < cmplt +264 12 > cmpgt +264 12 =< cmple +264 12 => cmpge +518 13 -= sub +518 13 += add +517 13 /= div +517 13 *= mul +517 13 **= mulh +517 13 //= mod +519 13 #>= minlim +519 13 <#= maxlim +514 13 ~>= sar +514 13 <<= shl +514 13 >>= shr +514 13 ->= ror +514 13 <-= rol +514 13 ><= rev +515 13 &= and +516 13 |= or +516 13 ^= xor +522 13 and= andl +523 13 or= orl +520 13 === cmpeq +520 13 <>= cmpne +520 13 <= cmplt +520 13 >= cmpgt +520 13 =<= cmple +520 13 =>= cmpge +268 14 := 0 +257 15 ^^ sqrt +257 15 || abs +257 15 |< encode +257 15 >| decode +265 15 not notl +257 15 ! comp +256 16 @ 0 +513 17 ++ preinc-postinc +513 17 -- predec-postdec +513 17 ~ sexb-postclr +513 17 ~~ sexw-postset +513 17 ? randf-randr +0 18 ( 0 +0 19 ) 0 +0 19 ] 0 +0 19 , 0 +0 19 : 0 +1 24 string 0 +1 21 strsize 0 +1 21 strcomp 0 +1 21 bytemove 0 +1 21 wordmove 0 +1 21 longmove 0 +1 21 bytefill 0 +1 21 wordfill 0 +1 21 longfill 0 +1 21 cogid 0 +1 21 cognew 0 +1 21 coginit 0 +1 21 cogstop 0 +1 21 reboot 0 +1 21 locknew 0 +1 21 lockret 0 +1 21 lockclr 0 +1 21 lockset 0 +1 21 waitcnt 0 +1 21 waitpeq 0 +1 21 waitpne 0 +1 21 waitvid 0 +10 0 par 496 +10 0 cnt 497 +10 0 ina 498 +10 0 inb 499 +10 0 outa 500 +10 0 outb 501 +10 0 dira 502 +10 0 dirb 503 +10 0 ctra 504 +10 0 ctrb 505 +10 0 frqa 506 +10 0 frqb 507 +10 0 phsa 508 +10 0 phsb 509 +10 0 vcfg 510 +10 0 vscl 511 +4 0 false 0 +4 0 true 65535 +4 0 negx 0 +4 0 posx 65535 +4 0 pi 4059 +4 0 rcfast 1 +4 0 rcslow 2 +4 0 xinput 4 +4 0 xtal1 8 +4 0 xtal2 16 +4 0 xtal3 32 +4 0 pll1x 64 +4 0 pll2x 128 +4 0 pll4x 256 +4 0 pll8x 512 +4 0 pll16x 1024 diff --git a/src/splash.spin b/src/splash.spin new file mode 100644 index 0000000..42c7ee6 --- /dev/null +++ b/src/splash.spin @@ -0,0 +1,567 @@ +CON + _clkmode = xinput + _xinfreq = $4E505350 ' PSPN + DOUBLE_QUOTE = $22 + +OBJ + c : "clib_os" + sys : "sysdefs" + vs : "varsubs" + +PUB start(argc, argv) + main(argc, argv) + +VAR + byte buffer[100] + byte buffer2[100] + long tokens[25] + +DAT + datstr byte + nullstr long 0 + shell_level long 0 + ifflag long 0 + whileflag long 0 + skipflag long 0 + runmode long 0 + scriptfile long 0 + scriptline long 0 + scriptname long 0[20] + redirectin long 0 + redirectout long 0 + strbuf long 0[20] + strptr long 0 + retvalstr long 0[4] + CouldNotOpen byte "Could not open %s\n", 0 + RedirectionError byte "Redirection error\n", 0 + +' ################################################### + +PUB SaveState | outfile, ptr, ptr1, fname[5] + c.strcpy(@fname, string("/tmp/_shellva.rsa")) + byte[@fname][16] += shell_level + outfile := c.fopen(@fname, string("w")) + ifnot outfile + c.printf0(string("SaveState: Could not open file\n")) + return + c.fprintf1(outfile, string("%s\n"), @scriptname) + c.fprintf4(outfile, string("%d %d %d %d\n"), ifflag, whileflag, skipflag, scriptline) + ptr := sys#environ_vars + repeat while byte[ptr] + ptr1 := ptr + ptr += strsize(ptr) + 1 + c.fprintf2(outfile, string("%s %s\n"), ptr1, ptr) + ptr += strsize(ptr) + 1 + RemoveLocalVars + shell_level++ + c.fclose(outfile) + +PUB RemoveLocalVars | ptr + ptr := sys#environ_vars + repeat while byte[ptr] + if byte[ptr] == VS#GLOBAL_FLAG + ptr += strsize(ptr) + 1 + ptr += strsize(ptr) + 1 + else + vs.RemoveEntry(ptr) + +PUB InitializeScriptVars + shell_level := 0 + scriptfile := c.getstdin + c.strcpy(@scriptname, string("stdin")) + +PUB RestoreState | infile, ptr, fname[5] + ' Check if shell level less than or equal to zero -- zero happens at startup + if shell_level =< 0 + if shell_level + c.printf1(string("RestoreState: Error, shell_level = %d\n"), shell_level) + InitializeScriptVars + return + + ' Drop down a level + shell_level-- + c.strcpy(@fname, string("/tmp/_shellva.rsa")) + byte[@fname][16] += shell_level + ifnot (infile := c.fopen(@fname, string("r"))) + c.printf1(string("RestoreState: Error, could not open %s\n"), @fname) + InitializeScriptVars + return + c.fgets(@buffer, 100, infile) + byte[@buffer][strsize(@buffer)-1] := 0 + c.strcpy(@scriptname, @buffer) + c.fgets(@buffer, 100, infile) + c.sscanf4(@buffer, string("%d %d %d %d"), @ifflag, @whileflag, @skipflag, @scriptline) + 'c.printf5(string("RestoreState: %s %d %d %d %d\n"), @scriptname, ifflag, whileflag, skipflag, scriptline) + ptr := sys#environ_vars + repeat while c.fgets(ptr, 100, infile) + ptr := vs.FindChar(ptr, " ") + byte[ptr++] := 0 + ptr += strsize(ptr) - 1 + byte[ptr++] := 0 + byte[ptr] := 0 + c.fclose(infile) + + ' Open script file + if shell_level == 0 and strcomp(@scriptname, string("stdin")) + scriptfile := c.getstdin + elseif (scriptfile := c.fopen(@scriptname, string("r"))) + repeat scriptline + if c.fgets(@buffer, 100, scriptfile) =< 0 + quit + else + c.printf1(string("RestoreState: Error, could not open %s\n"), @scriptname) + InitializeScriptVars + +PUB CheckTest(argc, argv) + if argc > 1 + if strcomp(long[argv][1], string("[")) + long[argv][1] := string("/bin/test") + if argc > 2 + if strcomp(long[argv][argc-1], string("]")) + argc-- + return argc + +PUB AddParms(argc, argv) | i, numstr[3] + i := 1 + c.sprintf1(@numstr, string("%d"), argc - 1) + vs.SaveVar(string("#"), @numstr, vs#PARM_FLAG) + repeat while (i < argc) + c.sprintf1(@numstr, string("%d"), i) + vs.SaveVar(@numstr, long[argv][i], vs#PARM_FLAG) + i++ + +PUB main(argc, argv) | i, num, ptr, numstr[3], bootflag + + scriptline := 0 + ifflag := 0 + whileflag := 0 + skipflag := 0 + AddParms(argc, argv) + shell_level := 0 + bootflag := 0 + + if argc == 2 and strcomp(long[argv][1], string("boot")) + load_sysparm + if c.remove(string("/tmp/__reboot")) => 0 + c.strcpy(@scriptname, string("/tmp/_restart.spl")) + scriptfile := c.fopen(@scriptname, string("r")) + ifnot scriptfile + bootflag := 1 + i := string("/bin/splash0") + Execute(1, @i) + c.strcpy(@scriptname, string("/_startup")) + scriptfile := c.fopen(@scriptname, string("r")) + else + c.strcpy(@scriptname, string("/tmp/_restart.spl")) + scriptfile := c.fopen(@scriptname, string("r")) + + ifnot scriptfile + InitializeScriptVars + elseif bootflag + SaveState + + repeat + ' Close redirection files if opened + if redirectin + c.resetstdin + c.fclose(redirectin) + redirectin := 0 + if redirectout + c.resetstdout + c.fclose(redirectout) + redirectout := 0 + +{ + CheckAlias +} + + if (scriptfile == c.getstdin) + c.printf0(string("splash> ")) +{ + c.getcwd(@buffer, 100) + c.printf1(STRING("\n%s$ "), @buffer) + c.gets(@buffer) + if CheckBang + next +} + + if (not c.fgets(@buffer, 100, scriptfile)) + if (scriptfile == c.getstdin) + quit + c.fclose(scriptfile) + RestoreState + next + scriptline++ + if byte[@buffer][0] == "#" + next + RemoveCRLF(@buffer) + if (scriptline < skipflag) + next + if (scriptfile == c.getstdin) + WriteHistory + skipflag := 0 + num := tokenize(@buffer, @tokens) + expand(num) + 'num := Redirection(num, @tokens) + if (not num) + next + if (whileflag == - 1) + if (not c.strcmp(tokens[0], string("done"))) + whileflag := 0 + next + if (not c.strcmp(tokens[0], string("else"))) + ifflag := not ifflag + next + elseif (not c.strcmp(tokens[0], string("fi"))) + ifflag := 0 + next + elseif (ifflag) + next + ptr := vs.FindChar(tokens[0], "=") + if num == 1 and byte[ptr] + byte[ptr++] := 0 + vs.SaveVar(tokens[0], ptr, vs#LOCAL_FLAG) + elseif (not c.strcmp(tokens[0], string("exit"))) + if (scriptfile == c.getstdin) + quit + c.fclose(scriptfile) + RestoreState + elseif (not c.strcmp(tokens[0], string("setlev"))) + shell_level := vs.atol(tokens[1]) + elseif (not c.strcmp(tokens[0], string("if"))) + num := CheckTest(num, @tokens) + Execute(num-1, @tokens[1]) + elseif (not c.strcmp(tokens[0], string("then"))) + ifflag := long[sys#return_value] + elseif (not c.strcmp(tokens[0], string("while"))) + num := CheckTest(num, @tokens) + Execute(num-1, @tokens[1]) + elseif (not c.strcmp(tokens[0], string("do"))) + if long[sys#return_value] + whileflag := - 1 + else + whileflag := scriptline - 1 + elseif (not c.strcmp(tokens[0], string("done"))) + if (scriptfile <> c.getstdin and whileflag) + skipflag := whileflag + whileflag := 0 + scriptline := 0 + c.fseek(scriptfile, 0, c#SEEK_SET) + else + Execute(num, @tokens) == 1 + return 0 + +PUB RemoveCRLF(str) | len + len := strsize(str) + str += len - 1 + repeat while (len > 0) + if (byte[str] <> 10 and byte[str] <> 13) + quit + byte[str--] := 0 + len-- + +PUB FindBlank(str) | val + val := " " + repeat while byte[str] + if byte[str] == val + if val == " " + quit + val := " " + elseif byte[str] == DOUBLE_QUOTE + val := DOUBLE_QUOTE + str++ + return str + +PUB tokenize(str, tokens_0) | ptr, num + num := 0 + repeat while (byte[str]) + ptr := vs.SkipChar(str, " ") + if (byte[ptr] == 0) + quit + str := FindBlank(str) + if (byte[str]) + byte[str++] := 0 + long[tokens_0][num++] := ptr + return num + +PUB GetVarName(ptr1, ptr2) | brace, val + if byte[ptr1] == "#" or byte[ptr1] == "?" + byte[ptr2++] := byte[ptr1++] + byte[ptr2] := 0 + return ptr1 + brace := 0 + if byte[ptr1] == "{" + brace := 1 + ptr1++ + repeat while (val := byte[ptr1]) + ifnot (c.isdigit(val) or vs.IsAlpha(val)) + if val == "}" and brace + ptr1++ + quit + byte[ptr2++] := byte[ptr1++] + byte[ptr2] := 0 + return ptr1 + +PUB expand(num) | i, ptr1, ptr2, ptr3, len, varname[20] + i := 0 + ptr2 := @buffer2 + repeat while i < num + ptr1 := tokens[i] + tokens[i] := ptr2 + repeat while byte[ptr1] + if byte[ptr1] == DOUBLE_QUOTE + ptr1++ + next + if byte[ptr1] == "$" + ptr1 := GetVarName(ptr1 + 1, @varname) + ptr3 := vs.GetVarVal(@varname) + len := strsize(ptr3) + bytemove(ptr2, ptr3, len) + ptr2 += len + next + if byte[ptr1] == "\" + ifnot byte[++ptr1] + quit + 'c.printf1(string("Copy %c\n"), byte[ptr1]) + byte[ptr2++] := byte[ptr1++] + byte[ptr2++] := 0 + i++ + +PUB Execute(argc, argv) | i, pathstr[20] + strptr := @strbuf + + ifnot (i := FindExecTarget(long[argv][0], @pathstr)) + return i + + if i == 1 + if scriptfile <> c.getstdin + c.fclose(scriptfile) + SaveState + scriptfile := c.fopen(@pathstr, string("r")) + scriptline := 0 + if byte[@pathstr] <> "/" + c.getcwd(@scriptname, 50) + c.strcat(@scriptname, string("/")) + else + scriptname := 0 + c.strcat(@scriptname, @pathstr) + AddParms(argc, argv) + ifnot scriptfile + i := -1 + c.printf1(@CouldNotOpen, @pathstr) + { + else + if argc > 1 + c.sscanf1(long[argv][1], string("%d"), @scriptline) + repeat scriptline + if c.fgets(@buffer, 100, scriptfile) =< 0 + quit + } + elseif i := run(@pathstr, argc, argv, runmode) + if i == -2 + c.printf1(string("Not enough memory to run %s\n"), @pathstr) + else + c.printf1(@CouldNotOpen, @pathstr) + return i + +PUB CheckMemoryAvail(size) + if runmode & sys#run_kill_caller + return + if redirectin or redirectout + return + if (result := c.malloc(size + 900)) + c.free(result) + else + runmode |= sys#run_kill_caller | cogid + +PUB FindExecTarget(fname, pathstr) | size, temp, infile, typestr[8] + c.strcpy(pathstr, fname) + repeat 2 + if (infile := c.fopen(pathstr, string("r"))) + quit + temp := vs.FindChar(pathstr, "/") + if byte[temp] + quit + c.strcpy(pathstr, string("/bin/")) + c.strcat(pathstr, fname) + + ifnot infile + c.printf1(@CouldNotOpen, fname) + return + + longfill(@typestr, 0, 8) + c.fread(@typestr, 1, 32, infile) + c.fseek(infile, 0, c#SEEK_END) + size := c.ftell(infile) + c.fclose(infile) + + if typestr == $4e495053 ' SPIN + size := word[@typestr][5] ' The Spin program size is determined by dbase + 'c.printf0(string("Spin Program Detected\n")) + CheckMemoryAvail(size) + result := 2 + elseif typestr == $50504143 ' CAPP + 'c.printf0(string("C App Detected\n")) + runmode |= sys#run_c_program | cogid + CheckMemoryAvail(size) + result := 4 + elseif typestr == $4F525053 ' SPRO + 'c.printf0(string("Spin Program Detected\n")) + runmode := sys#run_spin_program | sys#run_at_address0 | sys#run_stand_alone | cogid + result := 5 + elseif typestr == $4F525043 ' CPRO + 'c.printf0(string("C Program Detected\n")) + runmode := sys#run_c_program | sys#run_stand_alone | cogid + result := 6 + elseif typestr => 80_000_000 and typestr =< 120_000_000 + ifnot runmode + 'c.printf0(string("Stand-alone Program Detected\n")) + runmode := sys#run_at_address0 | sys#run_stand_alone | cogid + result := 3 + elseifnot c.strncmp(@typestr, string("#shell"), 6) + result := 1 + else + c.printf1(string("%s is not an executable file\n"), fname) + +PUB AddToCogTable(cognum, type, name, startaddr, size) | entryptr + entryptr := sys#cogtable + (cognum * 24) + byte[entryptr] := type + bytemove(entryptr+1, name, 19) + byte[entryptr+19] := 0 + word[entryptr+20] := startaddr + word[entryptr+22] := size + +PUB RemoveFromCogTable(cognum) | entryptr + entryptr := sys#cogtable + (cognum * 24) + byte[entryptr] := 0 + +PUB Redirection(argc, argv) | i, argc1, ptr, char, mode + runmode := sys#run_shell_wait + if (argc < 2) + return argc + argc1 := 1 + repeat i from 1 to argc - 1 + ptr := long[argv][i] + char := byte[ptr++] + if char == "<" + if (byte[ptr] == 0) + ptr := long[argv][++i] + if (i => argc) + c.printf0(@RedirectionError) + argc1 := 0 + quit + redirectin := c.fopen(ptr, string("r")) + ifnot redirectin + c.printf1(@CouldNotOpen, ptr) + argc1 := 0 + quit + else + c.setstdin(redirectin) + elseif char == ">" + if (byte[ptr] == ">") + ptr++ + mode := string("a") + else + mode := string("w") + if (byte[ptr] == 0) + ptr := long[argv][++i] + if (i => argc) + c.printf0(@RedirectionError) + argc1 := 0 + quit + redirectout := c.fopen(ptr, mode) + ifnot redirectout + c.printf1(@CouldNotOpen, ptr) + argc1 := 0 + quit + else + c.setstdout(redirectout) + elseif char == "&" + if byte[ptr] == "&" ' Kill shell + ptr++ + runmode := sys#run_kill_caller | cogid + elseif byte[ptr] == "^" ' Shell waits + ptr++ + elseif byte[ptr] == "*" ' Run stand-alone + ptr++ + runmode := sys#run_stand_alone | cogid + else ' Shell does not wait + runmode := sys#run_shell_nowait + if byte[ptr] == "0" ' Run at location 0 + runmode |= sys#run_at_address0 + elseif byte[ptr] == "c" ' Run C program at location 0 + runmode |= sys#run_c_program + else + long[argv][argc1++] := argv[i] + + ' Check if redirecting and killing shell or stand-alone or no wait + if redirectin or redirectout + if runmode & (sys#run_kill_caller | sys#run_stand_alone | sys#run_shell_nowait) + c.printf0(string("Cannot redirect stdio with this run mode\n")) + argc1 := 0 + + return argc1 + +PUB run(fname, argc, pargv, mode) | cognum, entryptr, outfile + 'c.printf1(string("runmode = %2.2x\n"), runmode) + mode |= cogid + if mode & (sys#run_kill_caller | sys#run_stand_alone) + if mode & sys#run_stand_alone + save_sysparm + outfile := c.fopen(string("/tmp/__reboot"), string("w")) + if outfile + c.fclose(outfile) + SaveState + outfile := c.fopen(string("/tmp/_restart.spl"), string("w")) + if outfile + 'if mode & $08 + ' c.fprintf0(outfile, string("#standalone\n")) + c.fprintf1(outfile, string("config %x\n"), long[sys#config]) + c.getcwd(strptr, 50) + c.fprintf1(outfile, string("cd %s\n"), strptr) + c.fprintf1(outfile, string("setlev %d\n"), shell_level) + 'PrintAlias(outfile) + 'if scriptfile + 'c.fprintf2(outfile, string("%s %d\n"), @scriptname, scriptline) + c.fclose(outfile) + long[pargv] := fname + if mode & sys#run_c_program + 'c.printf0(string("shell: calling runc\n")) + cognum := c.runc(argc, pargv, mode) + else + cognum := c.run(argc, pargv, mode) + if cognum < 0 + return cognum + entryptr := sys#cogtable + (cognum * 24) + ifnot (mode & sys#run_shell_nowait) + repeat while byte[entryptr] + +pub load_sysparm | infile, ptr + infile := c.fopen(string("/_sysparm"), string("r")) + ifnot infile + c.printf0(string("Could not open _sysparm\n")) + return + ptr := sys#unixtime + repeat 3 + ifnot c.fgets(@buffer, 100, infile) + quit + c.sscanf1(@buffer, string("%d"), ptr) + 'c.printf2(string("Location $%4.4x = %d\n"), ptr, long[ptr]) + ptr += 4 + c.fclose(infile) + +pub save_sysparm | outfile, ptr + outfile := c.fopen(string("/_sysparm"), string("w")) + ptr := sys#unixtime + repeat 3 + c.fprintf1(outfile, string("%d\n"), long[ptr]) + ptr += 4 + c.fclose(outfile) + +pub WriteHistory | outfile, ptr + ptr := vs.SkipChar(@buffer, " ") + if byte[ptr] + if (outfile := c.fopen(string("/_history"), string("a"))) + c.fprintf1(outfile, string("%s\n"), @buffer) + c.fclose(outfile) + diff --git a/src/splash0.spin b/src/splash0.spin new file mode 100644 index 0000000..ad8e954 --- /dev/null +++ b/src/splash0.spin @@ -0,0 +1,36 @@ +CON + _clkmode = xinput + _xinfreq = $4E505350 ' PSPN + +OBJ + ut : "unixtime" + c : "clib_os" + sys : "sysdefs" + +con + CYCLE0 = sys#cycle0 + SECONDS = sys#unixtime + TIMEZONE = sys#timezone + +pub main(argc, argv) | unixtime, date, time, hour, minute, second, year, month, day, cycle, temp, tz + tz := long[TIMEZONE] + unixtime := long[SECONDS] + date := ut.date(unixtime, tz) + time := ut.time(unixtime, tz) + year := word[@date][1] + month := byte[@date][1] + day := byte[@date][0] + hour := word[@time][1] + minute := byte[@time][1] + second := byte[@time][0] + c.printf0(string("******************************************************\n")) + c.printf0(string("* *\n")) + c.printf0(string("* SPINIX 1.17 October 3, 2013 *\n")) + c.printf0(string("* *\n")) + c.printf0(string("******************************************************\n")) + c.printf0(string("\n")) + c.printf0(string("Current date and time: ")) + c.printf3(string("%2.2d/%2.2d/%2.2d "), year, month, day) + c.printf3(string("%2.2d:%2.2d:%2.2d\n"),hour, minute, second) + c.printf0(string("\n")) + diff --git a/src/splink.spin b/src/splink.spin new file mode 100644 index 0000000..66b83e8 --- /dev/null +++ b/src/splink.spin @@ -0,0 +1,199 @@ +'********************************************** +'* Spin code generated by cspin version 0.68 * +'********************************************** + + +CON + _clkmode = xinput + _xinfreq = $4e495053 ' SPIN + + MAX_OBJECTS = 20 + +OBJ + c : "clibsd" + strsub : "strsubs" + +PUB start(argc, argv) + c.enter + strsub.start + main(argc, argv) + waitcnt(clkfreq/10+cnt) + c.exit(0) + +DAT + debugflag long 0 + +PUB usage + c.printf0(string("usage: splink file1 file2 file3 [-d]\n")) + c.exit(1) + +PUB OpenFile(fname, mode) | fileptr + fileptr := c.fopen(fname, mode) + if (not fileptr) + c.printf1(string("Could not open %s\n"), fname) + c.exit(1) + return fileptr + +VAR + long topmets + long topobjs + long numobjs + +DAT + offset long 0 + +VAR + word objoffs[MAX_OBJECTS] + +CON +' typedef struct SpinHeaderS +clockfreq = 0 +mode_0 = 4 +checksum = 8 +pbase = 12 +vbase = 16 +dbase = 20 +pcurr = 24 +dcurr = 28 + +VAR + word buf[512] + long header1[8] + long header2[8] + +PUB PrintHeader(header) + c.printf3(string("clkfreq = %d, mode = %d, checksum = %d\n"), long[header + clockfreq], long[header + mode_0], long[header + checksum]) + c.printf5(string("pbase = %d, vbase = %d, dbase = %d, pcurr = %d, dcurr = %d\n"), long[header + pbase], long[header + vbase], long[header + dbase], long[header + pcurr], long[header + dcurr]) + +PUB GetSpinHeader(infile, header) | buf_0[4] + c.fread(@buf_0, 1, 16, infile) + long[header + clockfreq] := buf_0[0] + long[header + mode_0] := buf_0[1] & 255 + long[header + checksum] :=(buf_0[1] >> 8) & 255 + long[header + pbase] := buf_0[1] >> 16 + long[header + vbase] := buf_0[2] & $ffff + long[header + dbase] := buf_0[2] >> 16 + long[header + pcurr] := buf_0[3] & $ffff + long[header + dcurr] := buf_0[3] >> 16 + if (debugflag) + PrintHeader(header) + +PUB ComputeCheckSum(ptr, num) | i, checksum_0 + checksum_0 := 0 + i := 0 + repeat while (i < num) + checksum_0 += byte[ptr++] + i++ + return checksum_0 & 255 + +PUB WriteHeader(outfile, header) | buf_0[4] + word[@buf_0][0] := long[header + clockfreq] + word[@buf_0][1] := long[header + clockfreq] >> 16 + word[@buf_0][2] :=(long[header + checksum] << 8) | long[header + mode_0] + word[@buf_0][3] := long[header + pbase] + word[@buf_0][4] := long[header + vbase] + word[@buf_0][5] := long[header + dbase] + word[@buf_0][6] := long[header + pcurr] + word[@buf_0][7] := long[header + dcurr] + c.fwrite(@buf_0, 1, 16, outfile) + +PUB CopyFile(infile, outfile, linkflag) | i, j, num + repeat while ((num := c.fread(@buf, 1, 512, infile)) > 0) + if (linkflag) + linkflag := 0 + i := buf[1] & 255 + if (debugflag) + c.printf1(string("CopyFile: %d methods\n"), i) + j := 0 + repeat while (j < topobjs) + if (debugflag) + c.printf2(string("CopyFile: Change %d to %d\n"), buf[i << 1], objoffs[j]) + buf[i << 1] := objoffs[j] + i++ + j++ + if (debugflag) + i := 0 + repeat while (i < 14) + c.printf1(string("%4.4x "), buf[i]) + i++ + c.printf0(string("\n")) + c.fwrite(@buf, 1, num, outfile) + +PUB GetObjectInfo(infile, top) | objnum, num, nummet, numobj, objsize + objnum := 0 + repeat while ((num := c.fread(@buf, 1, 4, infile)) > 0) + nummet := buf[1] & 255 + numobj := buf[1] >> 8 + objsize := buf[0] + if (debugflag) + c.printf5(string("GetObjInfo: obj %3d, methods = %3d, objects = %3d, size = %5d, offset = %5d\n"), objnum, nummet, numobj, objsize, offset) + objnum++ + if (top) + if (objnum > 1) + c.printf0(string("Top object contains more than one object\n")) + c.exit(1) + topmets := nummet + topobjs := numobj + else + if (objnum > MAX_OBJECTS) + c.printf0(string("Too many objects\n")) + c.exit(1) + numobjs := objnum + objoffs[objnum - 1] := offset + offset += objsize + objsize -= 4 + repeat while (objsize > 0) + num := 512 + if (objsize < 512) + num := objsize + num := c.fread(@buf, 1, num, infile) + if (num =< 0) + c.printf0(string("Object too small\n")) + c.exit(1) + objsize -= num + c.fseek(infile, 16, c#SEEK_SET) + +PUB main(argc, argv) | i, infile1, infile2, outfile, psize1, psize2, vsize2, checksum_old, checksum_new + if (argc == 5 and not c.strcmp(long[argv][4], string("-d"))) + debugflag := 1 + elseif (argc <> 4) + usage + infile1 := OpenFile(long[argv][1], string("r")) + infile2 := OpenFile(long[argv][2], string("r")) + outfile := OpenFile(long[argv][3], string("w")) + GetSpinHeader(infile1, @header1) + GetSpinHeader(infile2, @header2) + checksum_old := ComputeCheckSum(@header1, 32) - $14 + checksum_old += ComputeCheckSum(@header2, 32) - $14 + checksum_old &= 255 + if (debugflag) + c.printf1(string("Header checksum = %d\n"), checksum_old) + psize1 := long[@header1 + vbase] - long[@header1 + pbase] + psize2 := long[@header2 + vbase] - long[@header2 + pbase] + vsize2 := long[@header2 + dbase] - long[@header2 + vbase] - 8 + if (debugflag) + c.printf2(string("psize2 = %d, vsize2 = %d\n"), psize2, vsize2) + c.printf1(string("psize1 %d\n"), psize1) + long[@header1 + vbase] += psize2 + long[@header1 + dbase] += psize2 + vsize2 + long[@header1 + dcurr] += psize2 + vsize2 + GetObjectInfo(infile1, 1) + GetObjectInfo(infile2, 0) + checksum_new := ComputeCheckSum(@header1, 32) + if (debugflag) + c.printf1(string("New checksum = %d\n"), checksum_new) + ' Update the checksum + i := 0 + repeat while (i < topobjs) + checksum_new += ComputeCheckSum(@objoffs[i], 2) + i++ + long[@header1 + checksum] :=(long[@header1 + checksum] -(checksum_new - checksum_old) + $14) & 255 + if (debugflag) + PrintHeader(@header1) + WriteHeader(outfile, @header1) + CopyFile(infile1, outfile, 1) + CopyFile(infile2, outfile, 0) + c.fclose(infile1) + c.fclose(infile2) + c.fclose(outfile) + return 0 diff --git a/src/stdout.spin b/src/stdout.spin new file mode 100644 index 0000000..e83dbb8 --- /dev/null +++ b/src/stdout.spin @@ -0,0 +1,60 @@ +'****************************************************************************** +' Copyright (c) 2011, 2012, Dave Hein +' See end of file for terms of use. +'****************************************************************************** +CON + _clkmode = xinput + _xinfreq = $4e495053 ' SPIN + +OBJ + c : "clibsd" + sys : "sysdefs" + +PUB main(argc, argv) | stdout + + c.enter + + if argc <> 2 + usage + + stdout := long[sys#stdout] + + if strcomp(long[argv][1], string("serial")) + long[stdout][0] := c#stream_serial + long[stdout][1] := long[sys#serial] + elseif strcomp(long[argv][1], string("display")) + long[stdout][0] := c#stream_display + long[stdout][1] := long[sys#vga_handle] + else + usage + + c.exit(0) + +PUB usage + c.printf0(string("usage: stdout parm\n")) + c.printf0(string(" parm is either serial or display\n")) + c.exit(1) + +{{ ++-----------------------------------------------------------------------------+ +| TERMS OF USE: MIT License | ++-----------------------------------------------------------------------------+ +|Permission is hereby granted, free of charge, to any person obtaining a copy | +|of this software and associated documentation files (the "Software"), to deal| +|in the Software without restriction, including without limitation the rights | +|to use, copy, modify, merge, publish, distribute, sublicense, and/or sell | +|copies of the Software, and to permit persons to whom the Software is | +|furnished to do so, subject to the following conditions: | +| | +|The above copyright notice and this permission notice shall be included in | +|all copies or substantial portions of the Software. | +| | +|THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | +|IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | +|FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE | +|AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | +|LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,| +|OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE| +|SOFTWARE. | ++-----------------------------------------------------------------------------+ +}} diff --git a/src/strsubs.spin b/src/strsubs.spin new file mode 100644 index 0000000..112dc16 --- /dev/null +++ b/src/strsubs.spin @@ -0,0 +1,147 @@ +'********************************************** +'* Spin code generated by cspin version 0.066 * +'********************************************** + +OBJ + c : "clibsd" + +CON + DOUBLE_QUOTE = $22 + SINGLE_QUOTE = $27 + TAB = 9 + +DAT + zero_byte byte 0 + +PUB toupper(val) + if (val => "a" and val =< "z") + val -= 32 + return val + +PUB StrCompNoCase(str1, str2) + repeat while (byte[str1] and toupper(byte[str1]) == toupper(byte[str2])) + str1++ + str2++ + return byte[str1] == byte[str2] + +PUB FindChar(str, val) + repeat while (byte[str]) + if (byte[str] == val) + quit + str++ + return str + +PUB SkipChars(str1, str2) | str3 + repeat while (byte[str1]) + str3 := FindChar(str2, byte[str1]) + if (byte[str3] == 0) + quit + str1++ + return str1 + +PUB FindChars(str1, str2) | str3 + repeat while (byte[str1]) + str3 := FindChar(str2, byte[str1]) + if (byte[str3]) + quit + str1++ + return str1 + +PUB FindStr(str1, str2) | len + len := strsize(str2) + repeat while (byte[str1]) + if (c.strncmp(str1, str2, len) == 0) + quit + str1++ + return str1 + +DAT + datstr byte "++", 0, "+=", 0, "+", 0, "--", 0, "-=", 0 + byte "-", 0, ">>=", 0, ">>", 0, ">-=", 0, ">-", 0, "><=", 0 + byte "><", 0, ">|", 0, ">=", 0, ">", 0, "<<=", 0, "<<", 0 + byte "<-=", 0, "<-", 0, "<#=", 0, "<#", 0, "<>=", 0, "<>", 0 + byte "<=", 0, "<", 0, "**=", 0, "**", 0, "*=", 0, "*", 0 + byte "//=", 0, "//", 0, "/=", 0, "/", 0, "||", 0, "|<", 0 + byte "|=", 0, "|", 0, "@@", 0, "@", 0, "#>=", 0, "#>", 0 + byte "^^", 0, "^=", 0, "^", 0, "=>=", 0, "=>", 0, "=<=", 0 + byte "=<", 0, "!", 0, "~~", 0, "~>=", 0, "~>", 0, "~", 0 + byte "?", 0, "===", 0, "==", 0 + ops long @datstr + 16, @datstr + 19, @datstr + 22, @datstr + 24 + long @datstr + 27, @datstr + 30, @datstr + 32, @datstr + 36 + long @datstr + 39, @datstr + 43, @datstr + 46, @datstr + 50 + long @datstr + 53, @datstr + 56, @datstr + 59, @datstr + 61 + long @datstr + 65, @datstr + 68, @datstr + 72, @datstr + 75 + long @datstr + 79, @datstr + 82, @datstr + 86, @datstr + 89 + long @datstr + 92, @datstr + 94, @datstr + 98, @datstr + 101 + long @datstr + 104, @datstr + 106, @datstr + 110, @datstr + 113 + long @datstr + 116, @datstr + 118, @datstr + 121, @datstr + 124 + long @datstr + 127, @datstr + 129, @datstr + 132, @datstr + 134 + long @datstr + 138, @datstr + 141, @datstr + 144, @datstr + 147 + long @datstr + 149, @datstr + 153, @datstr + 156, @datstr + 160 + long @datstr + 163, @datstr + 165, @datstr + 168, @datstr + 172 + long @datstr + 175, @datstr + 177, @datstr + 179, @datstr + 183 + long 0 + +PUB start | ptr + ptr := @ops + repeat while long[ptr] + long[ptr] += @@0 - 16 + ptr += 4 + +PUB FindList(str, list) | i + i := 0 + repeat while (long[list][i]) + if (c.strncmp(str, long[list][i], strsize(long[list][i])) == 0) + return i + i++ + return - 1 + +PUB FindListNoCase(str, list) | i + i := 0 + repeat while (long[list][i]) + if (StrCompNoCase(str, long[list][i])) + return i + i++ + return - 1 + +PUB GetTokens(str, str2, tokens) | len, num, str1, j + num := 0 + repeat while (byte[str]) + str := SkipChars(str, string(" ", TAB)) + if (byte[str] == 0) + quit + if (byte[str] == SINGLE_QUOTE or byte[str] == "{") + len := strsize(str) + str1 := str + len + elseif (byte[str] == DOUBLE_QUOTE) + str1 := FindChar(str + 1, DOUBLE_QUOTE) + if (byte[str1]) + str1++ + len := str1 - str + elseif ((j := FindList(str, @ops)) => 0) + len := strsize(ops[j]) + str1 := str + len + else + str1 := FindChars(str, string(" ", TAB, "()[]@|,+-<>#*/!^~?")) + if (str == str1) + str1++ + len := str1 - str + 'printf("len = %d\n", len); + c.memcpy(str2, str, len) + byte[str2][len] := 0 + long[tokens][num++] := str2 + 'printf("tokens[%d] = %s\n", num-1, str2); + str2 += len + 1 + str += len + if (num > 1 and(byte[long[tokens][num - 1]][0] == SINGLE_QUOTE or byte[long[tokens][num - 1]][0] == "{")) + num-- + return num + +PUB RemoveCRLF(str) | len + len := strsize(str) + str += len - 1 + repeat while (len > 0) + if (byte[str] <> 10 and byte[str] <> 13) + quit + byte[str--] := 0 + len-- diff --git a/src/sysdefs.spin b/src/sysdefs.spin new file mode 100644 index 0000000..61fe3ca --- /dev/null +++ b/src/sysdefs.spin @@ -0,0 +1,112 @@ +'****************************************************************************** +' Copyright (c) 2011, 2012, Dave Hein +' See end of file for terms of use. +'****************************************************************************** +CON + 'start = $7e50 ' Extra space for the stand-alone loader + start = $7c00 ' Extra space for the stand-alone loader + + ' SD CLIB data + rendezvous = $7e50 + + ' Exported variables + environ_vars = $7e50 + environ_vars_end = $7ed3 + + ' Argv parameter area + argv_parms = $7ed4 + + ' Additional system parameters + return_value = $7f94 + vga_cog = $7f98 + vga_handle = $7f9c + sd_pins = $7fa0 + config = $7fa4 + shell_sector = $7fa8 + + ' System time + unixtime = $7fac + cycle0 = $7fb0 + timezone = $7fb4 +{ + ' I2C Driver + i2c_cog = $7fb8 + i2c_cmd = $7fbc + i2c_parm = $7fc0 + + ' Kernel data + filelock = $7fc4 + filecmd = $7fc8 + fileparm = $7fcc +} + 'Shell variables + scriptline = $7fb8 + ifflag = $7fbc + whileflag = $7fc0 + shell_size = $7fc4 + shell_level = $7fc8 + bootflag = $7fcc + + 'File I/O + spi_engine_cog = $7fd0 + spi_command = $7fd4 + spi_block_index = $7fd8 + spi_buffer_address = $7fdc + + ' Basic CLIB data + ' Serial data + serial = $7fe0 + stdio = $7fe4 + stdin = $7fe8 + stdout = $7fec + + ' Malloc data + memlocknum = $7ff0 + memfreelist = $7ff4 + malloclist = $7ff8 + laststackaddr = $7ffc + + ' Stack check word + checkword = $dead1eaf + + ' Process types + proc_type_spin = 1 + proc_type_pasm = 2 + proc_type_capp = 3 + proc_type_driver = $80 + + ' Run modes + run_shell_wait = $00 + run_shell_nowait = $08 + run_kill_caller = $10 + run_at_address0 = $20 + run_c_program = $40 + run_spin_program = $80 + run_stand_alone = $100 + +PUB main + return + +{{ ++-----------------------------------------------------------------------------+ +| TERMS OF USE: MIT License | ++-----------------------------------------------------------------------------+ +|Permission is hereby granted, free of charge, to any person obtaining a copy | +|of this software and associated documentation files (the "Software"), to deal| +|in the Software without restriction, including without limitation the rights | +|to use, copy, modify, merge, publish, distribute, sublicense, and/or sell | +|copies of the Software, and to permit persons to whom the Software is | +|furnished to do so, subject to the following conditions: | +| | +|The above copyright notice and this permission notice shall be included in | +|all copies or substantial portions of the Software. | +| | +|THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | +|IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | +|FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE | +|AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | +|LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,| +|OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE| +|SOFTWARE. | ++-----------------------------------------------------------------------------+ +}} diff --git a/src/t b/src/t new file mode 100644 index 0000000..6e3303f --- /dev/null +++ b/src/t @@ -0,0 +1,41 @@ +'****************************************************************************** +' Copyright (c) 2013, Dave Hein +' See end of file for terms of use. +'****************************************************************************** + +pub save_sysparm | outfile, ptr + outfile := c.fopen(string("/_sysparm"), string("w")) + ifnot outfile + c.printf0(string("Can't open /_sysparm\n")) + return + c.fprintf1(outfile, string("%d\n"), long[sys#unixtime]) + c.fprintf2(outfile, string("%d %d 0\n"), long[sys#timezone], long[sys#config]) + c.fprintf0(outfile, string("0 0 0\n")) + c.fprintf0(outfile, string("GPWD /\n")) + c.fprintf0(outfile, string("LSCRIPT_FILE /bin/_shellrc\n")) + c.fprintf0(outfile, string("P# 0\n")) + c.fclose(outfile) + +{{ ++-----------------------------------------------------------------------------+ +| TERMS OF USE: MIT License | ++-----------------------------------------------------------------------------+ +|Permission is hereby granted, free of charge, to any person obtaining a copy | +|of this software and associated documentation files (the "Software"), to deal| +|in the Software without restriction, including without limitation the rights | +|to use, copy, modify, merge, publish, distribute, sublicense, and/or sell | +|copies of the Software, and to permit persons to whom the Software is | +|furnished to do so, subject to the following conditions: | +| | +|The above copyright notice and this permission notice shall be included in | +|all copies or substantial portions of the Software. | +| | +|THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | +|IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | +|FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE | +|AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | +|LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,| +|OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE| +|SOFTWARE. | ++-----------------------------------------------------------------------------+ +}} diff --git a/src/t.spin b/src/t.spin new file mode 100644 index 0000000..9bf2d28 --- /dev/null +++ b/src/t.spin @@ -0,0 +1,50 @@ +'****************************************************************************** +' Copyright (c) 2011, 2012, Dave Hein +' See end of file for terms of use. +'****************************************************************************** +CON + _clkmode = xinput + _xinfreq = $4e495053 ' SPIN + +OBJ + c : "clibsd" + +PUB main(argc, argv) + c.enter + c.printf1(string("rcfast = %2.2x\n"), rcfast) + c.printf1(string("rcslow = %2.2x\n"), rcslow) + c.printf1(string("xinput = %2.2x\n"), xinput) + c.printf1(string("xtal1 = %2.2x\n"), xtal1) + c.printf1(string("xtal2 = %2.2x\n"), xtal2) + c.printf1(string("xtal3 = %2.2x\n"), xtal3) + c.printf1(string("pll1x = %2.2x\n"), pll1x) + c.printf1(string("pll2x = %2.2x\n"), pll2x) + c.printf1(string("pll4x = %2.2x\n"), pll4x) + c.printf1(string("pll8x = %2.2x\n"), pll8x) + c.printf1(string("pll16x = %2.2x\n"), pll16x) + waitcnt(clkfreq/100+cnt) + c.exit(0) + +{{ ++-----------------------------------------------------------------------------+ +| TERMS OF USE: MIT License | ++-----------------------------------------------------------------------------+ +|Permission is hereby granted, free of charge, to any person obtaining a copy | +|of this software and associated documentation files (the "Software"), to deal| +|in the Software without restriction, including without limitation the rights | +|to use, copy, modify, merge, publish, distribute, sublicense, and/or sell | +|copies of the Software, and to permit persons to whom the Software is | +|furnished to do so, subject to the following conditions: | +| | +|The above copyright notice and this permission notice shall be included in | +|all copies or substantial portions of the Software. | +| | +|THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | +|IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | +|FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE | +|AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | +|LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,| +|OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE| +|SOFTWARE. | ++-----------------------------------------------------------------------------+ +}} diff --git a/src/tail.spin b/src/tail.spin new file mode 100644 index 0000000..c8059b1 --- /dev/null +++ b/src/tail.spin @@ -0,0 +1,99 @@ +'****************************************************************************** +' Copyright (c) 2012, Dave Hein +' See end of file for terms of use. +'****************************************************************************** +CON + _clkmode = xinput + _xinfreq = $4e495053 ' SPIN + +OBJ + c : "clibsd" + +PUB main(argc, argv) | infile, outfile, numlines, totlines, ptr, buffer[25] + + c.enter + + infile := 0 + numlines := 10 + ptr := long[argv][1] + + ' Parse the command-line arguments + if argc < 2 + infile := c.getstdin + elseif argc > 3 + usage + elseif byte[ptr] == "-" + ifnot c.isdigit(byte[++ptr]) + usage + c.sscanf1(ptr, string("%d"), @numlines) + if numlines =< 0 + usage + if argc == 2 + infile := c.getstdin + else + ptr := long[argv][2] + elseif argc == 2 + ptr := long[argv][1] + else + usage + + ' Return if stdin selected + if infile + c.exit(0) + + ' Open the input file if not stdin + infile := c.fopen(ptr, string("r")) + if (infile == 0) + c.printf1(string("Error opening %s\n"), ptr) + c.exit(0) + + outfile := c.getstdout + + ' Count the number of lines in the file + totlines := 0 + repeat while c.fgets(@buffer, 100, infile) > 0 + totlines++ + + ' Rewind the input file and skip the (totlines - numlines) lines + c.fseek(infile, 0, c#SEEK_SET) + if totlines > numlines + repeat totlines - numlines + c.fgets(@buffer, 100, infile) + + ' Print the tail + repeat numlines + if c.fgets(@buffer, 100, infile) =< 0 + quit + c.fputs(@buffer, outfile) + + if (infile <> c.getstdin) + c.fclose(infile) + c.exit(0) + +PUB usage + c.printf0(string("usage: tail [-n] file\n")) + c.exit(0) + +{{ ++-----------------------------------------------------------------------------+ +| TERMS OF USE: MIT License | ++-----------------------------------------------------------------------------+ +|Permission is hereby granted, free of charge, to any person obtaining a copy | +|of this software and associated documentation files (the "Software"), to deal| +|in the Software without restriction, including without limitation the rights | +|to use, copy, modify, merge, publish, distribute, sublicense, and/or sell | +|copies of the Software, and to permit persons to whom the Software is | +|furnished to do so, subject to the following conditions: | +| | +|The above copyright notice and this permission notice shall be included in | +|all copies or substantial portions of the Software. | +| | +|THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | +|IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | +|FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE | +|AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | +|LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,| +|OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE| +|SOFTWARE. | ++-----------------------------------------------------------------------------+ +}} diff --git a/src/tar.spin b/src/tar.spin new file mode 100644 index 0000000..b58cd2d --- /dev/null +++ b/src/tar.spin @@ -0,0 +1,175 @@ +'********************************************** +'* Spin code generated by cspin version 0.060 * +'********************************************** +'****************************************************************************** +' Copyright (c) 2011, 2012, Dave Hein +' See end of file for terms of use. +'****************************************************************************** + +CON + _clkmode = xinput + _xinfreq = $4e495053 ' SPIN + +OBJ + c : "clibsd" + +CON + NAME = 0 + MODE = 100 + OWNER = 108 + GROUP = 116 + SIZE = 124 + TIME = 136 + CHECKSUM = 148 + FILETYPE = 156 + LINKEDFILE = 157 + +DAT + buffer byte 0[512] + +PUB main(argc, argv) + c.enter + if (argc < 3) + usage + if (c.strcmp(long[argv][1], string("-xf")) == 0) + extract(argc, argv) + elseif (c.strcmp(long[argv][1], string("-cf")) == 0) + create(argc, argv) + else + c.exit(1) + c.exit(0) + +PUB usage + c.printf0(string("usage: tar [flags] tarfile [filelist]\n")) + c.printf0(string(" -xf Extract files from tarfile\n")) + c.printf0(string(" -cf Create tarfile and add files\n")) + c.exit(0) + +PUB create(argc, argv) | infile, outfile, i, num, filesize, checksum_0, argi + outfile := c.fopen(long[argv][2], string("w")) + if (not outfile) + c.printf1(string("Could not create tarfile %s\n"), long[argv][2]) + return 1 + argi := 3 + repeat while (argi < argc) + infile := c.fopen(long[argv][argi], string("r")) + if (infile == 0) + c.printf1(string("Could not open input file %s\n"), long[argv][argi]) + 'continue; + c.memset(@buffer, 0, 512) + c.strcpy(@buffer, long[argv][argi]) + c.strcpy(@buffer[MODE], string("0000666")) + c.strcpy(@buffer[OWNER], string("0001370")) + c.strcpy(@buffer[GROUP], string("0000765")) + c.fseek(infile, 0, c#SEEK_END) + filesize := c.ftell(infile) + c.fseek(infile, 0, c#SEEK_SET) + c.sprintf1(@buffer[SIZE], string("%011o"), filesize) + c.strcpy(@buffer[TIME], string("11374320374")) + buffer[FILETYPE] := "0" + 'checksum = 1718 + buffer[FILETYPE]; + checksum_0 := 256 + i := 0 + repeat while (i < CHECKSUM) + checksum_0 += buffer[i] + i++ + i := FILETYPE + repeat while (i < 512) + checksum_0 += buffer[i] + i++ + buffer[CHECKSUM + 7] := " " + c.sprintf1(@buffer[CHECKSUM], string("%06o"), checksum_0) + c.fwrite(@buffer, 1, 512, outfile) + repeat while (filesize > 0) + num := c.fread(@buffer, 1, 512, infile) + if (num =< 0) + quit + if (num < 512) + c.memset(@buffer[num], 0, 512 - num) + c.fwrite(@buffer, 1, 512, outfile) + filesize -= 512 + c.fclose(infile) + argi++ + c.memset(@buffer, 0, 512) + c.fwrite(@buffer, 1, 512, outfile) + c.fwrite(@buffer, 1, 512, outfile) + c.fclose(outfile) + +PUB extract(argc, argv) | i, num, blocknum, infile, outfile, skipcount, filesize, checksum_0, attributes + blocknum := 0 + skipcount := 0 + checksum_0 := 0 + infile := c.fopen(long[argv][2], string("r")) + if (infile == 0) + c.printf0(string("Could not open input file\n")) + return 1 + repeat while (1) + if (c.fread(@buffer, 1, 512, infile) <> 512) + quit + 'if (skipcount-- > 0) continue; + i := 0 + repeat while (i < 512) + if (buffer[i]) + quit + i++ + if (i == 512) + quit + c.sscanf1(@buffer[CHECKSUM], string("%o"), @checksum_0) + 'checksum = 1718 + buffer[FILETYPE] - checksum; + checksum_0 := 256 - checksum_0 + i := 0 + repeat while (i < CHECKSUM) + checksum_0 += buffer[i] + i++ + i := FILETYPE + repeat while (i < 512) + checksum_0 += buffer[i] + i++ + 'printf("checksum = %o", checksum); + c.sscanf1(@buffer[SIZE], string("%o"), @filesize) + c.sscanf1(@buffer[MODE], string("%o"), @attributes) + c.printf4(string("filename = %s, filesize = %d, attributes = %o, checksum = %d\n"), @buffer, filesize, attributes, checksum_0) + skipcount :=(filesize + 511) >> 9 + if (checksum_0) + c.printf0(string("Checksum error\n")) + return 1 + outfile := c.fopen(@buffer, string("w")) + if (not outfile) + c.printf1(string("Could not open output file %s\n"), @buffer) + return 1 + repeat while (filesize > 0) + num := c.fread(@buffer, 1, 512, infile) + if (num <> 512) + c.printf0(string("Encountered EOF on tar file\n")) + return 1 + num := 512 + if (filesize < num) + num := filesize + c.fwrite(@buffer, 1, num, outfile) + filesize -= 512 + c.fclose(outfile) + c.fclose(infile) + +{{ ++-----------------------------------------------------------------------------+ +| TERMS OF USE: MIT License | ++-----------------------------------------------------------------------------+ +|Permission is hereby granted, free of charge, to any person obtaining a copy | +|of this software and associated documentation files (the "Software"), to deal| +|in the Software without restriction, including without limitation the rights | +|to use, copy, modify, merge, publish, distribute, sublicense, and/or sell | +|copies of the Software, and to permit persons to whom the Software is | +|furnished to do so, subject to the following conditions: | +| | +|The above copyright notice and this permission notice shall be included in | +|all copies or substantial portions of the Software. | +| | +|THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | +|IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | +|FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE | +|AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | +|LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,| +|OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE| +|SOFTWARE. | ++-----------------------------------------------------------------------------+ +}} diff --git a/src/ted.spin b/src/ted.spin new file mode 100644 index 0000000..fc862a1 --- /dev/null +++ b/src/ted.spin @@ -0,0 +1,246 @@ +'********************************************** +'* Spin code generated by cspin version 0.060 * +'********************************************** +'****************************************************************************** +' Copyright (c) 2011, 2012, Dave Hein +' See end of file for terms of use. +'****************************************************************************** +CON + _clkmode = xinput + _xinfreq = $4e495053 ' SPIN + +OBJ + c : "clibsd" + +VAR + byte bigbuf[12000] + +'PUB main | argc, argv, infile, bigptr, bigbuf, numchar, linenum, instr[25], temp, i, first, last, cmd, changed +PUB main(argc, argv) | infile, bigptr, numchar, linenum, instr[25], temp, i, first, last, cmd, changed + c.enter + numchar := 0 + changed := 0 + infile := c.fopen(long[argv][1], string("r")) +{ + bigbuf := c.malloc(4000) + ifnot bigbuf + c.printf0(string("Could not malloc 4000 bytes\n")) + c.exit(0) + bigptr := bigbuf +} + bigptr := @bigbuf + if (infile) + repeat while (c.fgets(bigptr, 100, infile)) + RemoveCRLF(bigptr) + lineptr[numline++] := bigptr + numchar += strsize(bigptr) + bigptr += strsize(bigptr) + 1 + c.fclose(infile) + else + numchar := 0 + c.printf1(string("%d\n"), numchar) + linenum := numline - 1 + repeat while (1) + c.gets(@instr) + if (not ParseCommand(@instr, linenum, @first, @last, @cmd)) + c.printf0(string("?\n")) + next + if (cmd == 0) + if (last => 0) + linenum := last + elseif (first => 0) + linenum := first + c.printf1(string("%s\n"), lineptr[linenum]) + elseif (cmd == "Q") + quit + elseif (cmd == "q") + if (changed == 0) + quit + changed := 0 + c.printf0(string("?\n")) + elseif (cmd == "w") + numchar := 0 + infile := c.fopen(long[argv][1], string("w")) + i := 0 + repeat while (i < numline) + numchar += strsize(lineptr[i]) + c.fprintf1(infile, string("%s\n"), lineptr[i]) + i++ + c.fclose(infile) + c.printf1(string("%d\n"), numchar) + changed := 0 + elseif (cmd == "d") + DeleteLine(linenum) + changed := 1 + elseif (cmd == "i" or byte[@instr][0] == "a") + if (cmd == "a" or linenum < 0) + linenum++ + repeat while (1) + c.gets(bigptr) + RemoveCRLF(bigptr) + if (byte[bigptr][0] == "." and byte[bigptr][1] == 0) + quit + InsertLine(bigptr, linenum++) + bigptr += strsize(bigptr) + 1 + linenum-- + changed := 1 + elseif (cmd == "j") + c.strcpy(bigptr, lineptr[linenum]) + c.strcat(bigptr, lineptr[linenum + 1]) + lineptr[linenum] := bigptr + bigptr += strsize(bigptr) + 1 + DeleteLine(linenum + 1) + changed := 1 + elseif (cmd == "p") + if (last < 0) + last := first + if (first < 0) + last := first := linenum + linenum := first + repeat while (linenum =< last) + c.printf1(string("%s\n"), lineptr[linenum]) + linenum++ + linenum := last + elseif (cmd == "l") + if (last < 0) + last := first + if (first < 0) + last := first := linenum + linenum := first + repeat while (linenum =< last) + c.printf1(string("%s$\n"), lineptr[linenum]) + linenum++ + linenum := last + elseif (cmd == "n") + if (last < 0) + last := first + if (first < 0) + last := first := linenum + linenum := first + repeat while (linenum =< last) + c.printf2(string("%4d %s\n"), linenum + 1, lineptr[linenum]) + linenum++ + linenum := last + elseif (cmd == "h") + help + else + c.printf0(string("?\n")) + 'c.free(bigbuf) + c.exit(0) + +DAT + numline long 0 + lineptr long 0[300] + +PUB RemoveCRLF(str) | len + len := strsize(str) + str += len - 1 + repeat while (len > 0) + if (byte[str] <> 10 and byte[str] <> 13) + quit + byte[str--] := 0 + len-- + +PUB InsertLine(str, linenum) | i + i := numline + repeat while (i > linenum) + lineptr[i] := lineptr[i - 1] + i-- + lineptr[linenum] := str + numline++ + +PUB DeleteLine(linenum) | i + i := linenum + repeat while (i < numline) + lineptr[i] := lineptr[i + 1] + i++ + numline-- + +PUB PrintLines(first, last, mode) | line + line := first + repeat while (line =< last) + if (mode == "n") + c.printf1(string("%d %s\n"), lineptr[line]) + elseif (mode == "p") + c.printf1(string("%d %s$\n"), lineptr[line]) + else + c.printf1(string("%d %s\n"), lineptr[line]) + line++ + +PUB GetLineNumber(str, linenum, num) + if (byte[str][0] == ".") + long[num] := linenum + str++ + elseif (byte[str][0] == "-") + long[num] := linenum - 1 + str++ + elseif (byte[str][0] == "+") + long[num] := linenum + 1 + str++ + elseif (byte[str][0] == "$") + long[num] := numline - 1 + str++ + elseif (~byte[str][0] => "0" and ~byte[str][0] =< "9") + long[num] := 0 + repeat while (~byte[str][0] => "0" and ~byte[str][0] =< "9") + long[num] :=((long[num]) * 10) + ~byte[str][0] - "0" + str++ + long[num]-- + else + long[num] := - 2 + if (long[num] <> - 2 and(long[num] < 0 or long[num] => numline)) + long[num] := - 3 + return str + +PUB ParseCommand(str, linenum, first, last, cmd) + str := GetLineNumber(str, linenum, first) + if (long[first] == - 3) + return 0 + if (byte[str] == ",") + str := GetLineNumber(str + 1, linenum, last) + if (long[last] == - 3) + return 0 + else + long[last] := - 2 + long[cmd] := ~byte[str] + return 1 + +PUB help + c.printf0(string("ED Commands\n")) + c.printf0(string("q - quit\n")) + c.printf0(string("w - write\n")) + c.printf0(string("d - delete line\n")) + c.printf0(string("i - insert line\n")) + c.printf0(string("a - append line\n")) + c.printf0(string("p - print lines\n")) + c.printf0(string("j - join line\n")) + c.printf0(string("# - move to line #\n")) + c.printf0(string(". - current line\n")) + c.printf0(string("$ - move to last line\n")) + c.printf0(string("- - move down one line\n")) + c.printf0(string("+ - move up one line\n")) + c.printf0(string("h - print help information\n")) + +{{ ++-----------------------------------------------------------------------------+ +| TERMS OF USE: MIT License | ++-----------------------------------------------------------------------------+ +|Permission is hereby granted, free of charge, to any person obtaining a copy | +|of this software and associated documentation files (the "Software"), to deal| +|in the Software without restriction, including without limitation the rights | +|to use, copy, modify, merge, publish, distribute, sublicense, and/or sell | +|copies of the Software, and to permit persons to whom the Software is | +|furnished to do so, subject to the following conditions: | +| | +|The above copyright notice and this permission notice shall be included in | +|all copies or substantial portions of the Software. | +| | +|THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | +|IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | +|FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE | +|AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | +|LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,| +|OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE| +|SOFTWARE. | ++-----------------------------------------------------------------------------+ +}} diff --git a/src/temp.spin b/src/temp.spin new file mode 100644 index 0000000..5cfb0da --- /dev/null +++ b/src/temp.spin @@ -0,0 +1,108 @@ +'****************************************************************************** +' Copyright (c) 2011, 2012, Dave Hein +' See end of file for terms of use. +'****************************************************************************** +CON + _clkmode = xinput + _xinfreq = $4e495053 ' SPIN + +OBJ + c : "clibsd" + ser : "cserial" + run : "runprog" + sys : "sysdefs" + f : "fsrwx" + +DAT + nullstr byte 0[4] + tokens long 0[20] + buffer byte 0[100] + +PUB main(argc, argv) | infile, stream, handle, num, sector, size, i + c.enter + repeat + c.printf0(string("$ ")) + c.gets(@buffer) + num := tokenize + ifnot num + next + repeat i from 0 to num-1 + c.printf1(string("<%s> "), tokens[i]) + c.printf0(string("\n")) + infile := c.fopen(tokens[0], string("r")) + ifnot infile + c.printf1(string("Could not open %s\n"), tokens[0]) + next + handle := long[infile][c#stream_handle] + sector := f.hget_first_sector(handle) + size := f.hget_filesize(handle) + c.fclose(infile) + runit(num, @tokens, sector, size) + +PUB runit(argc, argv, sector, size) | stream, handle + long[argv][argc] := @nullstr + long[argv][argc+1] := @nullstr + copyargs(argc+2, argv) + stream := c.getstdin + handle := long[stream][c#stream_handle] + ser.stop1(handle) + run.run(argc, sys#argv_parms, sector, size) + +PUB copyargs(argc, argv) | i, ptr1, ptr2 + if argc > 0 + ptr1 := sys#argv_parms + ptr2 := ptr1 + argc * 4 + repeat i from 0 to argc + 1 + long[ptr1][i] := ptr2 + c.strcpy(ptr2, long[argv][i]) + ptr2 += strsize(long[argv][i]) + 1 + +PUB tokenize | ptr + ptr := @buffer + repeat while byte[ptr] + ptr := SkipChar(ptr, " ") + ifnot byte[ptr] + quit + tokens[result++] := ptr + ptr := FindChar(ptr, " ") + if byte[ptr] + byte[ptr++] := 0 + +PUB FindChar(ptr, val) + repeat while byte[ptr] + if byte[ptr] == val + quit + ptr++ + return ptr + +PUB SkipChar(ptr, val) + repeat while byte[ptr] + if byte[ptr] <> val + quit + ptr++ + return ptr + +{{ ++-----------------------------------------------------------------------------+ +| TERMS OF USE: MIT License | ++-----------------------------------------------------------------------------+ +|Permission is hereby granted, free of charge, to any person obtaining a copy | +|of this software and associated documentation files (the "Software"), to deal| +|in the Software without restriction, including without limitation the rights | +|to use, copy, modify, merge, publish, distribute, sublicense, and/or sell | +|copies of the Software, and to permit persons to whom the Software is | +|furnished to do so, subject to the following conditions: | +| | +|The above copyright notice and this permission notice shall be included in | +|all copies or substantial portions of the Software. | +| | +|THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | +|IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | +|FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE | +|AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | +|LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,| +|OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE| +|SOFTWARE. | ++-----------------------------------------------------------------------------+ +}} + diff --git a/src/test.spin b/src/test.spin new file mode 100644 index 0000000..b6b03e3 --- /dev/null +++ b/src/test.spin @@ -0,0 +1,111 @@ +'****************************************************************************** +' Copyright (c) 2013 Dave Hein +' See end of file for terms of use. +'****************************************************************************** +CON + _clkmode = xinput + _xinfreq = $4e495053 ' SPIN + +OBJ + c : "clibsd" + vs : "varsubs" + +PUB main(argc, argv) + c.enter + result := (evaluate(argc-1, argv+4) == 0) & 1 + c.exit(result) + +PUB evaluate(argc, argv) + if argc == 1 and strcomp(long[argv], string("-z")) + return 1 + if argc == 2 + return eval2(argv) + if argc == 3 + return eval3(argv) + return 0 + +PUB eval2(argv) | operator, operand + operator := long[argv] + operand := long[argv][1] + if strcomp(operator, string("-n")) + return 1 + if strcomp(operator, string("-f")) + return IsFile(operand) + if strcomp(operator, string("-d")) + return IsDir(operand) + if strcomp(operator, string("-e")) + return Exists(operand) + +PUB eval3(argv) | operator, operand1, operand2, value1, value2 + operand1 := long[argv] + operator := long[argv][1] + operand2 := long[argv][2] + + ' Check for string comparisons + if strcomp(operator, string("=")) or strcomp(operator, string("==")) + return strcomp(operand1, operand2) + if strcomp(operator, string("!=")) + return not strcomp(operand1, operand2) + if strcomp(operator, string("<")) + return c.strcmp(operand1, operand2) < 0 + if strcomp(operator, string(">")) + return c.strcmp(operand1, operand2) > 0 + + ' Check for numerical comparisons + value1 := atol(operand1) + value2 := atol(operand2) + if strcomp(operator, string("-eq")) + return value1 == value2 + if strcomp(operator, string("-ne")) + return value1 <> value2 + if strcomp(operator, string("-gt")) + return value1 > value2 + if strcomp(operator, string("-ge")) + return value1 => value2 + if strcomp(operator, string("-lt")) + return value1 < value2 + if strcomp(operator, string("-le")) + return value1 =< value2 + +PUB IsFile(name) | stream + if (stream := c.fopen(name, string("r"))) + c.fclose(stream) + return 1 + +PUB IsDir(name) | stream + if (stream := c.opendir(name)) + c.closedir(stream) + return 1 + +PUB Exists(name) + if IsFile(name) + return 1 + if IsDir(name) + return 1 + +PUB atol(ptr) + c.sscanf1(ptr, string("%d"), @result) + +{{ ++-----------------------------------------------------------------------------+ +| TERMS OF USE: MIT License | ++-----------------------------------------------------------------------------+ +|Permission is hereby granted, free of charge, to any person obtaining a copy | +|of this software and associated documentation files (the "Software"), to deal| +|in the Software without restriction, including without limitation the rights | +|to use, copy, modify, merge, publish, distribute, sublicense, and/or sell | +|copies of the Software, and to permit persons to whom the Software is | +|furnished to do so, subject to the following conditions: | +| | +|The above copyright notice and this permission notice shall be included in | +|all copies or substantial portions of the Software. | +| | +|THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | +|IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | +|FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE | +|AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | +|LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,| +|OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE| +|SOFTWARE. | ++-----------------------------------------------------------------------------+ +}} diff --git a/src/timezone.spin b/src/timezone.spin new file mode 100644 index 0000000..f078795 --- /dev/null +++ b/src/timezone.spin @@ -0,0 +1,63 @@ +'****************************************************************************** +' Copyright (c) 2013, Dave Hein +' See end of file for terms of use. +'****************************************************************************** +CON + _clkmode = xinput + _xinfreq = $4e495053 ' SPIN + +OBJ + c : "clibsd" + sys : "sysdefs" + +pub main(argc, argv) + c.enter + if argc == 1 + c.printf1(string("%d\n"), long[sys#timezone]) + elseif argc == 3 and strcomp(long[argv][1], string("-s")) + c.sscanf1(long[argv][2], string("%d"), sys#timezone) + save_sysparm + else + usage + c.exit(0) + +pub usage + c.printf0(string("usage: timezone [-s num]\n")) + c.exit(1) + +pub save_sysparm | outfile, ptr + outfile := c.fopen(string("/_sysparm"), string("w")) + ifnot outfile + c.printf0(string("Can't open /_sysparm\n")) + return + c.fprintf1(outfile, string("%d\n"), long[sys#unixtime]) + c.fprintf2(outfile, string("%d %d 0\n"), long[sys#timezone], long[sys#config]) + c.fprintf0(outfile, string("0 0 0\n")) + c.fprintf0(outfile, string("GPWD /\n")) + c.fprintf0(outfile, string("LSCRIPT_FILE /bin/_shellrc\n")) + c.fprintf0(outfile, string("P# 0\n")) + c.fclose(outfile) + +{{ ++-----------------------------------------------------------------------------+ +| TERMS OF USE: MIT License | ++-----------------------------------------------------------------------------+ +|Permission is hereby granted, free of charge, to any person obtaining a copy | +|of this software and associated documentation files (the "Software"), to deal| +|in the Software without restriction, including without limitation the rights | +|to use, copy, modify, merge, publish, distribute, sublicense, and/or sell | +|copies of the Software, and to permit persons to whom the Software is | +|furnished to do so, subject to the following conditions: | +| | +|The above copyright notice and this permission notice shall be included in | +|all copies or substantial portions of the Software. | +| | +|THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | +|IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | +|FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE | +|AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | +|LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,| +|OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE| +|SOFTWARE. | ++-----------------------------------------------------------------------------+ +}} diff --git a/src/touch.spin b/src/touch.spin new file mode 100644 index 0000000..e951421 --- /dev/null +++ b/src/touch.spin @@ -0,0 +1,56 @@ +'****************************************************************************** +' Copyright (c) 2013, Dave Hein +' See end of file for terms of use. +'****************************************************************************** +CON + _clkmode = xinput + _xinfreq = $4e495053 ' SPIN + +OBJ + c : "clibsd" + g : "glob" + +PUB main(argc, argv) | argi, globdata, str[25], fname, infile + c.enter + + if argc < 2 + c.printf0(string("usage: touch file1 [file2 ...]\n")) + c.exit(1) + + repeat argi from 1 to argc - 1 + globdata := 0 + repeat while (fname := g.glob(long[argv][argi], @globdata, @str)) + infile := c.fopen(fname, string("r")) + ifnot infile + c.printf1(string("%s not found\n"), fname) + else + c.fclose(infile) + infile := c.fopen(fname, string("a")) + if infile + c.fclose(infile) + + c.exit(0) + +{{ ++-----------------------------------------------------------------------------+ +| TERMS OF USE: MIT License | ++-----------------------------------------------------------------------------+ +|Permission is hereby granted, free of charge, to any person obtaining a copy | +|of this software and associated documentation files (the "Software"), to deal| +|in the Software without restriction, including without limitation the rights | +|to use, copy, modify, merge, publish, distribute, sublicense, and/or sell | +|copies of the Software, and to permit persons to whom the Software is | +|furnished to do so, subject to the following conditions: | +| | +|The above copyright notice and this permission notice shall be included in | +|all copies or substantial portions of the Software. | +| | +|THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | +|IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | +|FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE | +|AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | +|LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,| +|OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE| +|SOFTWARE. | ++-----------------------------------------------------------------------------+ +}} diff --git a/src/unalias.spin b/src/unalias.spin new file mode 100644 index 0000000..5a716e4 --- /dev/null +++ b/src/unalias.spin @@ -0,0 +1,57 @@ +'****************************************************************************** +' Copyright (c) 2013 Dave Hein +' See end of file for terms of use. +'****************************************************************************** +CON + _clkmode = xinput + _xinfreq = $4e495053 ' SPIN + + DOUBLE_QUOTE = $22 + +OBJ + c : "clibsd" + vs : "varsubs" + +PUB main(argc, argv) | argi + c.enter + + ' Print if no parms + if argc < 2 + usage + + repeat argi from 1 to argc - 1 + ProcessParm(long[argv][argi]) + + c.exit(0) + +PUB ProcessParm(name) | ptr + if (ptr := vs.FindAlias(name)) + ptr := vs.RemoveEntry(ptr) + +PUB usage + c.printf0(string("usage: unalias: name ...\n")) + c.exit(1) + +{{ ++-----------------------------------------------------------------------------+ +| TERMS OF USE: MIT License | ++-----------------------------------------------------------------------------+ +|Permission is hereby granted, free of charge, to any person obtaining a copy | +|of this software and associated documentation files (the "Software"), to deal| +|in the Software without restriction, including without limitation the rights | +|to use, copy, modify, merge, publish, distribute, sublicense, and/or sell | +|copies of the Software, and to permit persons to whom the Software is | +|furnished to do so, subject to the following conditions: | +| | +|The above copyright notice and this permission notice shall be included in | +|all copies or substantial portions of the Software. | +| | +|THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | +|IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | +|FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE | +|AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | +|LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,| +|OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE| +|SOFTWARE. | ++-----------------------------------------------------------------------------+ +}} diff --git a/src/unix2dos.spin b/src/unix2dos.spin new file mode 100644 index 0000000..d386c07 --- /dev/null +++ b/src/unix2dos.spin @@ -0,0 +1,95 @@ +'****************************************************************************** +' Copyright (c) 2011, 2012, Dave Hein +' See end of file for terms of use. +'****************************************************************************** +CON + _clkmode = xinput + _xinfreq = $4e495053 ' SPIN + +OBJ + c : "clibsd" + +DAT + buffer long 0[50] + +PUB main(argc, argv) | infile, outfile, len, ptr + + c.enter + + if argc <> 2 + c.printf0(string("usage: unix2dos file\n")) + c.exit(1) + + ' Copy file to temporary file and remove CR + infile := c.fopen(long[argv][1], string("r")) + ifnot infile + c.printf1(string("Could not open %s\n"), long[argv][1]) + c.exit(1) + + outfile := c.fopen(string("_temp123.tmp"), string("w")) + ifnot outfile + c.fclose(infile) + c.printf0(string("Could not open temporary file\n")) + c.exit(1) + + repeat while c.fgets(@buffer, 197, infile) + len := strsize(@buffer) + ptr := @buffer + len - 1 + if len and byte[ptr] == 10 + len-- + ptr-- + if len and byte[ptr] == 13 + len-- + ptr-- + byte[ptr][1] := 13 + byte[ptr][2] := 10 + byte[ptr][3] := 0 + c.fputs(@buffer, outfile) + + c.fclose(infile) + c.fclose(outfile) + + ' Copy temporary file to file + infile := c.fopen(string("_temp123.tmp"), string("r")) + ifnot infile + c.printf0(string("Could not open temporary file\n")) + c.exit(1) + + outfile := c.fopen(long[argv][1], string("w")) + ifnot outfile + c.fclose(infile) + c.printf1(string("Could not open %s\n"), long[argv][1]) + c.exit(1) + + repeat while (len := c.fread(@buffer, 1, 200, infile)) > 0 + c.fwrite(@buffer, 1, len, outfile) + + c.fclose(infile) + c.fclose(outfile) + + c.remove(string("_temp123.tmp")) + c.exit(0) + +{{ ++-----------------------------------------------------------------------------+ +| TERMS OF USE: MIT License | ++-----------------------------------------------------------------------------+ +|Permission is hereby granted, free of charge, to any person obtaining a copy | +|of this software and associated documentation files (the "Software"), to deal| +|in the Software without restriction, including without limitation the rights | +|to use, copy, modify, merge, publish, distribute, sublicense, and/or sell | +|copies of the Software, and to permit persons to whom the Software is | +|furnished to do so, subject to the following conditions: | +| | +|The above copyright notice and this permission notice shall be included in | +|all copies or substantial portions of the Software. | +| | +|THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | +|IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | +|FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE | +|AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | +|LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,| +|OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE| +|SOFTWARE. | ++-----------------------------------------------------------------------------+ +}} diff --git a/src/unixtime.spin b/src/unixtime.spin new file mode 100644 index 0000000..d8a9691 --- /dev/null +++ b/src/unixtime.spin @@ -0,0 +1,41 @@ +PUB unix_time(yr, mo, da, hr, mn, sec, timezone) | a, days + if (mo < 3) + yr-- + mo += 12 + a := yr / 100 + days := (36525 * yr) / 100 + 306001 * (mo + 1) / 10000 + da - a + a ~> 2 - 719591 + return days * 86400 + (hr - timezone) * 3600 + mn * 60 + sec + +PUB date(unixtime, timezone) | yyyymmdd, year, month, day, leap + day := floor_div(unixtime + timezone * 3600, 86400) + year := floor_div(day << 2 + 2, 1461) + word[@yyyymmdd][1] := year + 1970 + leap := floor_mod(year, 4) == 2 + day -= (year * 1461 + 1) ~> 2 + if (day > 58 - leap) + day += leap + 2 + byte[@yyyymmdd][1] := (day * 12 + 6) / 367 + byte[@yyyymmdd][0] := day + 1 - (byte[@yyyymmdd][1]++ * 367 + 5) / 12 + return yyyymmdd + +PUB time(unixtime, timezone) | hours, minutes, seconds + seconds := floor_mod(unixtime + timezone * 3600, 86400) + minutes := seconds / 60 + seconds -= minutes * 60 + hours := minutes / 60 + minutes -= hours * 60 + return hours << 16 + minutes << 8 + seconds + +PUB weekday(unixtime, timezone) + return floor_mod(floor_div(unixtime + timezone * 3600, 86400) + 4, 7) + +PUB floor_div(numer, denom) + if (numer < 0) + numer -= denom - 1 + return numer / denom + +PUB floor_mod(numer, denom) + result := numer // denom + if (result < 0) + result += denom + diff --git a/src/unset.spin b/src/unset.spin new file mode 100644 index 0000000..07f15a7 --- /dev/null +++ b/src/unset.spin @@ -0,0 +1,49 @@ +'****************************************************************************** +' Copyright (c) 2013 Dave Hein +' See end of file for terms of use. +'****************************************************************************** +CON + _clkmode = xinput + _xinfreq = $4e495053 ' SPIN + +OBJ + c : "clibsd" + vs : "varsubs" + +PUB main(argc, argv) | ptr, argi + c.enter + if argc < 2 + usage + argi := 1 + repeat while argi < argc + if (ptr := vs.FindVar(long[argv][argi++])) + vs.RemoveEntry(ptr) + c.exit(0) + +PUB usage + c.printf0(string("usage: unset [name...]\n")) + c.exit(0) + +{{ ++-----------------------------------------------------------------------------+ +| TERMS OF USE: MIT License | ++-----------------------------------------------------------------------------+ +|Permission is hereby granted, free of charge, to any person obtaining a copy | +|of this software and associated documentation files (the "Software"), to deal| +|in the Software without restriction, including without limitation the rights | +|to use, copy, modify, merge, publish, distribute, sublicense, and/or sell | +|copies of the Software, and to permit persons to whom the Software is | +|furnished to do so, subject to the following conditions: | +| | +|The above copyright notice and this permission notice shall be included in | +|all copies or substantial portions of the Software. | +| | +|THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | +|IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | +|FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE | +|AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | +|LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,| +|OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE| +|SOFTWARE. | ++-----------------------------------------------------------------------------+ +}} diff --git a/src/varsubs.spin b/src/varsubs.spin new file mode 100644 index 0000000..a67761a --- /dev/null +++ b/src/varsubs.spin @@ -0,0 +1,171 @@ +'****************************************************************************** +' Copyright (c) 2013 Dave Hein +' See end of file for terms of use. +'****************************************************************************** +CON + GLOBAL_FLAG = "G" + LOCAL_FLAG = "L" + PARM_FLAG = "P" + ALIAS_FLAG = "A" + +DAT + nullstr long 0 + retvalstr long 0[4] + +OBJ + dbg : "cserial" + sys : "sysdefs" + +PUB FindVar(str) | ptr + ptr := sys#environ_vars + repeat while byte[ptr] + if strcomp(str, ptr+1) + if byte[ptr] <> ALIAS_FLAG + return ptr + ptr += strsize(ptr) + 1 + ptr += strsize(ptr) + 1 + +PUB FindAlias(str) | ptr + ptr := sys#environ_vars + repeat while byte[ptr] + if strcomp(str, ptr+1) + if byte[ptr] == ALIAS_FLAG + return ptr + ptr += strsize(ptr) + 1 + ptr += strsize(ptr) + 1 + +PUB FindEnd | ptr + ptr := sys#environ_vars + repeat while byte[ptr] + ptr += strsize(ptr) + 1 + ptr += strsize(ptr) + 1 + return ptr + +PUB RemoveEntry(ptr1) | ptr2, len + ptr2 := ptr1 + strsize(ptr1) + 1 + ptr2 += strsize(ptr2) + 1 + repeat while byte[ptr2] + repeat 2 + len := strsize(ptr2) + 1 + bytemove(ptr1, ptr2, len) + ptr1 += len + ptr2 += len + byte[ptr1] := 0 + return ptr1 + +PUB RemoveVar(name) | ptr + if (ptr := FindVar(name)) + ptr := RemoveEntry(ptr) + +PUB SaveVar(name, value, type) | ptr, space, len1, len2 + + ' Check if variable already exists + if (ptr := FindVar(name)) + type := byte[ptr] + ptr := RemoveEntry(ptr) + else + ptr := FindEnd + + ' Check if space is available + space := sys#environ_vars_end - ptr + 1 + len1 := strsize(name) + 1 + len2 := strsize(value) + 1 + if (space < len1 + len2 + 2) + dbg.dbprintf0(string("Not enough variable space\n")) + return + elseif (space < len1 + len2 + 52) + dbg.dbprintf2(string("space = %d, need = %d\n"), space, len1 + len2 + 2) + + ' Add variable + byte[ptr++] := type + bytemove(ptr, name, len1) + ptr += len1 + bytemove(ptr, value, len2) + ptr += len2 + byte[ptr] := 0 + +PUB GetVarVal(name) | ptr + if strcomp(name, string("?")) + itoa(long[sys#return_value], @retvalstr) + 'dbg.dbprintf2(string("GetVarVal($?) %d %s\n"), long[sys#return_value], @retvalstr) + return @retvalstr + if (ptr := FindVar(name)) + return ptr + strsize(ptr) + 1 + return @nullstr + +PUB Val_0(ptr) + if (byte[ptr][0] <> "$") + return ptr + return GetVarVal(@byte[ptr][1]) + +PUB NumVal(ptr) + if (byte[ptr][0] == "$") + if strcomp(ptr, string("$?")) + return long[sys#return_value] + ptr := GetVarVal(@byte[ptr][1]) + return atol(ptr) + +PUB FindChar(str, val) + repeat while byte[str] + if byte[str] == val + quit + str++ + return str + +PUB SkipChar(str, val) + repeat while byte[str] + if byte[str] <> val + quit + str++ + return str + +PUB IsAlpha(val) + return ((val => "A" and val =< "Z") or (val => "a" and val =< "z")) + +PUB atol(ptr) | signflag + signflag := (byte[ptr] == "-") + ptr -= signflag + repeat while byte[ptr] => "0" and byte[ptr] =< "9" + result := (result * 10) + byte[ptr++] - "0" + if signflag + result := -result + +PUB itoa(val, str) | divisor, flag + if val < 0 + byte[str++] := "-" + val := -val + flag := 0 + divisor := 1_000_000_000 + repeat 9 + result := val / divisor + val //= divisor + divisor /= 10 + if result or flag + flag := 1 + byte[str++] := result + "0" + byte[str++] := val + "0" + byte[str] := 0 + +{{ ++-----------------------------------------------------------------------------+ +| TERMS OF USE: MIT License | ++-----------------------------------------------------------------------------+ +|Permission is hereby granted, free of charge, to any person obtaining a copy | +|of this software and associated documentation files (the "Software"), to deal| +|in the Software without restriction, including without limitation the rights | +|to use, copy, modify, merge, publish, distribute, sublicense, and/or sell | +|copies of the Software, and to permit persons to whom the Software is | +|furnished to do so, subject to the following conditions: | +| | +|The above copyright notice and this permission notice shall be included in | +|all copies or substantial portions of the Software. | +| | +|THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | +|IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | +|FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE | +|AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | +|LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,| +|OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE| +|SOFTWARE. | ++-----------------------------------------------------------------------------+ +}} diff --git a/src/vgatdemo.spin b/src/vgatdemo.spin new file mode 100644 index 0000000..e022dc2 --- /dev/null +++ b/src/vgatdemo.spin @@ -0,0 +1,101 @@ +'****************************************************************************** +' Copyright (c) 2011, 2012, Dave Hein +' See end of file for terms of use. +'****************************************************************************** +CON + _clkmode = xinput + _xinfreq = $4e495053 ' SPIN + +OBJ + c : "clibsd" + vga : "vgatext" + vgap : "vgatextp" + +DAT + c3_flag long 0 + base_pin long 0 + vga_struct long 0[vgap#handlesize] + +PUB main(argc, argv) | i, handle, vga_cog + c.enter + ProcessParm(argc, argv) + if c3_flag + DIRA |= $8000 + handle := @vga_struct + vga_cog := vgap.start(handle, base_pin) + ifnot vga_cog + c.printf0(string("VGA driver is not loaded\n")) + exit(1) + + 'start term + vga.str(handle, string(13," VGA Text Demo...",13,13,$C,5," OBJ and VAR require only 2.6KB ",$C,1)) + repeat 14 + vga.out(handle, " ") + repeat i from $0E to $FF + vga.out(handle, i) + vga.str(handle, string($C,6," Uses internal ROM font ",$C,2)) + c.getchar + vga.out(handle, $c) + vga.out(handle, 0) + vga.out(handle, 0) + repeat + result := c.getchar + if result == 3 or result == 27 + quit + c.putchar(result) + vga.out(handle, result) + + exit(0) + +PUB ProcessParm(argc, argv) | ptr + + 'check for two parms + if argc <> 2 + usage + + ' Check if arg is c3 or C3 + ptr := long[argv][1] + if strcomp(ptr, string("c3")) or strcomp(ptr, string("C3")) + base_pin := 16 + ' else, check if it's 0, 8, 16 or 24 + else + c.sscanf1(ptr, string("%d"), @base_pin) + if base_pin <> 0 and base_pin <> 8 and base_pin <> 16 and base_pin <> 24 + usage + +PRI usage + c.printf0(string("usage: vgatdemo parm\n")) + c.printf0(string(" parm is either 'c3' for the C3 board or\n")) + c.printf0(string(" a starting pin number of 0, 8, 16 or 24\n")) + waitcnt(cnt + clkfreq/10) + exit(1) + +PRI exit(retval) + if c3_flag + DIRA &= !$8000 + c.exit(retval) + +{{ ++-----------------------------------------------------------------------------+ +| TERMS OF USE: MIT License | ++-----------------------------------------------------------------------------+ +|Permission is hereby granted, free of charge, to any person obtaining a copy | +|of this software and associated documentation files (the "Software"), to deal| +|in the Software without restriction, including without limitation the rights | +|to use, copy, modify, merge, publish, distribute, sublicense, and/or sell | +|copies of the Software, and to permit persons to whom the Software is | +|furnished to do so, subject to the following conditions: | +| | +|The above copyright notice and this permission notice shall be included in | +|all copies or substantial portions of the Software. | +| | +|THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | +|IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | +|FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE | +|AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | +|LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,| +|OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE| +|SOFTWARE. | ++-----------------------------------------------------------------------------+ +}} + diff --git a/src/vgatext.spin b/src/vgatext.spin new file mode 100644 index 0000000..7cea12c --- /dev/null +++ b/src/vgatext.spin @@ -0,0 +1,159 @@ +''*************************************** +''* VGA Text 32x15 v1.0 * +''* Author: Chip Gracey * +''* Copyright (c) 2006 Parallax, Inc. * +''* See end of file for terms of use. * +''* Modified by Dave Hein for spinix * +''*************************************** + +CON + + cols = 32 + rows = 15 + + screensize = cols * rows + lastrow = screensize - cols + + s_vga_screen = 4 + s_vga_colors = 5 + s_vga_col = 21 + s_vga_row = 22 + s_vga_color = 23 + +DAT + flag long 0 + +PUB str(handle, stringptr) + +'' Print a zero-terminated string + + repeat strsize(stringptr) + out(handle, byte[stringptr++]) + + +PUB out(handle, c) | i, k, pscreen, col, row, color + +'' Output a character +'' +'' $00 = clear screen +'' $01 = home +'' $08 = backspace +'' $09 = tab (8 spaces per) +'' $0A = set X position (X follows) +'' $0B = set Y position (Y follows) +'' $0C = set color (color follows) +'' $0D = return +'' others = printable characters + + pscreen := long[handle][s_vga_screen] + col := long[handle][s_vga_col] + row := long[handle][s_vga_row] + + case flag + $00: case c + $00: wordfill(pscreen, $220, screensize) + col := row := 0 + $01: col := row := 0 + $08: if col + col-- + $09: repeat + print(handle, " ") + while (col++) & 7 + return + $0A..$0C: flag := c + return + $0D: newline(handle) + return + other: print(handle, c) + return + $0A: col := c // cols + $0B: row := c // rows + $0C: color := c & 7 + long[handle][s_vga_color] := color + flag := 0 + return + flag := 0 + + long[handle][s_vga_col] := col + long[handle][s_vga_row] := row + + +PUB setcolors(handle, colorptr) | i, fore, back, pcolors + +'' Override default color palette +'' colorptr must point to a list of up to 8 colors +'' arranged as follows (where r, g, b are 0..3): +'' +'' fore back +'' ------------ +'' palette byte %%rgb, %%rgb 'color 0 +'' byte %%rgb, %%rgb 'color 1 +'' byte %%rgb, %%rgb 'color 2 +'' ... + + pcolors := long[handle][s_vga_colors] + + repeat i from 0 to 7 + fore := byte[colorptr][i << 1] << 2 + back := byte[colorptr][i << 1 + 1] << 2 + long[pcolors][i << 1] := fore << 24 + back << 16 + fore << 8 + back + long[pcolors][i << 1 + 1] := fore << 24 + fore << 16 + back << 8 + back + + +PRI print(handle, c) | pscreen, col, row, color + + pscreen := long[handle][s_vga_screen] + col := long[handle][s_vga_col] + row := long[handle][s_vga_row] + color := long[handle][s_vga_color] + + if col == cols - 1 + c := ">" + + word[pscreen][row * cols + col] := (color << 1 + c & 1) << 10 + $200 + c & $FE + if col < cols - 1 + if ++col == cols + newline(handle) + else + long[handle][s_vga_col] := col + + +PUB newline(handle) | i, pscreen, col, row + + pscreen := long[handle][s_vga_screen] + row := long[handle][s_vga_row] + + col := 0 + if ++row == rows + row-- + wordmove(pscreen, pscreen + cols*2, lastrow) 'scroll lines + wordfill(pscreen + lastrow*2, $220, cols) 'clear new line + + long[handle][s_vga_col] := col + long[handle][s_vga_row] := row + + + +{{ ++-----------------------------------------------------------------------------+ +| TERMS OF USE: MIT License | ++-----------------------------------------------------------------------------+ +|Permission is hereby granted, free of charge, to any person obtaining a copy | +|of this software and associated documentation files (the "Software"), to deal| +|in the Software without restriction, including without limitation the rights | +|to use, copy, modify, merge, publish, distribute, sublicense, and/or sell | +|copies of the Software, and to permit persons to whom the Software is | +|furnished to do so, subject to the following conditions: | +| | +|The above copyright notice and this permission notice shall be included in | +|all copies or substantial portions of the Software. | +| | +|THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | +|IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | +|FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE | +|AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | +|LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,| +|OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE| +|SOFTWARE. | ++-----------------------------------------------------------------------------+ +}} diff --git a/src/vgatextp.spin b/src/vgatextp.spin new file mode 100644 index 0000000..4df30ff --- /dev/null +++ b/src/vgatextp.spin @@ -0,0 +1,705 @@ +''*************************************** +''* VGA Driver v1.1 * +''* Author: Chip Gracey * +''* Copyright (c) 2006 Parallax, Inc. * +''* See end of file for terms of use. * +''* Modified by Dave Hein for spinix * +''*************************************** + +' v1.0 - 01 May 2006 - original version +' v1.1 - 15 May 2006 - pixel tile size can now be 16 x 32 to enable more efficient +' character displays utilizing the internal font - see 'vga_mode' + +CON + + paramcount = 21 + colortable = $180 'start of colortable inside cog + + cols = 32 + rows = 15 + + screensize = cols * rows + lastrow = screensize - cols + + color_count = 8 * 2 + s_vga_pins = 2 + s_vga_screen = 4 + s_vga_colors = 5 + s_vga_rate = 20 + s_vga_col = 21 + s_vga_row = 22 + s_vga_color = 23 + vga_count = 24 + + handlesize = (vga_count + color_count)*4 + screensize*2 + + +VAR + + long cog + + +PUB start1(vgaptr) : okay + +'' Start VGA driver - starts a cog +'' returns false if no cog available +'' +'' vgaptr = pointer to VGA parameters + + stop1 + okay := cog := cognew(@entry, vgaptr) + 1 + + +PUB stop1 + +'' Stop VGA driver - frees a cog + + if cog + cogstop(cog~ - 1) + + +PUB start(handle, basepin) : okay + +'' Start terminal - starts a cog +'' returns false if no cog available +'' +'' requires at least 80MHz system clock + + longmove(handle, @vga_params, vga_count) + long[handle][s_vga_pins] := basepin | %000_111 + long[handle][s_vga_screen] := @long[handle][vga_count + color_count] + long[handle][s_vga_colors] := @long[handle][vga_count] + long[handle][s_vga_rate] := clkfreq >> 2 + wordfill(@long[handle][vga_count + color_count], $220, screensize) + + setcolors(handle, @palette) + + okay := start1(handle) + + +PUB stop + +'' Stop terminal - frees a cog + + stop1 + + +PUB setcolors(handle, colorptr) | i, fore, back, pcolors + +'' Override default color palette +'' colorptr must point to a list of up to 8 colors +'' arranged as follows (where r, g, b are 0..3): +'' +'' fore back +'' ------------ +'' palette byte %%rgb, %%rgb 'color 0 +'' byte %%rgb, %%rgb 'color 1 +'' byte %%rgb, %%rgb 'color 2 +'' ... + + pcolors := long[handle][s_vga_colors] + + repeat i from 0 to 7 + fore := byte[colorptr][i << 1] << 2 + back := byte[colorptr][i << 1 + 1] << 2 + long[pcolors][i << 1] := fore << 24 + back << 16 + fore << 8 + back + long[pcolors][i << 1 + 1] := fore << 24 + fore << 16 + back << 8 + back + + +DAT + +vga_params long 0 'status + long 1 'enable + long 0 'pins + long %1000 'mode + long 0 'videobase + long 0 'colorbase + long cols 'hc + long rows 'vc + long 1 'hx + long 1 'vx + long 0 'ho + long 0 'vo + long 512 'hd + long 10 'hf + long 75 'hs + long 43 'hb + long 480 'vd + long 11 'vf + long 2 'vs + long 31 'vb + long 0 'rate + long 0 'col + long 0 'row + long 0 'color + + ' fore back + ' RGB RGB +palette byte %%333, %%001 '0 white / dark blue + byte %%330, %%110 '1 yellow / brown + byte %%202, %%000 '2 magenta / black + byte %%111, %%333 '3 grey / white + byte %%033, %%011 '4 cyan / dark cyan + byte %%020, %%232 '5 green / gray-green + byte %%100, %%311 '6 red / pink + byte %%033, %%003 '7 cyan / blue + + +DAT + +'******************************** +'* Assembly language VGA driver * +'******************************** + + org +' +' +' Entry +' +entry mov taskptr,#tasks 'reset tasks + + mov x,#8 'perform task sections initially +:init jmpret taskret,taskptr + djnz x,#:init +' +' +' Superfield +' +superfield mov hv,hvbase 'set hv + + mov interlace,#0 'reset interlace + + test _mode,#%0100 wz 'get interlace into nz +' +' +' Field +' +field wrlong visible,par 'set status to visible + + tjz vb,#:nobl 'do any visible back porch lines + mov x,vb + movd bcolor,#colortable + call #blank_line +:nobl + mov screen,_screen 'point to first tile (upper-leftmost) + mov y,_vt 'set vertical tiles +:line mov vx,_vx 'set vertical expand +:vert if_nz xor interlace,#1 'interlace skip? + if_nz tjz interlace,#:skip + + tjz hb,#:nobp 'do any visible back porch pixels + mov vscl,hb + waitvid colortable,#0 +:nobp + mov x,_ht 'set horizontal tiles + mov vscl,hx 'set horizontal expand + +:tile rdword tile,screen 'read tile + add tile,line 'set pointer bits into tile + rol tile,#6 'read tile pixels + rdlong pixels,tile '(8 clocks between reads) + shr tile,#10+6 'set tile colors + movd :color,tile + add screen,#2 'point to next tile +:color waitvid colortable,pixels 'pass colors and pixels to video + djnz x,#:tile 'another tile? + + sub screen,hc2x 'repoint to first tile in same line + + tjz hf,#:nofp 'do any visible front porch pixels + mov vscl,hf + waitvid colortable,#0 +:nofp + mov x,#1 'do hsync + call #blank_hsync '(x=0) + +:skip djnz vx,#:vert 'vertical expand? + ror line,linerot 'set next line + add line,lineadd wc + rol line,linerot + if_nc jmp #:line + add screen,hc2x 'point to first tile in next line + djnz y,#:line wc 'another tile line? (c=0) + + tjz vf,#:nofl 'do any visible front porch lines + mov x,vf + movd bcolor,#colortable + call #blank_line +:nofl + if_nz xor interlace,#1 wc,wz 'get interlace and field1 into nz (c=0/?) + + if_z wrlong invisible,par 'unless interlace and field1, set status to invisible + + mov taskptr,#tasks 'reset tasks + + addx x,_vf wc 'do invisible front porch lines (x=0 before, c=0 after) + call #blank_line + + mov x,_vs 'do vsync lines + call #blank_vsync + + mov x,_vb 'do invisible back porch lines, except last + call #blank_vsync + + if_nz jmp #field 'if interlace and field1, display field2 + jmp #superfield 'else, new superfield +' +' +' Blank line(s) +' +blank_vsync cmp interlace,#2 wc 'vsync (c=1) + +blank_line mov vscl,h1 'blank line or vsync-interlace? + if_nc add vscl,h2 + if_c_and_nz xor hv,#%01 + if_c waitvid hv,#0 + if_c mov vscl,h2 'blank line or vsync-normal? + if_c_and_z xor hv,#%01 +bcolor waitvid hv,#0 + + if_nc jmpret taskret,taskptr 'call task section (z undisturbed) + +blank_hsync mov vscl,_hf 'hsync, do invisible front porch pixels + waitvid hv,#0 + + mov vscl,_hs 'do invisble sync pixels + xor hv,#%10 + waitvid hv,#0 + + mov vscl,_hb 'do invisible back porch pixels + xor hv,#%10 + waitvid hv,#0 + + djnz x,#blank_line wc '(c=0) + + movd bcolor,#hv +blank_hsync_ret +blank_line_ret +blank_vsync_ret ret +' +' +' Tasks - performed in sections during invisible back porch lines +' +tasks mov t1,par 'load parameters + movd :par,#_enable '(skip _status) + mov t2,#paramcount - 1 +:load add t1,#4 +:par rdlong 0,t1 + add :par,d0 + djnz t2,#:load '+164 + + mov t1,#2 'set video pins and directions + shl t1,_pins '(if video disabled, pins will drive low) + sub t1,#1 + test _pins,#$20 wc + and _pins,#$38 + shr t1,_pins + movs vcfg,t1 + shl t1,_pins + or t1,_8000 + shr _pins,#3 + movd vcfg,_pins + if_nc mov dira,t1 + if_nc mov dirb,#0 + if_c mov dira,#0 + if_c mov dirb,t1 '+14 + + tjz _enable,#disabled '+2, disabled? + + jmpret taskptr,taskret '+1=181, break and return later + + rdlong t1,#0 'make sure CLKFREQ => 16MHz + shr t1,#1 + cmp t1,m8 wc + if_c jmp #disabled '+8 + + min _rate,pllmin 'limit _rate to pll range + max _rate,pllmax '+2 + + mov t1,#%00001_011 'set ctra configuration +:max cmp m8,_rate wc 'adjust rate to be within 4MHz-8MHz + if_c shr _rate,#1 '(vco will be within 64MHz-128MHz) + if_c add t1,#%00000_001 + if_c jmp #:max +:min cmp _rate,m4 wc + if_c shl _rate,#1 + if_c sub x,#%00000_001 + if_c jmp #:min + movi ctra,t1 '+22 + + rdlong t1,#0 'divide _rate/CLKFREQ and set frqa + mov hvbase,#32+1 +:div cmpsub _rate,t1 wc + rcl t2,#1 + shl _rate,#1 + djnz hvbase,#:div '(hvbase=0) + mov frqa,t2 '+136 + + test _mode,#%0001 wc 'make hvbase + muxnc hvbase,vmask + test _mode,#%0010 wc + muxnc hvbase,hmask '+4 + + jmpret taskptr,taskret '+1=173, break and return later + + mov hx,_hx 'compute horizontal metrics + shl hx,#8 + or hx,_hx + shl hx,#4 + + mov hc2x,_ht + shl hc2x,#1 + + mov h1,_hd + neg h2,_hf + sub h2,_hs + sub h2,_hb + sub h1,h2 + shr h1,#1 wc + addx h2,h1 + + mov t1,_ht + mov t2,_hx + call #multiply + mov hf,_hd + sub hf,t1 + shr hf,#1 wc + mov hb,_ho + addx hb,hf + sub hf,_ho '+59 + + mov t1,_vt 'compute vertical metrics + mov t2,_vx + call #multiply + test _mode,#%1000 wc 'consider tile size + muxc linerot,#1 + mov lineadd,lineinc + if_c shr lineadd,#1 + if_c shl t1,#1 + test _mode,#%0100 wc 'consider interlace + if_c shr t1,#1 + mov vf,_vd + sub vf,t1 + shr vf,#1 wc + neg vb,_vo + addx vb,vf + add vf,_vo '+53 + + movi vcfg,#%01100_000 '+1, set video configuration + +:colors jmpret taskptr,taskret '+1=114/160, break and return later + + mov t1,#13 'load next 13 colors into colortable +:loop mov t2,:color '5 times = 65 (all 64 colors loaded) + shr t2,#9-2 + and t2,#$FC + add t2,_colors + rdlong t2,t2 + and t2,colormask + or t2,hvbase +:color mov colortable,t2 + add :color,d0 + andn :color,d6 + djnz t1,#:loop '+158 + + jmp #:colors '+1, keep loading colors +' +' +' Multiply t1 * t2 * 16 (t1, t2 = bytes) +' +multiply shl t2,#8+4-1 + + mov tile,#8 +:loop shr t1,#1 wc + if_c add t1,t2 + djnz tile,#:loop + +multiply_ret ret '+37 +' +' +' Disabled - reset status, nap ~4ms, try again +' +disabled mov ctra,#0 'reset ctra + mov vcfg,#0 'reset video + + wrlong outa,par 'set status to disabled + + rdlong t1,#0 'get CLKFREQ + shr t1,#8 'nap for ~4ms + min t1,#3 + add t1,cnt + waitcnt t1,#0 + + jmp #entry 'reload parameters +' +' +' Initialized data +' +pllmin long 500_000 'pll lowest output frequency +pllmax long 128_000_000 'pll highest output frequency +m8 long 8_000_000 '*16 = 128MHz (pll vco max) +m4 long 4_000_000 '*16 = 64MHz (pll vco min) +d0 long 1 << 9 << 0 +d6 long 1 << 9 << 6 +invisible long 1 +visible long 2 +line long $00060000 +lineinc long $10000000 +linerot long 0 +vmask long $01010101 +hmask long $02020202 +colormask long $FCFCFCFC + long 0[3] 'col, row and color used by Spin driver +_8000 long $8000 +' +' +' Uninitialized data +' +taskptr res 1 'tasks +taskret res 1 +t1 res 1 +t2 res 1 + +x res 1 'display +y res 1 +hf res 1 +hb res 1 +vf res 1 +vb res 1 +hx res 1 +vx res 1 +hc2x res 1 +screen res 1 +tile res 1 +pixels res 1 +lineadd res 1 +interlace res 1 +hv res 1 +hvbase res 1 +h1 res 1 +h2 res 1 +' +' +' Parameter buffer +' +_enable res 1 '0/non-0 read-only +_pins res 1 '%pppttt read-only +_mode res 1 '%tihv read-only +_screen res 1 '@word read-only +_colors res 1 '@long read-only +_ht res 1 '1+ read-only +_vt res 1 '1+ read-only +_hx res 1 '1+ read-only +_vx res 1 '1+ read-only +_ho res 1 '0+- read-only +_vo res 1 '0+- read-only +_hd res 1 '1+ read-only +_hf res 1 '1+ read-only +_hs res 1 '1+ read-only +_hb res 1 '1+ read-only +_vd res 1 '1+ read-only +_vf res 1 '1+ read-only +_vs res 1 '1+ read-only +_vb res 1 '2+ read-only +_rate res 1 '500_000+ read-only + + fit colortable 'fit underneath colortable ($180-$1BF) + + +'' +''___ +''VAR 'VGA parameters - 21 contiguous longs +'' +'' long vga_status '0/1/2 = off/visible/invisible read-only +'' long vga_enable '0/non-0 = off/on write-only +'' long vga_pins '%pppttt = pins write-only +'' long vga_mode '%tihv = tile,interlace,hpol,vpol write-only +'' long vga_screen 'pointer to screen (words) write-only +'' long vga_colors 'pointer to colors (longs) write-only +'' long vga_ht 'horizontal tiles write-only +'' long vga_vt 'vertical tiles write-only +'' long vga_hx 'horizontal tile expansion write-only +'' long vga_vx 'vertical tile expansion write-only +'' long vga_ho 'horizontal offset write-only +'' long vga_vo 'vertical offset write-only +'' long vga_hd 'horizontal display ticks write-only +'' long vga_hf 'horizontal front porch ticks write-only +'' long vga_hs 'horizontal sync ticks write-only +'' long vga_hb 'horizontal back porch ticks write-only +'' long vga_vd 'vertical display lines write-only +'' long vga_vf 'vertical front porch lines write-only +'' long vga_vs 'vertical sync lines write-only +'' long vga_vb 'vertical back porch lines write-only +'' long vga_rate 'tick rate (Hz) write-only +'' +''The preceding VAR section may be copied into your code. +''After setting variables, do start(@vga_status) to start driver. +'' +''All parameters are reloaded each superframe, allowing you to make live +''changes. To minimize flicker, correlate changes with vga_status. +'' +''Experimentation may be required to optimize some parameters. +'' +''Parameter descriptions: +'' __________ +'' vga_status +'' +'' driver sets this to indicate status: +'' 0: driver disabled (vga_enable = 0 or CLKFREQ < 16MHz) +'' 1: currently outputting invisible sync data +'' 2: currently outputting visible screen data +'' __________ +'' vga_enable +'' +'' 0: disable (pins will be driven low, reduces power) +'' non-0: enable +'' ________ +'' vga_pins +'' +'' bits 5..3 select pin group: +'' %000: pins 7..0 +'' %001: pins 15..8 +'' %010: pins 23..16 +'' %011: pins 31..24 +'' %100: pins 39..32 +'' %101: pins 47..40 +'' %110: pins 55..48 +'' %111: pins 63..56 +'' +'' bits 2..0 select top pin within group +'' for example: %01111 (15) will use pins %01000-%01111 (8-15) +'' ________ +'' vga_mode +'' +'' bit 3 selects between 16x16 and 16x32 pixel tiles: +'' 0: 16x16 pixel tiles (tileheight = 16) +'' 1: 16x32 pixel tiles (tileheight = 32) +'' +'' bit 2 controls interlace: +'' 0: progressive scan (less flicker, good for motion, required for LCD monitors) +'' 1: interlaced scan (allows you to double vga_vt, good for text) +'' +'' bits 1 and 0 select horizontal and vertical sync polarity, respectively +'' 0: active low +'' 1: active high +'' __________ +'' vga_screen +'' +'' pointer to words which define screen contents (left-to-right, top-to-bottom) +'' number of words must be vga_ht * vga_vt +'' each word has two bitfields: a 6-bit colorset ptr and a 10-bit pixelgroup ptr +'' bits 15..10: select the colorset* for the associated pixel tile +'' bits 9..0: select the pixelgroup** address %ppppppppppcccc00 (p=address, c=0..15) +'' +'' * colorsets are longs which each define four 8-bit colors +'' +'' ** pixelgroups are longs which define (left-to-right, top-to-bottom) the 2-bit +'' (four color) pixels that make up a 16x16 or a 16x32 pixel tile +'' __________ +'' vga_colors +'' +'' pointer to longs which define colorsets +'' number of longs must be 1..64 +'' each long has four 8-bit fields which define colors for 2-bit (four color) pixels +'' first long's bottom color is also used as the screen background color +'' 8-bit color fields are as follows: +'' bits 7..2: actual state of pins 7..2 within pin group* +'' bits 1..0: don't care (used within driver for hsync and vsync) +'' +'' * it is suggested that: +'' bits/pins 7..6 are used for red +'' bits/pins 5..4 are used for green +'' bits/pins 3..2 are used for blue +'' for each bit/pin set, sum 240 and 470-ohm resistors to form 75-ohm 1V signals +'' connect signal sets to RED, GREEN, and BLUE on VGA connector +'' always connect group pin 1 to HSYNC on VGA connector via 240-ohm resistor +'' always connect group pin 0 to VSYNC on VGA connector via 240-ohm resistor +'' ______ +'' vga_ht +'' +'' horizontal number of pixel tiles - must be at least 1 +'' ______ +'' vga_vt +'' +'' vertical number of pixel tiles - must be at least 1 +'' ______ +'' vga_hx +'' +'' horizontal tile expansion factor - must be at least 1 +'' +'' make sure 16 * vga_ht * vga_hx + ||vga_ho is equal to or at least 16 less than vga_hd +'' ______ +'' vga_vx +'' +'' vertical tile expansion factor - must be at least 1 +'' +'' make sure * vga_vt * vga_vx + ||vga_vo does not exceed vga_vd +'' (for interlace, use / 2 * vga_vt * vga_vx + ||vga_vo) +'' ______ +'' vga_ho +'' +'' horizontal offset in ticks - pos/neg value (0 recommended) +'' shifts the display right/left +'' ______ +'' vga_vo +'' +'' vertical offset in lines - pos/neg value (0 recommended) +'' shifts the display up/down +'' ______ +'' vga_hd +'' +'' horizontal display ticks +'' ______ +'' vga_hf +'' +'' horizontal front porch ticks +'' ______ +'' vga_hs +'' +'' horizontal sync ticks +'' ______ +'' vga_hb +'' +'' horizontal back porch ticks +'' ______ +'' vga_vd +'' +'' vertical display lines +'' ______ +'' vga_vf +'' +'' vertical front porch lines +'' ______ +'' vga_vs +'' +'' vertical sync lines +'' ______ +'' vga_vb +'' +'' vertical back porch lines +'' ________ +'' vga_rate +'' +'' tick rate in Hz +'' +'' driver will limit value to be within 500KHz and 128MHz +'' pixel rate (vga_rate / vga_hx) should be no more than CLKFREQ / 4 + +{{ + +┌──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┐ +│ TERMS OF USE: MIT License │ +├──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┤ +│Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation │ +│files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, │ +│modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software│ +│is furnished to do so, subject to the following conditions: │ +│ │ +│The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.│ +│ │ +│THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE │ +│WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR │ +│COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, │ +│ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. │ +└──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┘ +}} \ No newline at end of file diff --git a/src/vi.spin b/src/vi.spin new file mode 100644 index 0000000..1983838 --- /dev/null +++ b/src/vi.spin @@ -0,0 +1,1652 @@ +'********************************************** +'* Spin code generated by cspin version 0.066 * +'********************************************** + +CON + _clkmode = xinput + _xinfreq = $4e495053 ' SPIN + +OBJ + c : "clibsd" + +PUB start(argc, argv) | config + c.enter + config := c.getconfig + termtype := (config >> 4) & 3 + result := (config >> 8) & $ff + if result > 1 + vlines := result - 1 + main(argc, argv) + +'****************************************************************************** +' ED Text Editor +' Copyright (c) 2011, Dave Hein +' See end of file for terms of use. +'****************************************************************************** + +CON + HDR_SIZE = 8 +' typedef struct StringS +next_0 = 0 +size = 4 +str = 8 + UNSPECIFIED = - 2 + PARSING_ERROR = - 3 + TAB_CHAR = 9 + MAX_CHARS = 200 + MALLOC_SIZE = 16000 + CHANGE_ONE_LINE = 2 + CHANGE_TWO_LINES_UP = 3 + CHANGE_TWO_LINES_DOWN = 4 + CHANGE_CURSOR_POSITION = 5 +' Uninitialized global variables + +VAR + long memorysize + long ListHead + long YankHead + byte srchstr[100] + byte rplcstr1[100] + byte rplcstr2[100] + byte filename[100] + byte inbuf[MAX_CHARS] +' Initialized global variables + +DAT + rflag long 0 + gflag long 0 + vmode long 1 + prompt long 0 + vlines long 21 + scroll long 20 + changed long 0 + numlines long 0 + currline long 0 + currcol long 1 + dispcol long 1 + retcode long 0 + cmd0 long 0 + count long 0 + schange long 1 + topline long 1 + termtype long 0 + stdout long 0 + statuslen long 0 + srchdir long 1 + lastcmd long 0 + lastcnt long 0 + lastval long 0 + nullstr byte 0 +' Main program entry + +PUB main(argc, argv) | cmd, strbuf + stdout := c.getstdout + 'argc := 1 + ' Initialization + srchstr[0] := 0 + rplcstr1[0] := 0 + rplcstr2[0] := 0 + ListHead := 0 + YankHead := 0 + prompt := ":" + ' Allocate space for file + result := c.malloc(600) + ' Allocate the string buffer + memorysize := MALLOC_SIZE + repeat while (memorysize > 0) + strbuf := c.malloc(memorysize) + if (strbuf) + quit + memorysize -= 1000 + c.free(result) + smallocinit(strbuf, memorysize) + 'c.printf1(string("memorysize = %d\n"), memorysize) + ' Copy the input file name if specified + if (argc > 1) + c.strcpy(@filename, long[argv][1]) + else + filename[0] := 0 + currline := ReadFile(@filename, 0) + 'c.printf0(string("Press a key\n")) + 'c.getchar + if (numlines > 0) + currline := 1 + ' Get a command and execute it + repeat while (1) + gflag := 0 + CheckMemory + if (vmode == 1) + if (schange) + PrintScreen(currline) + schange := 0 + cmd := c.getchar + ExecuteViCommand(cmd) + else + if (prompt) + c.printf1(string("%c"), prompt) + c.gets(@inbuf) + retcode := ExecuteCommand + if (retcode > 0) + quit + elseif (retcode < 0) + c.printf1(string("E%d:\n"), - retcode) + if (vmode == - 1) + vmode := 1 + c.free(strbuf) + c.exit(0) + +' Allocate a string link and copy contents of "str" + +PUB AllocateString(str_0) | link + link := smalloc(strsize(str_0) + 1) + if (link) + c.strcpy((link + str), str_0) + return link +' Duplicate a string link + +PUB DuplicateString(link) + return AllocateString((link + str)) +' Check for a memory leak + +PUB CheckMemory | total, curr, listsize, yanksize, freesize + listsize := 0 + yanksize := 0 + freesize := 0 + curr := ListHead + repeat while (curr) + listsize += long[curr + size] + curr := long[curr + next_0] + curr := YankHead + repeat while (curr) + yanksize += long[curr + size] + curr := long[curr + next_0] + curr := smfreelist + repeat while (curr) + freesize += long[curr + size] + curr := long[curr + next_0] + total := listsize + yanksize + freesize + if (total <> memorysize) + c.printf0(string("Memory leak!\n")) + c.printf4(string("listsize = %d, yanksize = %d, freesize = %d, total = %d\n"), listsize, yanksize, freesize, listsize + yanksize + freesize) +' Get a pointer to a string link from its line number + +PUB GetLinePtr(linenum, pprev) | prev, curr + prev := 0 + curr := ListHead + repeat while (curr and--linenum > 0) + prev := curr + curr := long[curr + next_0] + long[pprev] := prev + return curr +' Remove the CR and LF characters from the end of a line + +PUB RemoveCRLF(str_0) | len + len := strsize(str_0) + str_0 += len - 1 + repeat while (len > 0) + if (byte[str_0] <> 10 and byte[str_0] <> 13) + quit + byte[str_0--] := 0 + len-- +' Insert a string link at the line given by "linenum" + +PUB InsertLine(line, linenum) | prev, curr + curr := GetLinePtr(linenum - 1, @prev) + if (linenum < 1 or linenum > numlines + 1) + c.printf2(string("InsertLine: linenum = %d, numlines = %d\n"), linenum, numlines) + if (curr == 0 and(linenum <> 1 or ListHead <> 0)) + c.printf1(string("InsertLine: curr == 0, linenum = %d\n"), linenum) + if (linenum == 1) + long[line + next_0] := ListHead + ListHead := line + else + long[line + next_0] := long[curr + next_0] + long[curr + next_0] := line + numlines++ +' Delete the line at "linenum" + +PUB DeleteLine(linenum) | prev, curr + curr := GetLinePtr(linenum, @prev) + if (not curr) + c.printf1(string("DeleteLine: curr = 0, linenum = %d\n"), linenum) + return + if (not prev) + ListHead := long[curr + next_0] + else + long[prev + next_0] := long[curr + next_0] + sfree(curr) + numlines-- +' Based on the value of gflag, check for if the line +' at "lineptr" does, or does not contains "str" + +PUB CheckMatch(lineptr, str_0) + if (gflag == 0) + return 0 + if (gflag == 1) + if (not FindString(lineptr, str_0)) + return 1 + return 0 + if (FindString(lineptr, str_0)) + return 1 + return 0 +' Print lines using the "n", "v", "l" or "p" format + +PUB PrintLines(linenum, first, last, mode) | col, len, line, fflag, lastproc, LinePtr_0, prev, curr + lastproc := linenum + curr := GetLinePtr(first, @prev) + line := first + repeat while (line =< last) + if (not curr) + c.printf1(string("PrintLines: curr = 0, line = %s\n"), line) + return 0 + LinePtr_0 :=(curr + str) + curr := long[curr + next_0] + if (CheckMatch(LinePtr_0, @srchstr)) + line++ + next + if (mode == "n") + c.printf2(string("%6d %s\n"), line, LinePtr_0) + elseif (mode == "v") + if (line == linenum and termtype <> 2) + len := strsize(LinePtr_0) + col := 0 + repeat while (col < len) + if (col + 1 <> dispcol) + c.printf1(string("%c"), byte[LinePtr_0][col]) + else + c.printf1(string("%c"), "@") + col++ + if (dispcol > len) + c.printf1(string("%c"), "@") + c.printf0(string("\n")) + else + c.printf1(string("%s\n"), LinePtr_0) + elseif (mode == "l") + repeat while (byte[LinePtr_0]) + if (byte[LinePtr_0] => 32 and byte[LinePtr_0] < 127) + c.printf1(string("%c"), byte[LinePtr_0]) + elseif (byte[LinePtr_0] == TAB_CHAR) + c.printf0(string("\\t")) + else + c.printf1(string("\\x%2.2x"), byte[LinePtr_0]) + LinePtr_0++ + c.printf0(string("$\n")) + else + c.printf1(string("%s\n"), LinePtr_0) + lastproc := line + line++ + return lastproc +' Decode a number from the string + +PUB getnum(pstr) | val, str_0 + val := 0 + str_0 := long[pstr] + repeat while (c.isdigit(byte[str_0])) + val :=(val * 10) + byte[str_0++] - "0" + long[pstr] := str_0 + return val +' Find the next character that matches "val" + +PUB FindChar(str_0, val) + repeat while (byte[str_0]) + if (byte[str_0] == val) + quit + str_0++ + return str_0 +' Skip the next character that does not match "val" + +PUB SkipChar(str_0, val) + repeat while (byte[str_0]) + if (byte[str_0] <> val) + quit + str_0++ + return str_0 + +' Return true if "str2" is found in "str1" +PUB FindString(str1, str2) | len, col + col := 1 + len := strsize(str2) + repeat while (byte[str1]) + if (c.strncmp(str1, str2, len) == 0) + return col + str1++ + col++ + return 0 + +' Return column if "str2" is found in "str1", otherwise return 0 +PUB FindStringBackward(str1, str2, len1) | len2 + if len1 < 1 + return + str1 += len1 - 1 + len2 := strsize(str2) + repeat while len1 + if (c.strncmp(str1, str2, len2) == 0) + return len1 + len1-- + str1-- + +' Use the first character as a delimeter and copy the string to srchstr +PUB GetString(pstr) | ptr, str_0, val, len + str_0 := long[pstr] + val := byte[str_0++] + if (not val) + return 0 + ptr := FindChar(str_0, val) + len :=(ptr) -(str_0) + if (byte[ptr]) + ptr++ + long[pstr] := ptr + if (len > 0) + c.memcpy(@srchstr, str_0, len) + srchstr[len] := 0 + return 1 +' Search forward through all the lines for the first occurance of srchstr + +PUB SearchStringForward(pstr, linenum) | i, prev, curr + curr := GetLinePtr(linenum + 1, @prev) + if (not GetString(pstr)) + return PARSING_ERROR + i := linenum + 1 + repeat while (i =< numlines) + if (not curr) + c.printf1(string("SearchStringForward: curr = 0, line = %d\n"), i) + return - 3 + if (FindString((curr + str), @srchstr)) + return i + curr := long[curr + next_0] + i++ + curr := ListHead + i := 1 + repeat while (i =< linenum) + if (not curr) + c.printf1(string("SearchStringForward: curr = 0, line = %d\n"), i) + return - 3 + if (FindString((curr + str), @srchstr)) + return i + curr := long[curr + next_0] + i++ + return PARSING_ERROR +' Search backward through all the lines for the first occurance of srchstr +' Note: This is not as efficient as the forward seach because we must use +' GetLinePtr to get the string link pointer + +PUB SearchStringBackward(pstr, linenum) | i, prev, curr + if (not GetString(pstr)) + return PARSING_ERROR + i := linenum - 1 + repeat while (i => 1) + curr := GetLinePtr(i, @prev) + if (not curr) + c.printf1(string("SearchStringBackward: curr = 0, line = %d\n"), i) + return - 3 + if (FindString((curr + str), @srchstr)) + return i + i-- + i := numlines + repeat while (i => linenum) + curr := GetLinePtr(i, @prev) + if (not curr) + c.printf1(string("SearchStringBackward: curr = 0, line = %d\n"), i) + return - 3 + if (FindString((curr + str), @srchstr)) + return i + i-- + return PARSING_ERROR +' Parse the replace string command and extract rplcstr1 and rplcstr2 + +PUB ParseReplaceString(str_0) | ptr + if (byte[str_0++] <> "s") + return 0 + if (byte[str_0] == 0) + return 1 + if (byte[str_0++] <> "/") + return 0 + ptr := FindChar(str_0, "/") + if (byte[ptr] <> "/") + return 0 + byte[ptr++] := 0 + if (byte[str_0]) + c.strcpy(@rplcstr1, str_0) + else + c.strcpy(@rplcstr1, @srchstr) + if (byte[ptr] == 0) + return 1 + str_0 := FindChar(ptr, "/") + if (byte[str_0] <> "/") + return 0 + byte[str_0++] := 0 + c.strcpy(@rplcstr2, ptr) + rflag :=(byte[str_0] == "g") + return 1 +' Store the results of the replace-string command on str1 into str2 + +PUB ReplaceString(str1, str2) | changeflag, len1, len2 + changeflag := 0 + len1 := strsize(@rplcstr1) + len2 := strsize(@rplcstr2) + ' Search through str1 for rplcstr1 and replace with rplcstr2 + repeat while (byte[str1]) + if (c.strncmp(str1, @rplcstr1, len1) == 0) + changeflag := 1 + c.memcpy(str2, @rplcstr2, len2) + str2 += len2 + str1 += len1 + ' Quit after the first time if 'g' was not specified + if (not rflag) + quit + else + byte[str2++] := byte[str1++] + ' Copy the remainder of str1 to str2 + repeat while (byte[str1]) + byte[str2++] := byte[str1++] + byte[str2] := 0 + return changeflag +' Execute the replace-string command on multiple lines in the text buffer + +PUB ReplaceStringCommand(inptr, first, last, linenum) | i, lastproc, changeflag, link, prev, curr + lastproc := linenum + changeflag := 0 + curr := GetLinePtr(first, @prev) + if (not ParseReplaceString(inptr)) + c.printf0(string("?\n")) + return last + i := first + repeat while (i =< last) + if (not curr) + c.printf1(string("ReplaceStringCommand: line = %d\n"), i) + return 1 + if (CheckMatch((curr + str), @srchstr)) + prev := curr + curr := long[curr + next_0] + i++ + next + if (ReplaceString((curr + str), @inbuf)) + link := curr + curr := smalloc(strsize(@inbuf) + 1) + if (not curr) + return lastproc + c.strcpy((curr + str), @inbuf) + if (prev) + long[prev + next_0] := curr + else + ListHead := curr + long[curr + next_0] := long[link + next_0] + sfree(link) + changeflag := 1 + changed := 1 + prev := curr + curr := long[curr + next_0] + lastproc := i + i++ + if (not changeflag) + c.printf0(string("?\n")) + return lastproc +' Extract a line number parameter from a command + +PUB GetLineNumber(str_0, linenum, num) | val + if (byte[str_0][0] == ".") + long[num] := linenum + str_0++ + elseif (byte[str_0][0] == "-") + str_0++ + if (c.isdigit(byte[str_0])) + val := getnum(@str_0) + else + val := 1 + long[num] := linenum - val + elseif (byte[str_0][0] == "+") + str_0++ + if (c.isdigit(byte[str_0])) + val := getnum(@str_0) + else + val := 1 + long[num] := linenum + val + elseif (byte[str_0][0] == "$") + long[num] := numlines + str_0++ + elseif (byte[str_0][0] == "/") + long[num] := SearchStringForward(@str_0, linenum) + elseif (byte[str_0][0] == "?") + long[num] := SearchStringBackward(@str_0, linenum) + elseif (c.isdigit(byte[str_0])) + long[num] := getnum(@str_0) + else + long[num] := UNSPECIFIED + if (long[num] <> UNSPECIFIED and(long[num] < 0 or long[num] > numlines)) + long[num] := PARSING_ERROR + return str_0 +' Extract the first and last numbers and the command from a command string + +PUB ParseCommand(pstr, linenum, first, last, cmd, bang) | done, str_0 + done := 0 + str_0 := long[pstr] + long[bang] := 0 + if (byte[str_0] == "%") + long[first] := 1 + long[last] := numlines + done := 1 + elseif (byte[str_0] == ";") + long[first] := linenum + long[last] := numlines + done := 1 + elseif (byte[str_0] == ",") + long[first] := long[last] := numlines + done := 1 + if (done) + str_0++ + long[cmd] := byte[str_0] + if (byte[str_0] and byte[str_0][1] == "!") + long[bang] := 1 + str_0++ + long[pstr] := str_0 + return 1 + str_0 := GetLineNumber(str_0, linenum, first) + if (long[first] == PARSING_ERROR) + return 0 + if (long[first] == UNSPECIFIED) + if (numlines == 0 and byte[str_0] == "i") + long[first] := long[last] := 1 + elseif (byte[str_0] == "g" or byte[str_0] == "v" or byte[str_0] == "w" or byte[str_0] == "x") + long[first] := 1 + long[last] := numlines + elseif (byte[str_0] == "j") + long[first] := linenum + long[last] := linenum + 1 + if (long[last] > numlines) + return 0 + elseif (byte[str_0] == "r") + long[first] := numlines + long[last] := numlines + elseif (byte[str_0] == 0 or byte[str_0] == "z") + long[first] := long[last] := linenum + 1 + if (long[first] > numlines) + return 0 + elseif (byte[str_0] == "=") + long[first] := long[last] := numlines + else + long[first] := linenum + long[last] := linenum + elseif (byte[str_0] == "," or byte[str_0] == ";") + str_0 := GetLineNumber(str_0 + 1, linenum, last) + if (long[last] == PARSING_ERROR) + return 0 + if (long[last] == UNSPECIFIED) + long[last] := long[first] + if (long[last] <(long[first])) + return 0 + else + long[last] := long[first] + long[cmd] := byte[str_0] + if (byte[str_0] and byte[str_0][1] == "!") + long[bang] := 1 + str_0++ + long[pstr] := str_0 + ' If first is zero, check if commands allows that + if (long[first] == 0) + str_0 := FindChar(string("aq=EerxQhVfP"), long[cmd]) + if (byte[str_0] == 0) + return 0 + return 1 +' Print a visualization mode screen + +PUB PrintScreen(linenum) | i, num, vlines1, vlines2, first, last, linenum1, linenum2 + vlines1 := vlines - 1 + vlines2 := vlines / 2 + first := topline + last := first + vlines1 + + if termtype == 1 + PrintScreenPST(linenum, vlines1, vlines2, first, last) + elseif termtype == 2 + PrintScreenANSI(linenum, vlines1, vlines2, first, last) + else + PrintScreenDumb(linenum, vlines1, vlines2, first, last) + + ' Print status line + if (retcode < 0) + c.printf1(string("E%d:"), - retcode) + statuslen := 2 + NumDigits(-retcode) + retcode := 0 + else + c.printf3(string("line %d, column %d, %d lines"), currline, dispcol, numlines) + statuslen := 22 + NumDigits(currline) + NumDigits(dispcol) + NumDigits(numlines) + if (filename[0]) + c.printf1(string(", %s"), @filename) + statuslen += strsize(@filename) + 2 + + if termtype == 2 + MoveCursorANSI(currline - topline + 1, dispcol) + +CON + PST_move_up = 5 + PST_move_down = 6 + PST_erase_line = 11 + PST_clear_screen = 0 + +PUB MoveCursorPST(row, col) + result := c.getconfig + c.setconfig((result & !3) | 1) + c.putchar(2) + c.putchar(col-1) + c.putchar(row-1) + c.setconfig(result) + +PUB PrintScreenPST(linenum, vlines1, vlines2, first, last) | linenum1, linenum2 + if (schange > 1) + if (schange == 2) + linenum2 := linenum1 := linenum + elseif (schange == 3) + linenum1 := linenum - 1 + linenum2 := linenum + else + linenum1 := linenum + linenum2 := linenum + 1 + ' Check if we need to only repaint the current line + if (schange > 1 and linenum1 => first and linenum2 =< last) + MoveCursorPST(linenum1 - topline + 1, 1) + c.putchar(PST_erase_line) + if (linenum1 <> linenum2) + c.putchar(PST_move_down) + c.putchar(PST_erase_line) + c.putchar(PST_move_up) + PrintLines(linenum, linenum1, linenum2, "v") + EraseStatusLine + else + ' Clear the screen + c.putchar(PST_clear_screen) + PrintScreenDumb(linenum, vlines1, vlines2, first, last) + +CON + OFFSET = 0 + +DAT + ANSI_move_up byte 27, "[", "A", 0 + ANSI_move_down byte 27, "[", "B", 0 + ANSI_erase_line byte 27, "[", "K", 0 + ANSI_scroll_up byte 27, "[", "9", "9", "B", 27, "D", 0 + ANSI_scroll_down byte 27, "M", 0 + ANSI_clear_screen byte 27, "[", "2", "J", 0 + +PUB MoveCursorANSI(row, col) + c.printf2(string("\033[%d;%dH"), row, col) + +PUB PrintScreenANSI(linenum, vlines1, vlines2, first, last) | i, num, linenum1, linenum2 + if (schange > 1) + if (schange == 2 or schange == 5) + linenum2 := linenum1 := linenum + elseif (schange == 3) + linenum1 := linenum - 1 + linenum2 := linenum + else + linenum1 := linenum + linenum2 := linenum + 1 + ' Check if we need to only repaint the current line + if (schange > 1 and linenum1 => first and linenum2 =< last) + if schange == 5 + EraseStatusLine + return + MoveCursorANSI(linenum1 - topline + 1, 1) + c.fputs(@ANSI_erase_line, stdout) + if (linenum1 <> linenum2) + c.fputs(@ANSI_move_down, stdout) + c.fputs(@ANSI_erase_line, stdout) + c.fputs(@ANSI_move_up, stdout) + PrintLines(linenum, linenum1, linenum2, "v") + EraseStatusLine + elseif schange == 5 and linenum == first - 1 + EraseStatusLine + MoveCursorANSI(1, 1) + c.fputs(@ANSI_scroll_down, stdout) + MoveCursorANSI(1, 1) + topline := --first + last := first + vlines1 + if last > numlines + last := numlines + PrintLines(linenum, linenum1, linenum2, "v") + EraseStatusLine + elseif schange == 5 and linenum == last + 1 + 'MoveCursorANSI(1, 1) + 'c.fputs(@ANSI_erase_line, stdout) + EraseStatusLine + c.fputs(@ANSI_scroll_up, stdout) + 'c.putchar(27) + 'c.putchar("D") + MoveCursorANSI(vlines, 1) + topline := ++first + last := first + vlines1 + if last > numlines + last := numlines + 'c.printf0(string("SU:")) + PrintLines(linenum, linenum1, linenum2, "v") + EraseStatusLine + else + ' Clear the screen + c.fputs(@ANSI_clear_screen, stdout) + MoveCursorANSI(1, 1) + PrintScreenDumb(linenum, vlines1, vlines2, first, last) + +PUB PrintScreenDumb(linenum, vlines1, vlines2, first, last) | i + ' Clear the screen + if termtype <> 1 and termtype <> 2 + i := 0 + repeat while (i < 50) + c.printf0(string("\n")) + i++ + ' Determine the first and last lines to display + if (first > currline) + first := currline + last := first + vlines1 + elseif (currline > last) + last := currline + first := last - vlines1 + if (first < 1) + first := 1 + if (last > numlines) + last := numlines + if (first > 0 and numlines > 0) + topline := first + PrintLines(linenum, first, last, "v") + else + last := first - 1 + i := last - first + repeat while (i < vlines1) + c.printf0(string("~\n")) + i++ + +' Empty a list and free the string links +PUB EmptyList(list) | next_1 + repeat while (list) + next_1 := long[list + next_0] + sfree(list) + list := next_1 +' Delete lines from the text file list and move them to the yank list + +PUB DeleteLines(first, last) | i, prev, curr, yank + curr := GetLinePtr(first, @prev) + yank := 0 + EmptyList(YankHead) + YankHead := 0 + i := first + repeat while (i =< last) + if (not curr) + c.printf1(string("DeleteLines: line = %d\n"), first) + return 1 + if (not yank) + YankHead := curr + else + long[yank + next_0] := curr + yank := curr + curr := long[curr + next_0] + numlines-- + i++ + if (prev) + long[prev + next_0] := curr + else + ListHead := curr + long[yank + next_0] := 0 + changed := 1 + if (first > numlines) + first := numlines + return first +' Copy lines from the text file list to the yank list + +PUB YankLines(first, last) | line, link, list, curr + list := 0 + curr := GetLinePtr(first, @link) + EmptyList(YankHead) + YankHead := 0 + line := first + repeat while (line =< last) + if (not curr) + c.printf1(string("YankLines: line = %d\n"), line) + return 1 + link := DuplicateString(curr) + if (not link) + quit + if (not list) + YankHead := link + else + long[list + next_0] := link + curr := long[curr + next_0] + list := link + line++ + if (list) + long[list + next_0] := 0 + return last +' Copy the lines on the yank list to the text file list + +PUB PushLines(line) | link, curr + curr := YankHead + repeat while (curr) + link := DuplicateString(curr) + if (not link) + quit + InsertLine(link,++line) + curr := long[curr + next_0] + changed := 1 + return line +' Go into the text input mode and append after "linenum" + +PUB AppendLines(linenum) | prev, curr, link + curr := GetLinePtr(linenum + 1, @prev) + repeat while (1) + c.gets(@inbuf) + RemoveCRLF(@inbuf) + if (inbuf[0] == "." and inbuf[1] == 0) + quit + link := AllocateString(@inbuf) + if (not link) + quit + long[link + next_0] := curr + if (prev) + long[prev + next_0] := link + else + ListHead := link + prev := link + linenum++ + numlines++ + changed := 1 + return linenum +' Go into the text input mode and insert lines before "linenum" + +PUB InsertLines(linenum) | linenum1 + linenum1 := AppendLines(linenum - 1) + if (linenum1 > linenum) + linenum := linenum1 + return linenum +' Join lines together and move the original lines to the yank buffer + +PUB JoinLines(first, last) | i, len, str_0, prev, curr, link, yank + len := 0 + curr := GetLinePtr(first, @prev) + link := curr + yank := 0 + EmptyList(YankHead) + YankHead := 0 + ' Determine the total length of the selected lines + i := first + repeat while (i =< last) + if (not link) + c.printf1(string("JoinLines: line = %d\n"), i) + return 1 + len += strsize((link + str)) + link := long[link + next_0] + i++ + ' Allocate a new string buffer and concatenate the strings + link := smalloc(len + 1) + if (not link) + return first + str_0 :=(link + str) + byte[str_0] := 0 + i := first + repeat while (i =< last) + if (not curr) + c.printf1(string("JoinLines: line = %d\n"), i) + return 1 + c.strcat(str_0,(curr + str)) + if (not yank) + YankHead := curr + else + long[yank + next_0] := curr + yank := curr + curr := long[curr + next_0] + numlines-- + changed := 1 + i++ + ' Insert the new string + if (prev) + long[prev + next_0] := link + else + ListHead := link + long[link + next_0] := curr + long[yank + next_0] := 0 + numlines++ + return first +' Move or copy lines to after the destination address + +PUB MoveLines(first, last, dstaddr, cmd) | i, prev, curr, link, srcaddr, insertbefore + if (dstaddr < 0 or dstaddr > numlines or(first =< dstaddr and dstaddr < last)) + c.printf0(string("?\n")) + else + srcaddr := first + insertbefore := dstaddr < first + i := first + repeat while (i =< last) + curr := GetLinePtr(srcaddr++, @prev) + link := DuplicateString(curr) + if (not link) + quit + InsertLine(link,++dstaddr) + if (insertbefore) + srcaddr++ + if (cmd == "m") + DeleteLine(--srcaddr) + if (not insertbefore) + dstaddr-- + i++ + changed := 1 + return dstaddr +' Read a file after "linenum" + +PUB ReadFile(fname, linenum) | numchar, infile, link + numchar := 0 + infile := c.fopen(fname, string("r")) + if (infile) + repeat while (c.fgets(@inbuf, MAX_CHARS, infile)) + RemoveCRLF(@inbuf) + link := AllocateString(@inbuf) + if (not link) + quit + InsertLine(link,++linenum) + numchar += strsize(@inbuf) + c.fclose(infile) + c.printf1(string("%d\n"), numchar) + return linenum +' Write lines first to last to the output file + +PUB WriteFile(fname, first, last) | i, LinePtr_0, curr, prev, numchar, outfile + numchar := 0 + outfile := c.fopen(fname, string("w")) + if (not outfile) + return - 1 + i := first + repeat while (i =< last) + curr := GetLinePtr(i, @prev) + LinePtr_0 :=(curr + str) + numchar += strsize(LinePtr_0) + c.fprintf1(outfile, string("%s\n"), LinePtr_0) + i++ + c.fclose(outfile) + c.printf1(string("%d\n"), numchar) + if (first == 1 and last == numlines) + changed := 0 + return 0 +' Execute a command + +PUB ExecuteCommand | curr, prev, LinePtr_0, outfile, inptr, first, last, cmd, i, temp, numchar, bang + inptr := @inbuf + ' Parse the command and extract the parameters + if (not ParseCommand(@inptr, currline, @first, @last, @cmd, @bang)) + return - 2 + ' Parse a "g" or "v" command if present + if (cmd == "g" or cmd == "v") + inptr++ + if (GetString(@inptr)) + if (cmd == "g") + gflag := 1 + else + gflag := 2 + cmd := byte[inptr] + if (cmd == 0) + cmd := "p" + c.printf1(string("cmd = %c\n"), cmd) + else + return - 3 + ' Select the command and execute it + case(cmd) + 0 : + ' Print the current line if no command + if (numlines == 0) + return - 4 + if (last => 0) + currline := last + elseif (first => 0) + currline := first + curr := GetLinePtr(currline, @prev) + LinePtr_0 :=(curr + str) + c.printf1(string("%s\n"), LinePtr_0) + "q" : + if (changed == 0 or bang) + return 1 + return - 5 + "e" : + if (changed and bang == 0) + return - 6 + inptr := SkipChar(inptr + 1, " ") + if (byte[inptr] == 0 and filename == 0) + return - 7 + if (byte[inptr]) + c.strcpy(@filename, inptr) + EmptyList(YankHead) + YankHead := 0 + EmptyList(ListHead) + ListHead := 0 + numlines := 0 + currline := ReadFile(@filename, 0) + changed := 0 + "r" : + ' Read the contents of a file after the specified line + inptr := SkipChar(inptr + 1, " ") + if (byte[inptr] == 0 and filename == 0) + return - 8 + if (byte[inptr]) + if (not filename[0]) + c.strcpy(@filename, inptr) + else + inptr := @filename + temp := numlines + currline := ReadFile(inptr, first) + if (temp and numlines <> temp) + changed := 1 + "x", "w" : + ' Write the text buffer to a file + inptr := SkipChar(inptr + 1, " ") + if (byte[inptr] == 0) + if (filename == 0) + return - 9 + inptr := @filename + if (WriteFile(inptr, first, last) < 0) + return - 10 + if (cmd == "x") + return 1 + if (filename == 0) + c.strcpy(@filename, inptr) + "d" : + ' Delete the specified lines + currline := DeleteLines(first, last) + "m", "t" : + ' Move or copy the specified lines + if (byte[inptr][1]) + c.sscanf1(inptr + 1, string("%d"), @temp) + else + temp := currline + currline := MoveLines(first, last, temp, cmd) + "c" : + ' Change the specified lines + temp :=(last == numlines) + currline := DeleteLines(first, last) + if (temp) + currline := AppendLines(currline) + else + currline := InsertLines(currline) + "a" : + ' Append text after the specified line + currline := AppendLines(first) + "i" : + ' Insert text before the specified line + currline := InsertLines(first) + "j" : + ' Join together lines + currline := JoinLines(first, last) + "p", "l", "#" : + ' Print lines in either the "p", "l" or "n" format + currline := PrintLines(currline, first, last, cmd) + "V" : + ' Set the number of lines in the visualization mode or + ' toggle the visualization mode if no lines are specified + if (byte[inptr][1]) + c.sscanf1(inptr + 1, string("%d"), @temp) + if (temp =< 0) + return - 11 + vlines := temp - 1 + vmode := 1 + else + vmode := 1 + "T" : + ' Set the terminal type, or toggle the terminal + ' type if no parameter is specified + if (byte[inptr][1]) + c.sscanf1(inptr + 1, string("%d"), @termtype) + else + termtype := (termtype + 1) // 3 + "s" : + ' Replace text in the specified lines + currline := ReplaceStringCommand(inptr, first, last, currline) + "P" : + ' Push text from the yank buffer + currline := PushLines(first) + "y" : + ' Yank lines to the yank buffer + currline := YankLines(first, last) + "=" : + ' Print the number of lines, or the line number if specified + c.printf1(string("%d\n"), last) + OTHER : + ' Return error code if not a valid command + return - 14 + return 0 + +PUB SetDispCol | len, curr + curr := GetLinePtr(currline, @curr) + if (curr) + len := strsize((curr + str)) + if (currcol > len) + dispcol := len + else + dispcol := currcol + if (dispcol < 1) + dispcol := 1 + +PUB NumDigits(num) + result := 1 + repeat while num > 9 + result++ + num /= 10 + +PUB EraseStatusLine | i + if (termtype == 1) + MoveCursorPST(vlines + 1, 1) + c.putchar(PST_erase_line) + elseif termtype == 2 + MoveCursorANSI(vlines + 1, 1) + c.fputs(@ANSI_erase_line, stdout) + else + i := 0 + repeat while (i < statuslen) + c.putchar(8) + i++ + i := 0 + repeat while (i < statuslen) + c.putchar(" ") + i++ + i := 0 + repeat while (i < statuslen) + c.putchar(8) + i++ + +PUB ExecuteViCommand(cmd) | temp2, index, val, inptr, temp, link, i, len, curr + inptr := @inbuf + temp := currline + val := 0 + + ' Check for repeat command + if cmd == "." + ifnot lastcmd + return + cmd := lastcmd + ifnot count + count := lastcnt + if cmd == "d" + cmd0 := "d" + elseif cmd == "r" + val := lastval + else + cmd0 := 0 + + case(cmd) + "Q" : + vmode := 0 + count := 0 + schange := 1 + prompt := ":" + EraseStatusLine + "Y" : + if (count =< 1) + temp2 := currline + else + temp2 := currline + count - 1 + if (temp2 > numlines) + temp2 := numlines + YankLines(currline, temp2) + lastcmd := cmd + lastcnt := count + count := 0 + "p" : + currline := PushLines(currline) + schange :=(currline <> temp) + lastcmd := cmd + lastcnt := count + count := 0 + ":" : + vmode := - 1 + count := 0 + schange := 1 + prompt := cmd + EraseStatusLine + "/", "?" : + srchdir := (cmd == "/") + EraseStatusLine + c.putchar(cmd) + result := c.getconfig + c.setconfig(result & !3) + c.gets(inptr) + c.setconfig(result) + VisualSearchString(inptr, srchdir) + count := 0 + "n" : + byte[inptr] := 0 + VisualSearchString(inptr, srchdir) + count := 0 + "N" : + byte[inptr] := 0 + VisualSearchString(inptr, not srchdir) + count := 0 + 13, "j" : + if (currline < numlines) + currline++ + if termtype == 2 + schange := 5 + else + schange := 3 + curr := GetLinePtr(currline, @curr) + if (curr) + len := strsize((curr + str)) + if (currcol > len) + dispcol := len + else + dispcol := currcol + if (dispcol < 1) + dispcol := 1 + count := 0 + "k" : + if (currline > 1) + currline-- + if termtype == 2 + schange := 5 + else + schange := 4 + curr := GetLinePtr(currline, @curr) + if (curr) + len := strsize((curr + str)) + if (currcol > len) + dispcol := len + else + dispcol := currcol + if (dispcol < 1) + dispcol := 1 + count := 0 + "h" : + if (dispcol > 1) + dispcol-- + currcol := dispcol + if termtype == 2 + schange := 5 + else + schange := 2 + count := 0 + "l" : + curr := GetLinePtr(currline, @curr) + if (curr) + len := strsize((curr + str)) + else + len := 0 + if (dispcol < len) + dispcol++ + currcol := dispcol + if termtype == 2 + schange := 5 + else + schange := 2 + count := 0 + 6 : + ' ^F + currline += vlines + if (currline > numlines) + currline := numlines + schange :=(currline <> temp) + SetDispCol + count := 0 + 2 : + ' ^B + currline -= vlines + if (currline < 1) + currline := 1 + schange :=(currline <> temp) + SetDispCol + count := 0 + 4 : + ' ^D + currline += vlines / 2 + if (currline > numlines) + currline := numlines + schange :=(currline <> temp) + SetDispCol + count := 0 + 21 : + ' ^U + currline -= vlines / 2 + if (currline < 1) + currline := 1 + schange :=(currline <> temp) + SetDispCol + count := 0 + "G" : + if (count => 1 and count =< numlines) + currline := count + elseif (count == 0) + currline := numlines + schange :=(currline <> temp) + count := 0 + SetDispCol + "J" : + if (count =< 1) + temp2 := currline + 1 + else + temp2 := currline + count + if (temp2 > numlines) + temp2 := numlines + currline := JoinLines(currline, temp2) + lastcmd := cmd + lastcnt := count + count := 0 + cmd0 := 0 + schange := 1 + "d" : + if (cmd0 == "d") + if (count =< 1) + temp2 := currline + else + temp2 := currline + count - 1 + if (temp2 > numlines) + temp2 := numlines + currline := DeleteLines(currline, temp2) + lastcmd := cmd + lastcnt := count + count := 0 + cmd0 := 0 + schange := 1 + SetDispCol + return + "x" : + if (curr := GetLinePtr(currline, @curr)) + inptr :=(curr + str) + len := strsize(inptr) + lastcmd := cmd + lastcnt := count + if count =< 0 + count := 1 + if (len) + changed := 1 + index := dispcol - 1 + if dispcol + count - 1 > len + count := len - dispcol + 1 + len -= count + repeat while (index =< len) + byte[inptr][index] := byte[inptr][index + count] + index++ + schange := 2 + if (dispcol > len and len > 0) + dispcol := len + currcol := dispcol + count := 0 + "r" : + if (curr := GetLinePtr(currline, @curr)) + inptr :=(curr + str) + len := strsize(inptr) + ifnot val + val := c.getchar + lastcmd := cmd + lastcnt := count + lastval := val + if (val <> 27) + schange := 2 + changed := 1 + if (len) + index := dispcol - 1 + byte[inptr][index] := val + else + link := AllocateString(string(" ")) + byte[link + str][0] := val + InsertLine(link, currline) + DeleteLine(currline + 1) + currcol := dispcol + count := 0 + "O", "A", "I", "i", "a" : + count := 0 + changed := 1 + if (numlines == 0) + index := 0 + inbuf[0] := 0 + len := 0 + currline := 1 + schange := 1 + elseif (cmd == "O") + index := 0 + inbuf[0] := 0 + len := 0 + schange := 1 + else + link := GetLinePtr(currline, @link) + c.strcpy(@inbuf,(link + str)) + len := strsize(@inbuf) + if (cmd == "A") + index := len + else + if (len == 0 or cmd == "I") + index := 0 + else + index := dispcol + if (cmd == "i") + index-- + DeleteLine(currline) + schange := 2 + currcol := dispcol := index + 1 + repeat while (1) + link := AllocateString(@inbuf) + InsertLine(link, currline) + PrintScreen(currline) + val := c.getchar + if (val == 27) + if (len > 0 and index => len) + index-- + schange := 2 + dispcol := currcol := index + 1 + quit + if (val == 8) + if (index > 0) + index-- + i := index + repeat while (i < len) + inbuf[i] := inbuf[i + 1] + i++ + len-- + DeleteLine(currline) + schange := 2 + elseif (val == 13) + if (index == 0) + link := AllocateString(@nullstr) + InsertLine(link, currline) + currline++ + DeleteLine(currline) + else + i := len + 1 + repeat while (i > index) + inbuf[i] := inbuf[i - 1] + i-- + inbuf[index++] := 0 + len++ + link := AllocateString(@inbuf) + InsertLine(link, currline) + currline++ + DeleteLine(currline) + len -= index + i := 0 + repeat while (i =< len) + inbuf[i] := inbuf[i + index] + i++ + index := 0 + schange := 1 + else + i := len + 1 + repeat while (i > index) + inbuf[i] := inbuf[i - 1] + i-- + inbuf[index++] := val + len++ + DeleteLine(currline) + schange := 2 + dispcol := currcol := index + 1 + "$" : + curr := GetLinePtr(currline, @curr) + if (curr) + len := strsize((curr + str)) + if (not len) + len++ + if (len <> dispcol) + dispcol := len + currcol := dispcol + schange := 1 + count := 0 + OTHER : + if (c.isdigit(cmd)) + if (count == 0 and cmd == "0") + if (dispcol <> 1) + dispcol := 1 + currcol := dispcol + schange := 1 + count :=(count * 10) + cmd - "0" + cmd0 := cmd + +PUB FindNextString(dir, newline) | curr, prev, lineptr, len + if (curr := GetLinePtr(currline, @prev)) + lineptr := curr + str + len := strsize(lineptr) + if dir + if newline + dispcol := currcol := 1 + if dispcol < len + prev := FindString(lineptr + dispcol, @srchstr) + if prev + result := dispcol + prev + else + if newline + dispcol := currcol := len + 1 + if dispcol > 1 + prev := FindStringBackward(lineptr, @srchstr, dispcol - 1) + if prev + result := prev + +PUB VisualSearchString(ptr, dir) | newline, oldline, newcol + if byte[ptr] + c.strcpy(@srchstr, ptr) + + ' Search current line + if (newcol := FindNextString(dir, 0)) + if termtype == 2 + schange := 5 + else + schange := 1 + currcol := dispcol := newcol + return + + ' Search other lines + c.strcpy(ptr, string("//")) + oldline := currline + if dir + newline := SearchStringForward(@ptr, currline) + else + newline := SearchStringBackward(@ptr, currline) + + ' Search for first occurance on new line + if newline > 0 and newline <> currline + if termtype == 2 + schange := 5 + else + schange := 1 + currline := newline + if (newcol := FindNextString(dir, 1)) + currcol := dispcol := newcol + else + if termtype == 2 + schange := 5 + else + schange := 2 + +'****************************************************************************** +' String Allocation Routines +' Copyright (c) 2011, Dave Hein +' See end of file for terms of use. +'****************************************************************************** +VAR + long smfreelist + +PUB smallocinit(ptr, size_0) | curr + curr := ptr + smfreelist := curr + long[curr + next_0] := 0 + long[curr + size] := size_0 +' Allocate a block of "size" bytes. Return a pointer to the block if +' successful, or zero if a large enough memory block could not be found + +PUB smalloc(size_0) | prev, curr, next_1 + 'printf("smalloc: %d\n", size); + if (size_0 =< 0) + return 0 + ' Adjust size to nearest long plus the header size + size_0 :=((size_0 + 3) &(! 3)) + HDR_SIZE + ' Search for a block of memory + prev := 0 + curr := smfreelist + repeat while (curr) + if (long[curr + size] => size_0) + quit + prev := curr + curr := long[curr + next_0] + ' Return null if block not found + if (curr == 0) + c.printf0(string("Out of memory!\n")) + return 0 + ' Get next pointer + next_1 := long[curr + next_0] + ' Split block if larger than needed + if (long[curr + size] => size_0 + HDR_SIZE + 16) + next_1 :=(((curr) + size_0)) + long[next_1 + next_0] := long[curr + next_0] + long[next_1 + size] := long[curr + size] - size_0 + long[curr + next_0] := next_1 + long[curr + size] := size_0 + ' Remove block from the free list + long[curr + next_0] := 0 + if (prev) + long[prev + next_0] := next_1 + else + smfreelist := next_1 + return curr +' Insert a memory block back into the free list. Merge blocks together if +' the memory block is contiguous with other blocks on the list. + +PUB sfree(curr) | prev, next_1 + prev := 0 + next_1 := smfreelist + ' Find Insertion Point + repeat while (next_1) + if (curr => prev and curr =< next_1) + quit + prev := next_1 + next_1 := long[next_1 + next_0] + ' Merge with the previous block if contiguous + if (prev and(((prev) + long[prev + size])) == curr) + long[prev + size] += long[curr + size] + ' Also merge with next block if contiguous + if ((((prev) + long[prev + size])) == next_1) + long[prev + size] += long[next_1 + size] + long[prev + next_0] := long[next_1 + next_0] + ' Merge with the next block if contiguous + elseif (next_1 and(((curr) + long[curr + size])) == next_1) + long[curr + size] += long[next_1 + size] + long[curr + next_0] := long[next_1 + next_0] + if (prev) + long[prev + next_0] := curr + else + smfreelist := curr + ' Insert in the middle of the free list if not contiguous + elseif (prev) + long[prev + next_0] := curr + long[curr + next_0] := next_1 + ' Otherwise, insert at beginning of the free list + else + smfreelist := curr + long[curr + next_0] := next_1 +{ ++----------------------------------------------------------------------------+ +| TERMS OF USE: MIT License | ++----------------------------------------------------------------------------+ +|Permission is hereby granted, free of charge, to any person obtaining a copy| +|of this software and associated documentation files (the "Software"), to | +|deal in the Software without restriction, including without limitation the | +|rights to use, copy, modify, merge, publish, distribute, sublicense, and/or | +|sell copies of the Software, and to permit persons to whom the Software is | +|furnished to do so, subject to the following conditions: | +| | +|The above copyright notice and this permission notice shall be included in | +|all copies or substantial portions of the Software. | +| | +|THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | +|IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | +|FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE | +|AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | +|LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING | +|FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS| +|IN THE SOFTWARE. | ++----------------------------------------------------------------------------+ +} diff --git a/src/wc.spin b/src/wc.spin new file mode 100644 index 0000000..ae0f347 --- /dev/null +++ b/src/wc.spin @@ -0,0 +1,88 @@ +'****************************************************************************** +' Copyright (c) 2013, Dave Hein +' See end of file for terms of use. +'****************************************************************************** +CON + _clkmode = xinput + _xinfreq = $4e495053 ' SPIN + +OBJ + c : "clibsd" + g : "glob" + +PUB main(argc, argv) | i, j, infile, num, buffer[5], str[25], fname, globdata, numchar, numline, numword, val, wordflag, totchar, totline, totword, totfile + + c.enter + + if argc < 2 + c.printf0(string("usage: wc [FILE]...\n")) + + i := 1 + totfile := totchar := totline := totword := 0 + repeat + globdata := 0 + repeat while (fname := g.glob(long[argv][i], @globdata, @str)) + infile := c.fopen(fname, string("r")) + if (infile == 0) + c.printf1(string("Error opening %s\n"), fname) + c.exit(1) + if c.getmod(fname) & $10 ' Check if it's a directory + c.printf1(string("%s is a directory\n"), fname) + c.fclose(infile) + next + + ' Read 20 characters at a time + wordflag := numchar := numline := numword := 0 + repeat while ((num := c.fread(@buffer, 1, 20, infile)) > 0) + numchar += num + repeat j from 0 to num - 1 + val := byte[@buffer][j] + if val == 10 + numline++ + wordflag := 0 + elseif whitespace(val) + wordflag := 0 + elseifnot wordflag + wordflag := 1 + numword++ + + c.fclose(infile) + c.printf4(string("%4d %5d %6d %s\n"), numline, numword, numchar, fname) + totline += numline + totword += numword + totchar += numchar + totfile++ + + while (++i < argc) + + if totfile > 1 + c.printf3(string("%4d %5d %6d total\n"), totline, totword, totchar) + + c.exit(0) + +PUB whitespace(val) + return val =< 32 + +{{ ++-----------------------------------------------------------------------------+ +| TERMS OF USE: MIT License | ++-----------------------------------------------------------------------------+ +|Permission is hereby granted, free of charge, to any person obtaining a copy | +|of this software and associated documentation files (the "Software"), to deal| +|in the Software without restriction, including without limitation the rights | +|to use, copy, modify, merge, publish, distribute, sublicense, and/or sell | +|copies of the Software, and to permit persons to whom the Software is | +|furnished to do so, subject to the following conditions: | +| | +|The above copyright notice and this permission notice shall be included in | +|all copies or substantial portions of the Software. | +| | +|THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | +|IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | +|FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE | +|AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | +|LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,| +|OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE| +|SOFTWARE. | ++-----------------------------------------------------------------------------+ +}} diff --git a/vga512/FullDuplexSerial.spin b/vga512/FullDuplexSerial.spin new file mode 100644 index 0000000..f731c4d --- /dev/null +++ b/vga512/FullDuplexSerial.spin @@ -0,0 +1,322 @@ +''******************************************** +''* Full-Duplex Serial Driver v1.2 * +''* Author: Chip Gracey, Jeff Martin * +''* Copyright (c) 2006-2009 Parallax, Inc. * +''* See end of file for terms of use. * +''******************************************** + +{-----------------REVISION HISTORY----------------- + v1.2 - 5/7/2009 fixed bug in dec method causing largest negative value (-2,147,483,648) to be output as -0. + v1.1 - 3/1/2006 first official release. +} + + +VAR + + long cog 'cog flag/id + + long rx_head '9 contiguous longs + long rx_tail + long tx_head + long tx_tail + long rx_pin + long tx_pin + long rxtx_mode + long bit_ticks + long buffer_ptr + + byte rx_buffer[16] 'transmit and receive buffers + byte tx_buffer[16] + + +PUB start(rxpin, txpin, mode, baudrate) : okay + +'' Start serial driver - starts a cog +'' returns false if no cog available +'' +'' mode bit 0 = invert rx +'' mode bit 1 = invert tx +'' mode bit 2 = open-drain/source tx +'' mode bit 3 = ignore tx echo on rx + + stop + longfill(@rx_head, 0, 4) + longmove(@rx_pin, @rxpin, 3) + bit_ticks := clkfreq / baudrate + buffer_ptr := @rx_buffer + okay := cog := cognew(@entry, @rx_head) + 1 + + +PUB stop + +'' Stop serial driver - frees a cog + + if cog + cogstop(cog~ - 1) + longfill(@rx_head, 0, 9) + + +PUB rxflush + +'' Flush receive buffer + + repeat while rxcheck => 0 + + +PUB rxcheck : rxbyte + +'' Check if byte received (never waits) +'' returns -1 if no byte received, $00..$FF if byte + + rxbyte-- + if rx_tail <> rx_head + rxbyte := rx_buffer[rx_tail] + rx_tail := (rx_tail + 1) & $F + + +PUB rxtime(ms) : rxbyte | t + +'' Wait ms milliseconds for a byte to be received +'' returns -1 if no byte received, $00..$FF if byte + + t := cnt + repeat until (rxbyte := rxcheck) => 0 or (cnt - t) / (clkfreq / 1000) > ms + + +PUB rx : rxbyte + +'' Receive byte (may wait for byte) +'' returns $00..$FF + + repeat while (rxbyte := rxcheck) < 0 + + +PUB tx(txbyte) + +'' Send byte (may wait for room in buffer) + + repeat until (tx_tail <> (tx_head + 1) & $F) + tx_buffer[tx_head] := txbyte + tx_head := (tx_head + 1) & $F + + if rxtx_mode & %1000 + rx + + +PUB str(stringptr) + +'' Send string + + repeat strsize(stringptr) + tx(byte[stringptr++]) + + +PUB dec(value) | i, x + +'' Print a decimal number + + x := value == NEGX 'Check for max negative + if value < 0 + value := ||(value+x) 'If negative, make positive; adjust for max negative + tx("-") 'and output sign + + i := 1_000_000_000 'Initialize divisor + + repeat 10 'Loop for 10 digits + if value => i + tx(value / i + "0" + x*(i == 1)) 'If non-zero digit, output digit; adjust for max negative + value //= i 'and digit from value + result~~ 'flag non-zero found + elseif result or i == 1 + tx("0") 'If zero digit (or only digit) output it + i /= 10 'Update divisor + + +PUB hex(value, digits) + +'' Print a hexadecimal number + + value <<= (8 - digits) << 2 + repeat digits + tx(lookupz((value <-= 4) & $F : "0".."9", "A".."F")) + + +PUB bin(value, digits) + +'' Print a binary number + + value <<= 32 - digits + repeat digits + tx((value <-= 1) & 1 + "0") + + +DAT + +'*********************************** +'* Assembly language serial driver * +'*********************************** + + org +' +' +' Entry +' +entry mov t1,par 'get structure address + add t1,#4 << 2 'skip past heads and tails + + rdlong t2,t1 'get rx_pin + mov rxmask,#1 + shl rxmask,t2 + + add t1,#4 'get tx_pin + rdlong t2,t1 + mov txmask,#1 + shl txmask,t2 + + add t1,#4 'get rxtx_mode + rdlong rxtxmode,t1 + + add t1,#4 'get bit_ticks + rdlong bitticks,t1 + + add t1,#4 'get buffer_ptr + rdlong rxbuff,t1 + mov txbuff,rxbuff + add txbuff,#16 + + test rxtxmode,#%100 wz 'init tx pin according to mode + test rxtxmode,#%010 wc + if_z_ne_c or outa,txmask + if_z or dira,txmask + + mov txcode,#transmit 'initialize ping-pong multitasking +' +' +' Receive +' +receive jmpret rxcode,txcode 'run a chunk of transmit code, then return + + test rxtxmode,#%001 wz 'wait for start bit on rx pin + test rxmask,ina wc + if_z_eq_c jmp #receive + + mov rxbits,#9 'ready to receive byte + mov rxcnt,bitticks + shr rxcnt,#1 + add rxcnt,cnt + +:bit add rxcnt,bitticks 'ready next bit period + +:wait jmpret rxcode,txcode 'run a chuck of transmit code, then return + + mov t1,rxcnt 'check if bit receive period done + sub t1,cnt + cmps t1,#0 wc + if_nc jmp #:wait + + test rxmask,ina wc 'receive bit on rx pin + rcr rxdata,#1 + djnz rxbits,#:bit + + shr rxdata,#32-9 'justify and trim received byte + and rxdata,#$FF + test rxtxmode,#%001 wz 'if rx inverted, invert byte + if_nz xor rxdata,#$FF + + rdlong t2,par 'save received byte and inc head + add t2,rxbuff + wrbyte rxdata,t2 + sub t2,rxbuff + add t2,#1 + and t2,#$0F + wrlong t2,par + + jmp #receive 'byte done, receive next byte +' +' +' Transmit +' +transmit jmpret txcode,rxcode 'run a chunk of receive code, then return + + mov t1,par 'check for head <> tail + add t1,#2 << 2 + rdlong t2,t1 + add t1,#1 << 2 + rdlong t3,t1 + cmp t2,t3 wz + if_z jmp #transmit + + add t3,txbuff 'get byte and inc tail + rdbyte txdata,t3 + sub t3,txbuff + add t3,#1 + and t3,#$0F + wrlong t3,t1 + + or txdata,#$100 'ready byte to transmit + shl txdata,#2 + or txdata,#1 + mov txbits,#11 + mov txcnt,cnt + +:bit test rxtxmode,#%100 wz 'output bit on tx pin according to mode + test rxtxmode,#%010 wc + if_z_and_c xor txdata,#1 + shr txdata,#1 wc + if_z muxc outa,txmask + if_nz muxnc dira,txmask + add txcnt,bitticks 'ready next cnt + +:wait jmpret txcode,rxcode 'run a chunk of receive code, then return + + mov t1,txcnt 'check if bit transmit period done + sub t1,cnt + cmps t1,#0 wc + if_nc jmp #:wait + + djnz txbits,#:bit 'another bit to transmit? + + jmp #transmit 'byte done, transmit next byte +' +' +' Uninitialized data +' +t1 res 1 +t2 res 1 +t3 res 1 + +rxtxmode res 1 +bitticks res 1 + +rxmask res 1 +rxbuff res 1 +rxdata res 1 +rxbits res 1 +rxcnt res 1 +rxcode res 1 + +txmask res 1 +txbuff res 1 +txdata res 1 +txbits res 1 +txcnt res 1 +txcode res 1 + +{{ + +┌──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┐ +│ TERMS OF USE: MIT License │ +├──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┤ +│Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation │ +│files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, │ +│modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software│ +│is furnished to do so, subject to the following conditions: │ +│ │ +│The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.│ +│ │ +│THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE │ +│WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR │ +│COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, │ +│ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. │ +└──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┘ +}} \ No newline at end of file diff --git a/vga512/VGA_512x384_Bitmap.spin b/vga512/VGA_512x384_Bitmap.spin new file mode 100644 index 0000000..5af6abd --- /dev/null +++ b/vga512/VGA_512x384_Bitmap.spin @@ -0,0 +1,224 @@ +''******************************************** +''* VGA 512x384 2-Color Bitmap Driver v1.0 * +''* Author: Chip Gracey * +''* Copyright (c) 2006 Parallax, Inc. * +''* See end of file for terms of use. * +''******************************************** +'' +'' This object generates a 512x384 pixel bitmap, signaled as 1024x768 VGA. +'' Each pixel is one bit, so the entire bitmap requires 512 x 384 / 32 longs, +'' or 6,144 longs (24KB). Color words comprised of two byte fields provide +'' unique colors for every 32x32 pixel group. These color words require 512/32 +'' * 384/32 words, or 192 words. Pixel memory and color memory are arranged +'' left-to-right then top-to-bottom. +'' +'' A sync indicator signals each time the screen is drawn (you may ignore). +'' +'' You must provide buffers for the colors, pixels, and sync. Once started, +'' all interfacing is done via memory. To this object, all buffers are read- +'' only, with the exception of the sync indicator which gets written with a +'' non-0 value. You may freely write all buffers to affect screen appearance. +'' + +CON + +' 512x384 settings - signals as 1024 x 768 @ 67Hz + + hp = 512 'horizontal pixels + vp = 384 'vertical pixels + hf = 8 'horizontal front porch pixels + hs = 48 'horizontal sync pixels + hb = 88 'horizontal back porch pixels + vf = 1 'vertical front porch lines + vs = 3 'vertical sync lines + vb = 28 'vertical back porch lines + hn = 1 'horizontal normal sync state (0|1) + vn = 1 'vertical normal sync state (0|1) + pr = 35 'pixel rate in MHz at 80MHz system clock (5MHz granularity) + +' Tiles + + xtiles = hp / 32 + ytiles = vp / 32 + +' H/V inactive states + + hv_inactive = (hn << 1 + vn) * $0101 + + +VAR long cog + +PUB start(BasePin, ColorPtr, PixelPtr, SyncPtr) : okay | i, j + +'' Start VGA driver - starts a COG +'' returns false if no COG available +'' +'' BasePin = VGA starting pin (0, 8, 16, 24, etc.) +'' +'' ColorPtr = Pointer to 192 words which define the "0" and "1" colors for +'' each 32x32 pixel group. The lower byte of each word contains +'' the "0" bit RGB data while the upper byte of each word contains +'' the "1" bit RGB data for the associated group. The RGB +'' data in each byte is arranged as %RRGGBB00 (4 levels each). +'' +'' color word example: %%0020_3300 = "0" = gold, "1" = blue +'' +'' PixelPtr = Pointer to 6,144 longs containing pixels that make up the 512 x +'' 384 pixel bitmap. Longs' LSBs appear left on the screen, while +'' MSBs appear right. The longs are arranged in sequence from left- +'' to-right, then top-to-bottom. +'' +'' SyncPtr = Pointer to long which gets written with non-0 upon each screen +'' refresh. May be used to time writes/scrolls, so that chopiness +'' can be avoided. You must clear it each time if you want to see +'' it re-trigger. + + 'if driver is already running, stop it + stop + + 'implant pin settings and pointers, then launch COG + reg_vcfg := $200000FF + (BasePin & %111000) << 6 + i := $FF << (BasePin & %011000) + j := BasePin & %100000 == 0 + reg_dira := i & j + reg_dirb := i & !j + longmove(@color_base, @ColorPtr, 2) + if (cog := cognew(@init, SyncPtr) + 1) + return true + + +PUB stop | i + +'' Stop VGA driver - frees a COG + + if cog + cogstop(cog~ - 1) + + +DAT + +'*********************************************** +'* Assembly language VGA 2-color bitmap driver * +'*********************************************** + + org 'set origin to $000 for start of program + +' Initialization code - init I/O + +init mov dira,reg_dira 'set pin directions + mov dirb,reg_dirb + + movi ctra,#%00001_101 'enable PLL in ctra (VCO runs at 4x) + movi frqa,#(pr / 5) << 3 'set pixel rate + + mov vcfg,reg_vcfg 'set video configuration + +' Main loop, display field and do invisible sync lines + +field mov color_ptr,color_base 'reset color pointer + mov pixel_ptr,pixel_base 'reset pixel pointer + mov y,#ytiles 'set y tiles +:ytile mov yl,#32 'set y lines per tile +:yline mov yx,#2 'set y expansion +:yexpand mov x,#xtiles 'set x tiles + mov vscl,vscl_pixel 'set pixel vscl + +:xtile rdword color,color_ptr 'get color word + and color,colormask 'clear h/v bits + or color,hv 'set h/v inactive states + rdlong pixel,pixel_ptr 'get pixel long + waitvid color,pixel 'pass colors and pixels to video + add color_ptr,#2 'point to next color word + add pixel_ptr,#4 'point to next pixel long + djnz x,#:xtile 'another x tile? + + sub color_ptr,#xtiles * 2 'repoint to first colors in same line + sub pixel_ptr,#xtiles * 4 'repoint to first pixels in same line + + mov x,#1 'do horizontal sync + call #hsync + + djnz yx,#:yexpand 'y expand? + + add pixel_ptr,#xtiles * 4 'point to first pixels in next line + djnz yl,#:yline 'another y line in same tile? + + add color_ptr,#xtiles * 2 'point to first colors in next tile + djnz y,#:ytile 'another y tile? + + + wrlong colormask,par 'visible done, write non-0 to sync + + mov x,#vf 'do vertical front porch lines + call #blank + mov x,#vs 'do vertical sync lines + call #vsync + mov x,#vb 'do vertical back porch lines + call #vsync + + jmp #field 'field done, loop + + +' Subroutine - do blank lines + +vsync xor hvsync,#$101 'flip vertical sync bits + +blank mov vscl,hvis 'do blank pixels + waitvid hvsync,#0 +hsync mov vscl,#hf 'do horizontal front porch pixels + waitvid hvsync,#0 + mov vscl,#hs 'do horizontal sync pixels + waitvid hvsync,#1 + mov vscl,#hb 'do horizontal back porch pixels + waitvid hvsync,#0 + djnz x,#blank 'another line? +hsync_ret +blank_ret +vsync_ret ret + + +' Data + +reg_dira long 0 'set at runtime +reg_dirb long 0 'set at runtime +reg_vcfg long 0 'set at runtime + +color_base long 0 'set at runtime (2 contiguous longs) +pixel_base long 0 'set at runtime + +vscl_pixel long 1 << 12 + 32 '1 pixel per clock and 32 pixels per set +colormask long $FCFC 'mask to isolate R,G,B bits from H,V +hvis long hp 'visible pixels per scan line +hv long hv_inactive '-H,-V states +hvsync long hv_inactive ^ $200 '+/-H,-V states + + +' Uninitialized data + +color_ptr res 1 +pixel_ptr res 1 +color res 1 +pixel res 1 +x res 1 +y res 1 +yl res 1 +yx res 1 + +{{ + +┌──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┐ +│ TERMS OF USE: MIT License │ +├──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┤ +│Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation │ +│files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, │ +│modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software│ +│is furnished to do so, subject to the following conditions: │ +│ │ +│The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.│ +│ │ +│THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE │ +│WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR │ +│COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, │ +│ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. │ +└──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┘ +}} \ No newline at end of file diff --git a/vga512/VGA_512x384_Bitmap_Demo.spin b/vga512/VGA_512x384_Bitmap_Demo.spin new file mode 100644 index 0000000..3431188 --- /dev/null +++ b/vga512/VGA_512x384_Bitmap_Demo.spin @@ -0,0 +1,142 @@ +''******************************************** +''* Added argc and argv parms to the start * +''* method, and extract the VGA pin from * +''* the second argv parameter. Check for * +''* key press in loops to terminate * +''* program. * +''* Copyright (c) 2012, Dave Hein * +''* See end of file for terms of use. * +''******************************************** +''* VGA 512x384 2-Color Bitmap Driver v1.0 * +''* Author: Chip Gracey * +''* Copyright (c) 2006 Parallax, Inc. * +''* See end of file for terms of use. * +''******************************************** + +CON + + _clkmode = xinput + _xinfreq = $4e495053 ' SPIN + + tiles = vga#xtiles * vga#ytiles + tiles32 = tiles * 32 + + +OBJ + + vga : "vga_512x384_bitmap" + ser : "FullDuplexSerial" + spinix : "exit" + + +VAR + + long sync, pixels[tiles32] + word colors[tiles] + + +PUB start(argc, argv) | h, i, j, k, x, y, ptr + + 'set the linefeed flag + set_linefeed(argc, argv) + + 'start serial port + ser.start(31, 30, 0, 115_200) + + 'check for two parms + if argc <> 2 + usage + + ' Check if arg is c3 or C3 + ptr := long[argv][1] + if strcomp(ptr, string("c3")) or strcomp(ptr, string("C3")) + j := 16 + DIRA |= $8000 + ' else, check if it's 0, 8, 16 or 24 + else + j := atol(ptr) + if j <> 0 and j <> 8 and j <> 16 and j <> 24 + usage + + 'start vga + vga.start(j, @colors, @pixels, @sync) + + 'print message to serial port + ser.str(string("Hit any key to terminate program")) + putnewline + + 'init colors to cyan on blue + repeat i from 0 to tiles - 1 + colors[i] := $2804 + + 'draw some lines + repeat y from 1 to 8 + repeat x from 0 to 511 + plot(x, x/y) + + 'wait ten seconds + repeat 100 + if ser.rxcheck <> -1 + spinix.exit(0) + waitcnt(clkfreq / 10 + cnt) + + 'randomize the colors and pixels + repeat + if ser.rxcheck <> -1 + spinix.exit(0) + colors[||?h // tiles] := ?i + repeat 100 + pixels[||?j // tiles32] := k? + + + +PRI plot(x,y) | i + + if x => 0 and x < 512 and y => 0 and y < 384 + pixels[y << 4 + x >> 5] |= |< x + +PRI usage + ser.str(string("usage: vga512 parm")) + putnewline + ser.str(string(" parm is either 'c3' for the C3 board or")) + putnewline + ser.str(string(" a starting pin number of 0, 8, 16 or 24")) + putnewline + waitcnt(cnt + clkfreq/10) + spinix.exit(1) + +PRI set_linefeed(argc, argv) | ptr + ptr := long[argv][argc-1] + ptr += strsize(ptr) + 1 + ptr := ((ptr + 3) >> 2) << 2 + linefeed := long[ptr] & 1 + +PRI putnewline + ser.tx(13) + if linefeed + ser.tx(10) + +PRI atol(ptr) + repeat while byte[ptr] + result := (result * 10) + byte[ptr++] - "0" + +DAT + linefeed long 0 + +{{ +┌──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┐ +│ TERMS OF USE: MIT License │ +├──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┤ +│Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation │ +│files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, │ +│modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software│ +│is furnished to do so, subject to the following conditions: │ +│ │ +│The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.│ +│ │ +│THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE │ +│WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR │ +│COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, │ +│ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. │ +└──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┘ +}} diff --git a/vga512/exit.spin b/vga512/exit.spin new file mode 100644 index 0000000..dc5053f --- /dev/null +++ b/vga512/exit.spin @@ -0,0 +1,34 @@ +obj + sys : "sysdefs" + +dat + run_prog + long $a0bc65f0, $08bc6e32, $80fc6404, $08bc7032, $80fc6404, $08bc6032 + long $80fc6404, $08bc6232, $80fc63ff, $28fc6209, $08fc6600, $00fc6804 + long $083c6029, $083c5c2a, $083c582b, $08bc642b, $863c642c, $5c68000f + long $80fc6001, $80fc5d00, $80fc5d00, $e4fc620c, $087c6600, $007c6804 + long $04fc6a08, $04fc6c0a, $a0bc6236, $84bc6235, $28fc6202, $083c5a35 + long $80fc6a04, $e4fc621d, $083c5a36, $80fc6c04, $083c6e36, $80fc6c04 + long $083c7036, $0cfc6401, $60fc6407, $68bc5e32, $0c7c5e02, $00007fd8 + long $00007fdc, $00007fd4, $00000072, $00000000, $00000000, $0007c010 + + arg_list long 0[4] + +pub exit(retval) | i, cogspi + cogspi := long[sys#SPI_engine_cog] - 1 + arg_list[2] := long[sys#shell_sector] + arg_list[3] := long[sys#shell_size] + long[sys#return_value] := retval + + ' Stop all the cogs except this one and the SD SPI cog + repeat i from 0 to 7 + if (i <> cogid and i <> cogspi) + cogstop(i) + + ' Clear and return all the locks + repeat i from 0 to 7 + lockclr(i) + lockret(i) + + ' Start run_prog in this cog + coginit(cogid, @run_prog, @arg_list) diff --git a/vga512/sysdefs.spin b/vga512/sysdefs.spin new file mode 100644 index 0000000..61fe3ca --- /dev/null +++ b/vga512/sysdefs.spin @@ -0,0 +1,112 @@ +'****************************************************************************** +' Copyright (c) 2011, 2012, Dave Hein +' See end of file for terms of use. +'****************************************************************************** +CON + 'start = $7e50 ' Extra space for the stand-alone loader + start = $7c00 ' Extra space for the stand-alone loader + + ' SD CLIB data + rendezvous = $7e50 + + ' Exported variables + environ_vars = $7e50 + environ_vars_end = $7ed3 + + ' Argv parameter area + argv_parms = $7ed4 + + ' Additional system parameters + return_value = $7f94 + vga_cog = $7f98 + vga_handle = $7f9c + sd_pins = $7fa0 + config = $7fa4 + shell_sector = $7fa8 + + ' System time + unixtime = $7fac + cycle0 = $7fb0 + timezone = $7fb4 +{ + ' I2C Driver + i2c_cog = $7fb8 + i2c_cmd = $7fbc + i2c_parm = $7fc0 + + ' Kernel data + filelock = $7fc4 + filecmd = $7fc8 + fileparm = $7fcc +} + 'Shell variables + scriptline = $7fb8 + ifflag = $7fbc + whileflag = $7fc0 + shell_size = $7fc4 + shell_level = $7fc8 + bootflag = $7fcc + + 'File I/O + spi_engine_cog = $7fd0 + spi_command = $7fd4 + spi_block_index = $7fd8 + spi_buffer_address = $7fdc + + ' Basic CLIB data + ' Serial data + serial = $7fe0 + stdio = $7fe4 + stdin = $7fe8 + stdout = $7fec + + ' Malloc data + memlocknum = $7ff0 + memfreelist = $7ff4 + malloclist = $7ff8 + laststackaddr = $7ffc + + ' Stack check word + checkword = $dead1eaf + + ' Process types + proc_type_spin = 1 + proc_type_pasm = 2 + proc_type_capp = 3 + proc_type_driver = $80 + + ' Run modes + run_shell_wait = $00 + run_shell_nowait = $08 + run_kill_caller = $10 + run_at_address0 = $20 + run_c_program = $40 + run_spin_program = $80 + run_stand_alone = $100 + +PUB main + return + +{{ ++-----------------------------------------------------------------------------+ +| TERMS OF USE: MIT License | ++-----------------------------------------------------------------------------+ +|Permission is hereby granted, free of charge, to any person obtaining a copy | +|of this software and associated documentation files (the "Software"), to deal| +|in the Software without restriction, including without limitation the rights | +|to use, copy, modify, merge, publish, distribute, sublicense, and/or sell | +|copies of the Software, and to permit persons to whom the Software is | +|furnished to do so, subject to the following conditions: | +| | +|The above copyright notice and this permission notice shall be included in | +|all copies or substantial portions of the Software. | +| | +|THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | +|IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | +|FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE | +|AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | +|LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,| +|OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE| +|SOFTWARE. | ++-----------------------------------------------------------------------------+ +}}