diff --git a/Makefile b/Makefile index 3f57eba..422cc59 100644 --- a/Makefile +++ b/Makefile @@ -9,6 +9,7 @@ INC_DIRS := $(shell find $(SRC_DIRS) -type d) INC_FLAGS := $(addprefix -I,$(INC_DIRS)) CPPFLAGS ?= $(INC_FLAGS) -MMD -MP +CFLAGS := -std=c99 $(TARGET): $(OBJS) $(CC) $(LDFLAGS) $(OBJS) -o $@ $(LOADLIBES) $(LDLIBS) @@ -19,7 +20,7 @@ clean: -include $(DEPS) -# On the Linux command line: +# On the Linux or Mac OS command line: # make creates tiff and leaves a bunch of object files in /src # make clean deletes the object files as well as tiff diff --git a/README.md b/README.md index 3a9f6d9..7d39c76 100644 --- a/README.md +++ b/README.md @@ -6,7 +6,7 @@ relying on C libraries for middleware and other wheels that you don't want to re It's set up to compile to flash memory. Code space is flash (big) so that data space can be small. Try `bin/tiff.exe` to play, if you like to live dangerously. -If you're a Linux user, compile from source using the Makefile. +If you're a Linux or Mac OS user, compile from source using the Makefile. On Windows, I just pull all files in `src` into a Code::Blocks console project. Windows users would do well to run this console app under Conemu. diff --git a/examples/ANS/vmConsole.c b/examples/ANS/vmConsole.c index e86503c..f0a1276 100644 --- a/examples/ANS/vmConsole.c +++ b/examples/ANS/vmConsole.c @@ -12,7 +12,7 @@ vmKeyFormat = 0 for Windows, 1 for Linux. The escape sequences are different. */ -#ifdef __linux__ +#if defined(__linux__) || defined(__APPLE__) #include #include #include @@ -22,7 +22,7 @@ #include #endif // __linux__ -#ifdef __linux__ +#if defined(__linux__) || defined(__APPLE__) // Linux uses cooked mode to input a command line (see Tiff.c's QUIT loop). // Any keyboard input uses raw mode. // Apparently, Windows getch does this switchover for us. @@ -100,7 +100,7 @@ uint32_t vmKeyFormat(uint32_t dummy) { uint32_t vmEmit(uint32_t c) { putchar(c); -#ifdef __linux__ +#if defined(__linux__) || defined(__APPLE__) fflush(stdout); usleep(1000); #endif diff --git a/examples/ANS/vmConsole.h b/examples/ANS/vmConsole.h index a7dd63d..beb6fb9 100644 --- a/examples/ANS/vmConsole.h +++ b/examples/ANS/vmConsole.h @@ -4,7 +4,7 @@ #ifndef __VMCONSOLE_H__ #define __VMCONSOLE_H__ -#ifdef __linux__ +#if defined(__linux__) || defined(__APPLE__) void CookedMode(); void RawMode(); #endif // __linux__ diff --git a/examples/helloworld/vmConsole.c b/examples/helloworld/vmConsole.c index e86503c..f0a1276 100644 --- a/examples/helloworld/vmConsole.c +++ b/examples/helloworld/vmConsole.c @@ -12,7 +12,7 @@ vmKeyFormat = 0 for Windows, 1 for Linux. The escape sequences are different. */ -#ifdef __linux__ +#if defined(__linux__) || defined(__APPLE__) #include #include #include @@ -22,7 +22,7 @@ #include #endif // __linux__ -#ifdef __linux__ +#if defined(__linux__) || defined(__APPLE__) // Linux uses cooked mode to input a command line (see Tiff.c's QUIT loop). // Any keyboard input uses raw mode. // Apparently, Windows getch does this switchover for us. @@ -100,7 +100,7 @@ uint32_t vmKeyFormat(uint32_t dummy) { uint32_t vmEmit(uint32_t c) { putchar(c); -#ifdef __linux__ +#if defined(__linux__) || defined(__APPLE__) fflush(stdout); usleep(1000); #endif diff --git a/examples/helloworld/vmConsole.h b/examples/helloworld/vmConsole.h index a7dd63d..beb6fb9 100644 --- a/examples/helloworld/vmConsole.h +++ b/examples/helloworld/vmConsole.h @@ -4,7 +4,7 @@ #ifndef __VMCONSOLE_H__ #define __VMCONSOLE_H__ -#ifdef __linux__ +#if defined(__linux__) || defined(__APPLE__) void CookedMode(); void RawMode(); #endif // __linux__ diff --git a/examples/testbench/vmConsole.c b/examples/testbench/vmConsole.c index b320698..2d7312a 100644 --- a/examples/testbench/vmConsole.c +++ b/examples/testbench/vmConsole.c @@ -12,7 +12,7 @@ vmKeyFormat = 0 for Windows, 1 for Linux. The escape sequences are different. */ -#ifdef __linux__ +#if defined(__linux__) || defined(__APPLE__) #include #include #include @@ -22,7 +22,7 @@ #include #endif // __linux__ -#ifdef __linux__ +#if defined(__linux__) || defined(__APPLE__) // Linux uses cooked mode to input a command line (see Tiff.c's QUIT loop). // Any keyboard input uses raw mode. // Apparently, Windows getch does this switchover for us. @@ -125,7 +125,7 @@ uint32_t vmEmit(uint32_t xchar) { } char *s = c; char b; while ((b = *s++)) putchar(b); -#ifdef __linux__ +#if defined(__linux__) || defined(__APPLE__) fflush(stdout); usleep(1000); #endif diff --git a/examples/testbench/vmConsole.h b/examples/testbench/vmConsole.h index d97d403..b7da2f3 100644 --- a/examples/testbench/vmConsole.h +++ b/examples/testbench/vmConsole.h @@ -4,7 +4,7 @@ #ifndef __VMCONSOLE_H__ #define __VMCONSOLE_H__ -#ifdef __linux__ +#if defined(__linux__) || defined(__APPLE__) void CookedMode(); void RawMode(); #endif // __linux__ diff --git a/src/accessvm.c b/src/accessvm.c index 7b65ecd..1afd61b 100644 --- a/src/accessvm.c +++ b/src/accessvm.c @@ -15,7 +15,7 @@ #include #include -#ifdef __linux__ +#if defined(__linux__) || defined(__APPLE__) #include #include diff --git a/src/main.c b/src/main.c index 62c4815..0a5e739 100644 --- a/src/main.c +++ b/src/main.c @@ -7,7 +7,7 @@ #include "accessvm.h" #include "fileio.h" #include -#include "vmhost.h" +#include "vmHost.h" #define HP0max (MaxROMsize - 0x1000) /*global*/ int HeadPointerOrigin = (ROMsizeDefault + RAMsizeDefault)*4; diff --git a/src/readme.md b/src/readme.md index 6b90ac1..52c0fe7 100644 --- a/src/readme.md +++ b/src/readme.md @@ -16,7 +16,7 @@ This brings also Forth-like scripting and interactivity to C applications. ## Host Forth -The Host Forth is implemented in generic C. I use Code::Blocks to compile for Windows and execute it in ConEmu for decent VT220 emulation. I also compile and test it under Ubuntu Linux. Tiff is the name of the console application. +The Host Forth is implemented in generic C. I use Code::Blocks to compile for Windows and execute it in ConEmu for decent VT220 emulation. I also compile and test it under Ubuntu Linux. It is also supposed to work with Mac OS. Tiff is the name of the console application. It implements an interpreter and a number of words using C. They use data structures in the Forth VM whenever possible. This tends to be more wordy than simply using C data structures, but it allows C functions to be replaced by Forth versions as they become available. For example, when the C version of the interpreter fills the TIB with text using C's `getline`, the Forth system running in the VM sees that same text in its TIB. diff --git a/src/rs232.c b/src/rs232.c index 5e14882..3b4b7ba 100644 --- a/src/rs232.c +++ b/src/rs232.c @@ -34,7 +34,7 @@ #include "rs232.h" -#if defined(__linux__) || defined(__FreeBSD__) /* Linux & FreeBSD */ +#if defined(__linux__) || defined(__FreeBSD__) || defined(__APPLE__) /* Linux & FreeBSD & MacOS */ #define RS232_PORTNR 38 diff --git a/src/rs232.h b/src/rs232.h index 5599682..07a3510 100644 --- a/src/rs232.h +++ b/src/rs232.h @@ -42,7 +42,7 @@ extern "C" { -#if defined(__linux__) || defined(__FreeBSD__) +#if defined(__linux__) || defined(__FreeBSD__) || defined(__APPLE__) #include #include @@ -60,6 +60,22 @@ extern "C" { #endif +#if defined(__APPLE__) + +#define B460800 460800 +#define B500000 500000; +#define B576000 576000; +#define B921600 921600; +#define B1000000 1000000; +#define B1152000 1152000; +#define B1500000 1500000; +#define B2000000 2000000; +#define B2500000 2500000; +#define B3000000 3000000; +#define B3500000 3500000; +#define B4000000 4000000; +#endif + int RS232_OpenComport(int, int, const char *, int); int RS232_PollComport(int, unsigned char *, int); int RS232_SendByte(int, unsigned char); diff --git a/src/vmConsole.c b/src/vmConsole.c index e86503c..f0a1276 100644 --- a/src/vmConsole.c +++ b/src/vmConsole.c @@ -12,7 +12,7 @@ vmKeyFormat = 0 for Windows, 1 for Linux. The escape sequences are different. */ -#ifdef __linux__ +#if defined(__linux__) || defined(__APPLE__) #include #include #include @@ -22,7 +22,7 @@ #include #endif // __linux__ -#ifdef __linux__ +#if defined(__linux__) || defined(__APPLE__) // Linux uses cooked mode to input a command line (see Tiff.c's QUIT loop). // Any keyboard input uses raw mode. // Apparently, Windows getch does this switchover for us. @@ -100,7 +100,7 @@ uint32_t vmKeyFormat(uint32_t dummy) { uint32_t vmEmit(uint32_t c) { putchar(c); -#ifdef __linux__ +#if defined(__linux__) || defined(__APPLE__) fflush(stdout); usleep(1000); #endif diff --git a/src/vmConsole.h b/src/vmConsole.h index a7dd63d..beb6fb9 100644 --- a/src/vmConsole.h +++ b/src/vmConsole.h @@ -4,7 +4,7 @@ #ifndef __VMCONSOLE_H__ #define __VMCONSOLE_H__ -#ifdef __linux__ +#if defined(__linux__) || defined(__APPLE__) void CookedMode(); void RawMode(); #endif // __linux__ diff --git a/src/vmHost.c b/src/vmHost.c index a096fb8..7203f7c 100644 --- a/src/vmHost.c +++ b/src/vmHost.c @@ -1,286 +1,301 @@ -#include -#include -#include -#include "vm.h" -#include "accessvm.h" -#include "rs232.h" -#define MAXFILES 64 - -/* -Host functions operate on the stack and have access to host resources. -Parameter passing is through the stack, whose pointer is passed to them. -The return value is the number of cells to offset the stack immediately after return. -*/ - -// ============================================================================= -// COM port stuff adds about 5K to the executable. Not bad for Windows. - -static int activeport; - -// COM port parameters: N = BaudRate, T = Port, returns ior=0 if opened -static int opencomm (uint32_t *s) { // ( rate port -- ior ) - int r = RS232_OpenComport(s[0], s[1], "8N2", 0); - if (!r) activeport = s[0]; - s[1] = r; - return 1; -} -static int closecomm (uint32_t *s) { // ( -- ) - RS232_CloseComport(activeport); - return 0; -} -// Send one byte -static int commemit (uint32_t *s) { // ( c -- ) - RS232_SendByte(activeport, s[0]); - return 1; -} - -static unsigned char buf[4]; -static int full = 0; - -static int commQkeyC (void) { - if (full) { - return 1; - } - int r = RS232_PollComport(activeport, buf, 1); - full = r; - return r; -} - -static int commQkey (uint32_t *s) { // ( -- n ) - s[-1] = commQkeyC(); - return -1; -} - -static int commkey (uint32_t *s) { // ( -- c ) - while (commQkeyC() == 0) { Sleep(1); } - full = 0; - s[-1] = buf[0]; - return -1; -} - -// ============================================================================= -// File access words - -static char name[1024]; - -// unimplemented or already in Tiff: -// 11.6.1.0765 BIN -// 11.6.1.2165 S" -// 11.6.1.1717 INCLUDE-FILE -// 11.6.1.1718 INCLUDED -// 11.6.1.2218 SOURCE-ID - -// Rather than cast uint32_t to and from file pointers and wait for C to break -// the code, use an array of actual file pointers. - -FILE * filehandle[MAXFILES]; - -void vmHostInit(void) { - for (int i = 0; i= ' ') && (pos < length)) { - StoreByte(c, address++); - s[2]++; - } - break; - } - if (ferror(f)) { - s[0] = -71; - return 0; - } - } - return 0; -} - -static int FILE_POSITION ( uint32_t *s ) { // 11.6.1.1520 ( fileid -- ud ior ) - FILE * f = FilePointer(s[0]); - s[0] = 0; s[-1] = 0; s[-2] = -65; - fpos_t pos; - int ior = fgetpos(f, &pos); - if (ior == 0) { - uint64_t x = (uint64_t) pos; - s[0] = x & 0xFFFFFFFF; - s[-1] = x >> 32; - s[-2] = 0; - } - return -2; -} -static int REPOSITION_FILE ( uint32_t *s ) { // 11.6.1.2142 ( ud fileid -- ior ) - FILE * f = FilePointer(s[0]); - uint64_t x = (((uint64_t) s[1])<<32) | (uint64_t) s[2]; - fpos_t pos = (fpos_t) x; - int ior = fsetpos(f, &pos); - s[2] = (ior) ? -73 : 0; - return 2; -} - -static int WRITE_FILE ( uint32_t *s ) { // 11.6.1.2480 ( c-addr u fileid -- ior ) - uint32_t address = s[2]; - uint32_t length = s[1]; - FILE * f = FilePointer(s[0]); - s[2] = 0; - for (int i=0; i>32) & 0xFFFFFFFF; // high ud - s[-2] = 0; // ior - return -2; -} - -/* -static int OPEN-FILE ( uint32_t *s ) { // 11.6.1.1970 ( c-addr u fam -- fileid ior ) -static int DELETE-FILE ( uint32_t *s ) { // 11.6.1.1190 ( c-addr u -- ior ) - -static int RESIZE-FILE ( uint32_t *s ) { // 11.6.1.2147 ( ud fileid -- ior ) -* --61 RESIZE --62 CLOSE-FILE --63 CREATE-FILE --64 DELETE-FILE --65 FILE-POSITION --66 FILE-SIZE --67 FILE-STATUS --68 FLUSH-FILE --69 OPEN-FILE --70 READ-FILE --71 READ-LINE --72 RENAME-FILE --73 REPOSITION-FILE --74 RESIZE-FILE --75 WRITE-FILE --76 WRITE-LINE -*/ +#include +#include +#include +#include "vm.h" +#include "accessvm.h" +#include "rs232.h" + +#if defined(__linux__) || defined(__FreeBSD__) || defined(__APPLE__) + +#include // for usleep +#define MS(ms) usleep(1000*(ms)) + +#else + +#define MS(ms) Sleep(ms) + +#endif + +#define MAXFILES 64 + +/* +Host functions operate on the stack and have access to host resources. +Parameter passing is through the stack, whose pointer is passed to them. +The return value is the number of cells to offset the stack immediately after return. +*/ + +// ============================================================================= +// COM port stuff adds about 5K to the executable. Not bad for Windows. + +static int activeport; + +// COM port parameters: N = BaudRate, T = Port, returns ior=0 if opened +static int opencomm (uint32_t *s) { // ( rate port -- ior ) + int r = RS232_OpenComport(s[0], s[1], "8N2", 0); + if (!r) activeport = s[0]; + s[1] = r; + return 1; +} +static int closecomm (uint32_t *s) { // ( -- ) + RS232_CloseComport(activeport); + return 0; +} +// Send one byte +static int commemit (uint32_t *s) { // ( c -- ) + RS232_SendByte(activeport, s[0]); + return 1; +} + +static unsigned char buf[4]; +static int full = 0; + +static int commQkeyC (void) { + if (full) { + return 1; + } + int r = RS232_PollComport(activeport, buf, 1); + full = r; + return r; +} + +static int commQkey (uint32_t *s) { // ( -- n ) + s[-1] = commQkeyC(); + return -1; +} + + + + +static int commkey (uint32_t *s) { // ( -- c ) + while (commQkeyC() == 0) { MS(1); } + full = 0; + s[-1] = buf[0]; + return -1; +} + +// ============================================================================= +// File access words + +static char name[1024]; + +// unimplemented or already in Tiff: +// 11.6.1.0765 BIN +// 11.6.1.2165 S" +// 11.6.1.1717 INCLUDE-FILE +// 11.6.1.1718 INCLUDED +// 11.6.1.2218 SOURCE-ID + +// Rather than cast uint32_t to and from file pointers and wait for C to break +// the code, use an array of actual file pointers. + +FILE * filehandle[MAXFILES]; + +void vmHostInit(void) { + for (int i = 0; i= ' ') && (pos < length)) { + StoreByte(c, address++); + s[2]++; + } + break; + } + if (ferror(f)) { + s[0] = -71; + return 0; + } + } + return 0; +} + +static int FILE_POSITION ( uint32_t *s ) { // 11.6.1.1520 ( fileid -- ud ior ) + FILE * f = FilePointer(s[0]); + s[0] = 0; s[-1] = 0; s[-2] = -65; + fpos_t pos; + int ior = fgetpos(f, &pos); + if (ior == 0) { + uint64_t x = (uint64_t) pos; + s[0] = x & 0xFFFFFFFF; + s[-1] = x >> 32; + s[-2] = 0; + } + return -2; +} +static int REPOSITION_FILE ( uint32_t *s ) { // 11.6.1.2142 ( ud fileid -- ior ) + FILE * f = FilePointer(s[0]); + uint64_t x = (((uint64_t) s[1])<<32) | (uint64_t) s[2]; + fpos_t pos = (fpos_t) x; + int ior = fsetpos(f, &pos); + s[2] = (ior) ? -73 : 0; + return 2; +} + +static int WRITE_FILE ( uint32_t *s ) { // 11.6.1.2480 ( c-addr u fileid -- ior ) + uint32_t address = s[2]; + uint32_t length = s[1]; + FILE * f = FilePointer(s[0]); + s[2] = 0; + for (int i=0; i>32) & 0xFFFFFFFF; // high ud + s[-2] = 0; // ior + return -2; +} + +/* +static int OPEN-FILE ( uint32_t *s ) { // 11.6.1.1970 ( c-addr u fam -- fileid ior ) +static int DELETE-FILE ( uint32_t *s ) { // 11.6.1.1190 ( c-addr u -- ior ) + +static int RESIZE-FILE ( uint32_t *s ) { // 11.6.1.2147 ( ud fileid -- ior ) +* +-61 RESIZE +-62 CLOSE-FILE +-63 CREATE-FILE +-64 DELETE-FILE +-65 FILE-POSITION +-66 FILE-SIZE +-67 FILE-STATUS +-68 FLUSH-FILE +-69 OPEN-FILE +-70 READ-FILE +-71 READ-LINE +-72 RENAME-FILE +-73 REPOSITION-FILE +-74 RESIZE-FILE +-75 WRITE-FILE +-76 WRITE-LINE +*/ // void FetchString(char *s, int32_t address, uint8_t length); // get string -// void StoreString(char *s, int32_t address); // store string - - -static int testout (uint32_t *s) { // ( address length -- ) - FetchString(name, s[1], s[0]); - printf("testout[%s]\n", name); - return 2; -} - - -// Host Functions are accessed through: - -int HostFunction (uint32_t fn, uint32_t * s) { - static int (* const pf[])(uint32_t*) = { - opencomm, closecomm, commemit, commQkey, commkey, - testout, - CLOSE_FILE, CREATE_FILE, CREATE_FILE, // open-file uses create-file - READ_FILE, READ_LINE, FILE_POSITION, REPOSITION_FILE, WRITE_FILE, WRITE_LINE, - FILE_SIZE -// add your own here... - }; - if (fn < sizeof(pf) / sizeof(*pf)) { - return pf[fn](s); - } else { - return 0; - } -} - +// void StoreString(char *s, int32_t address); // store string + + +static int testout (uint32_t *s) { // ( address length -- ) + FetchString(name, s[1], s[0]); + printf("testout[%s]\n", name); + return 2; +} + + +// Host Functions are accessed through: + +int HostFunction (uint32_t fn, uint32_t * s) { + static int (* const pf[])(uint32_t*) = { + opencomm, closecomm, commemit, commQkey, commkey, + testout, + CLOSE_FILE, CREATE_FILE, CREATE_FILE, // open-file uses create-file + READ_FILE, READ_LINE, FILE_POSITION, REPOSITION_FILE, WRITE_FILE, WRITE_LINE, + FILE_SIZE +// add your own here... + }; + if (fn < sizeof(pf) / sizeof(*pf)) { + return pf[fn](s); + } else { + return 0; + } +} +