equi

A self-descriptive stack-based PC platform
git clone git://git.luxferre.top/equi.git
Log | Files | Refs | README | LICENSE

commit 991dff3eb1775e36ea3f1d0974f4a18a63f0fa08
parent efb6b7a3d6b7993cc3dc342df9a0b7bad40bce74
Author: Luxferre <lux@ferre>
Date:   Wed, 17 Aug 2022 21:30:09 +0300

some input upgrades

Diffstat:
MREADME.md | 5++---
Mequi.c | 57++++++++++++++++++++++++++++++++++++++++++++-------------
2 files changed, 46 insertions(+), 16 deletions(-)

diff --git a/README.md b/README.md @@ -134,10 +134,9 @@ See [FizzBuzz](examples/fizzbuzz.equi) for a more thorough example of how differ Being a purely PC-oriented low-level runtime/programming environment, Equi has the reference implementation emulator/VM written in C (ANSI C89 standard), `equi.c`, compilable and runnable on all the systems supporting standard I/O. Note that, for portability reasons, this emulator: -- accepts the programs from standard input only, +- accepts the program from a single file at a time only, - only implements four ports for `P` instruction: 0 as an echo port (returns passed parameters as corresponding result values), 1 as a random port (returns two random values in the results in the range between the two parameter values) 2 as a CRC16 calculation port for a given memory location and its length, and 3 for task control (see below), for any other port value it outputs its parameters to the standard error stream and puts three 0x0000 values back onto the stack, - implements `s` command line parameter that runs the emulator in the silent mode without printing any welcome banners or interactive prompts, -- doesn't implement non-blocking key input (the `,` instruction is identical to blocking key input instruction `?`), - sandboxes the `{` and `}` operations using the file with the name you supply on the compile time to the `PERSIST_FILE` constant. The file must already be created and accessible. If it doesn't exist, these operations will effectively do nothing except putting 0x0000 (success status) onto the stack. Additionally, this emulator implements `m` command line parameter that means that, instead of execution, the VM shall output the current command buffer contents upon reaching the `Q` instruction. This is particularly useful to save minified versions of `.equi` files to further reuse them in more space-restricted environments. Note that minified and non-minified files load and run fully identically, but the size difference can be significant. I.e. for the current FizzBuzz example version, the source is 1544 bytes long but its actual application snapshot in the command buffer (which can be dumped with the `m` parameter as a minified variant) is just [180 bytes long](examples/fizzbuz.min.equi). The rest is comments and whitespace characters that are skipped while loading the program into the command buffer. @@ -230,7 +229,7 @@ No, not at all. Although Equi was partially inspired by Uxn, it aims for a total ### I want to use Equi programs in a relatively modern POSIX environment as a part of a scripted process. Is this possible? -Totally! The Makefile for the reference implementation includes sensible default parameters for all targets. Just call `cat program.equi | /path/to/equi s | [other program]` in your scripts, where `s` parameter is used to suppress all banners and prompts and terminal initialization code from the standard output stream. Just make sure to place `PERS.DAT` file in the appropriate place if you need the persistence capabilities in your Equi-based scripts. +Totally! The Makefile for the reference implementation includes sensible default parameters for all targets. Just call `cat program.equi | /path/to/equi - s | [other program]` in your scripts, where `s` parameter is used to suppress all banners and prompts and terminal initialization code from the standard output stream. Just make sure to place `PERS.DAT` file in the appropriate place if you need the persistence capabilities in your Equi-based scripts, and not use input instructions in your programs if unsure what they will do with the streamed input. You can, of course, call Equi programs in a usual way just as well, with `/path/to/equi /path/to/program.equi s`, for instance. ### Too few core instructions! There still are lots of unused uppercase Latin letters, why not utilise them? diff --git a/equi.c b/equi.c @@ -18,12 +18,33 @@ #include <conio.h> #define CRLF "\n" #else + #include <termios.h> #include <unistd.h> +#include <sys/select.h> + #define cgetc() (getchar()) #define cputc(c) (putchar(c)) #pragma pack(2) #define CRLF "\r\n" + +/* also, attempt to emulate two other conio methods */ + +int kbhit() { + struct timeval tv = { 0L, 0L }; + fd_set fds; + FD_ZERO(&fds); + FD_SET(0, &fds); + return select(1, &fds, NULL, NULL, &tv) > 0; +} + +int getch() { + int r; + unsigned char c; + if((r = read(0, &c, 1)) < 0) return r; + else return c; +} + #endif /* Definitions section */ @@ -474,7 +495,7 @@ struct EquiCtx* equi_load_task(uchar priv, ushort len, ushort progStart) { /* Main interpreter loop */ void equi_main_loop() { - uchar instr; + uchar instr, bc; ushort lhash, pbuf, pbuf2; /* try to open the persistent sandbox file */ FILE *pfd = fopen(PERSIST_FILE, "r+b"); @@ -675,9 +696,14 @@ void equi_main_loop() { case INS_NHOUT: /* output the hex number from the stack top */ printf("%04X", popMain()); break; - case INS_NBKIN: /* non-blocking key input - in this implementation it's equal to: */ + case INS_NBKIN: /* non-blocking key input - best attempt to implement it with kbhit or our haphazard emulation of it */ + pushMain(kbhit() ? (ushort)cgetc() : 0); + break; case INS_BKIN: /* blocking key input (again, no Unicode support yet, assuming a single byte incoming to the 16-bit value on the stack) */ - pushMain((ushort)cgetc()); + bc = 0; + bc = cgetc(); + fprintf(stderr, "[in] %c" CRLF, bc); + pushMain((ushort) bc); break; case INS_PORTIO: /* ( p1 p2 port -- r1 r2 status ) - simulate/execute port I/O according to the spec */ if(curtask->msp < 3) @@ -709,10 +735,8 @@ void equi_main_loop() { /* Equi VM entry point */ int main(int argc, char* argv[]) { - uchar instr, bc, smode = 0; - /* detect host endianness */ - int host_islittle = 1; - host_islittle = (*((char *)&host_islittle) == 1) ? 1 : 0; + uchar instr, bc, modec, smode = 0; + FILE *prog = stdin; /* _attempt_ to disable buffering for char input/output */ #if !defined __CC65__ struct termios tty_opts_raw, tty_opts_backup; @@ -729,12 +753,18 @@ int main(int argc, char* argv[]) { ram.cmd_size = CMD_BUF_SIZE; ram.II = ram.MM = 0; /* reset all flags */ /* process command line params */ - if(argc > 1 && argv[1][0] == 'm') { /* enter minification mode, don't run the programs */ - ram.MM = 1; - smode = 1; /* also behave as if in the silent mode */ + if(argc > 1 && argv[1][0] != '-') { /* we passed the input file, - means "just use stdin" */ + prog = fopen(argv[1], "r"); } - else if(argc > 1 && argv[1][0] == 's') /* enter silent mode, don't print the banners and prompts */ - smode = 1; + if(argc > 2) { /* the first is the file, the second is the mode */ + if(argv[2][0] == 'm') { /* enter minification mode, don't run the programs */ + ram.MM = 1; + smode = 1; /* also behave as if in the silent mode */ + } + else if(argv[2][0] == 's') /* enter silent mode, don't print the banners and prompts */ + smode = 1; + } + /* Start input buffering from the start of command buffer (-1 because we use prefix increment) */ ram.ibp = 65535U; if(!ram.MM && !smode) { /* skip the terminal init and the greeting if in minification or silent mode */ @@ -749,7 +779,7 @@ int main(int argc, char* argv[]) { } while(1) { /* Now, we're in the command mode loop */ - instr = cgetc(); /* Fetch the next instruction from the keyboard/stdin */ + instr = fgetc(prog); /* Fetch the next instruction from the keyboard/file */ if(instr == 0xFFU || instr == 0U || instr == 3U || instr == 4U) /* exit on zero byte or ctrl+C or ctrl+D */ break; else if(instr == BS || instr == DEL || instr == CR || instr == LF || instr == ' ' || instr == '\t') { /* ignore the backspace or whitespaces */ @@ -813,5 +843,6 @@ int main(int argc, char* argv[]) { /* restore the terminal settings */ tcsetattr(STDIN_FILENO, TCSANOW, &tty_opts_backup); #endif + fclose(prog); /* close the input file */ return 0; }