equi

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

commit d48e6df014c778250cf2dbee84775783f0291586
parent d457e9c56ea0a7b683f0f8bdb3b1cf4ee454d023
Author: Luxferre <lux@ferre>
Date:   Thu, 11 Aug 2022 23:23:02 +0300

terminal and readme improvements

Diffstat:
MREADME.md | 52+++++++++++++++++++++++++++++++++++++++++-----------
Mequi.c | 17++++++++++-------
2 files changed, 51 insertions(+), 18 deletions(-)

diff --git a/README.md b/README.md @@ -19,10 +19,11 @@ Main features of an Equi machine: - Interpretation mode (IM) flag; - Compilation mode (CM) flag; - Instruction ignore (II) flag; +- 16-bit input buffer pointer; - 42752 bytes of main RAM (0x0000-0xa6ff), 41984 bytes of which are available for the program and data and 768 bytes hold the three stacks and necessary service registers listed above; - Up to 4GB flat persistent storage (tape, disk, flash etc); - Serial terminal input and output; -- Up to 65535 peripheral extension ports. +- Up to 65535 peripheral extension ports, including several virtual ports. The default Equi RAM layout is: @@ -38,17 +39,17 @@ Address range|Size (bytes)|Purpose 0x0227-0x0228|2 |CBP (compilation buffer pointer) 0x0229-0x022a|2 |CLTP (compilation lookup table pointer) 0x022b |1 |Flags (II/CM/IM) -0x022c-0x022d|2 |GPD area start (set to 0x2300 by default) -0x022e-0x022f|2 |Command buffer start (set to 0x5000 by default) -0x0230-0x0231|2 |Command buffer size (set to 0x5700 by default) -0x0232-0x02ff|206 |Reserved for internal usage by the interpreter and future extensions -0x0300-0x22ff|8192 |Compilation lookup table (CLT) - up to 1024 compiled words (6 bytes for literal + 2 bytes for CBP address each) by default -0x2300-0x3b7f|6272 |General purpose data (GPD) area -0x3b80-0x7dff|17024 |Command buffer area +0x022c-0x022d|2 |GPD area start address +0x022e-0x022f|2 |Command buffer start address +0x0230-0x0231|2 |Command buffer size in bytes +0x0232-0x0233|2 |IBP (input buffer pointer) +0x0234-... |varies |Compilation lookup table (CLT) - up to 1024 compiled words by default +... |varies |General purpose data (GPD) area +...-0x7dff |varies |Command buffer area This layout has been carefully chosen to achieve maximum portability: so that full a Equi system could fit inside the user area of Apple II's space between (physical) 0x0800 and 0x9600 addresses, while still leaving 4096 bytes for the interpreter itself with all necessary peripheral drivers if required so. As such, Apple II equipped with full 48K RAM is supposedly the lowest spec possible target system Equi could run on with the default virtual RAM configuration. -To preserve runtime integrity, Equi implementations are allowed to (but not required to) restrict all writes to the addresses below the GPD area start (0x2300 by default). On systems with less/more RAM where CLT and/or GPD and/or command buffer areas are smaller/larger, the values at 0x0227, 0x0229 and 0x022b must be populated accordingly. You don't need to fill in the sizes of CLT and GPD areas since they are calculated automatically from the specified offsets, you only need to specify how big your command buffer is to avoid any writes outside the user area. +To preserve runtime integrity, Equi implementations are allowed to (but not required to) restrict all writes to the addresses below the GPD area start. On systems with less/more RAM where CLT and/or GPD and/or command buffer areas are smaller/larger, the values at 0x022c, 0x022e and 0x0230 must be populated accordingly. You don't need to fill in the sizes of CLT and GPD areas since they are calculated automatically from the specified offsets, you only need to specify how big your command buffer is to avoid any writes outside the user area. Equi is strictly case-sensitive: all uppercase basic Latin letters, as well as a number of special characters, are reserved for machine instructions, and all custom words must be defined in lowercase only (additionally, `_` character is allowed in the identifiers). Within comments (see below), any characters can be used. @@ -117,7 +118,7 @@ Op |Stack state |Meaning This would be an example of loading a 15200-byte long Equi program from the persistent storage at address 0x12345678 into the RAM at the next free address and running it (discarding the load status for brevity): ``` -1234#5678#3b60X0007+{!(CR) +1234#5678#3b60X0007+{!Q ``` The logic of this loader snippet is as follows. After the `X` instruction, the top of the stack contains the address occupied by the following byte (`0`) in this case. Then we add the amount of bytes needed to shift loading to the end of the snippet, in our case 7 (additionally skipping `0`, `0`, `7`, `+`, `{` and `!`) and use the result as the target loading address in memory. Since this loader snippet itself ends at 0x5021 (with the default configuration, the command buffer starts at 0x5000) and the status is already popped from the stack, the execution will start from 0x5022 immediately. Please also notice how we don't need `#` after `3b60` (15200 in hex) and `0007` since all valid instructions except `:`, `'` and `"` trigger `#` automatically before execution when the literal stack is not empty. @@ -191,7 +192,36 @@ This will build a bootable disk image with Equi for Apple II that can be tested The Makefile also provides a usual `apple2` target and the disk image will run on a "normal" Apple IIe, however the environment is not guaranteed to work correctly due to the way CC65 compiler converts string literals for this target. -## Credits +## FAQ (under construction) + +### Why does the world need another Forth-like system? + +Because it aims for a different set of goals than typical Forth systems, mainly to explore the realms of blurring the borders between source and machine code, and to create a VM that can be easily programmed with printable text on the lowest level with no assembly required. Equi is to a typical Forth what VTL-2 was to BASIC, except in this case it is much more capable and extensible at its core. + +### Is Equi self-hosted, i.e. can it compile and run a new version of itself? + +Depends on what exactly you mean by this. If you mean something like [Uxntal assembler written in Uxntal](https://wiki.xxiivv.com/site/drifblim.html), the beauty of Equi is that it doesn't need such a tool, because what you type is what gets directly executed. A single-pass Equi code minifier, similar to what `equi m` does in the reference implementation, surely can be implemented in Equi itself, and a proof of this concept is under development now. With Equi being Turing-complete, a full Equi VM running inside an Equi VM is theoretically also possible, although it would be rather slow, complex to implement and bearing little to no practical use. If, however, you mean a compiler of Equi to the target's machine language, implemented in Equi itself, the amount of work required to do that would be comparable to implementing compilers on a Forth system, and would most likely hit the 64K RAM limit. But for the simplest targets this also it possible if you throw enough time and effort into this. + +### Where are labels? Macros? Includes? Why doesn't Equi have them? Even Uxntal has them! + +Being flexible and human-readable, but a machine language nevertheless, Equi deliberately doesn't include any features that would qualify as preprocessing and require more than a single pass when loading a program into the command buffer. The principle "one source instruction is one machine instruction" is paramount for the entire platform. One can, however, create a translator that compiles a higher level programming language into Equi, with that compiler/language having any required preprocessing features. + +Some features can be simulated with tools external to Equi, for instance, includes can be achieved by concatenating several files, as long as only the last of them contains the `Q` instruction, and loop labels can be emulated with saving jump addresses to the return stack and calling them back when necessary. Only whitespace and comments, which are absolutely needed in order to write readable programs directly in Equi, are being stripped during the single-pass program bootup. + +### What are the minimum system requirements to implement/port and/or run Equi? + +For the reference implementation in ANSI C, at least 32K of RAM and 6502 or better CPU are recommended, and 64K RAM and above are ideal. For your own implementations, make sure that the CPU speed is enough to perform 16-bit integer multiplication and division, as well as CRC-16 calculation, without noticeable lags, and that the command buffer, CLT and GPD areas are large enough to fit programs for your tasks. Also, persistent storage and realtime clock facilities are nice to have as the bare minimum. + +### Which CRC-16 variant is required for Equi? + +There is no **required** variant of CRC-16. Different implementations using different CRC-16 algos doesn't mean the programs for them would be incompatible, it's only related to the internal storage of the compiled words in CLT. The **recommended** CRC-16 variant though is the one used in the reference implementation, CRC-16-CCITT (0xFFFF). This one is simple to implement and provides a good pseudo-random distribution even for long sequences of zero bytes. + +### Is non-blocking key input implemented for the targets that support it? + +For now, no, but it may come true in the future versions. Now, more essential features are being focused upon. + + +# Credits Created by Luxferre in 2022, released into public domain. diff --git a/equi.c b/equi.c @@ -556,11 +556,6 @@ int main(int argc, char* argv[]) { uchar instr, bc, mmode = 0; if(argc > 1 && argv[1][0] == 'm') /* enter minification mode, don't run the programs */ mmode = 1; - /* CC65-specific terminal init */ -#ifdef __CC65__ - clrscr(); - bc = cursor(1); -#endif /* _attempt_ to disable buffering for char input/output */ #if !defined __CC65__ && !defined __TINYC__ setvbuf(stdin, NULL, _IONBF, 0); @@ -576,13 +571,21 @@ int main(int argc, char* argv[]) { /* Start both execution and input buffering from the start of command buffer (-1 because we use prefix increment) */ ram.pc = ram.ibp = 65535U; - if(!mmode) /* skip the greeting if in minification mode */ - printf("\nWelcome to Equi v" EQUI_VER " by Luxferre, 2022\nCLT: 0x%04X (%d bytes)\nGPD: 0x%04X (%d bytes)\nCommand buffer: 0x%04X (%d bytes)\nEqui ready\n\n> ", + if(!mmode) { /* skip the terminal init and the greeting if in minification mode */ + /* CC65-specific terminal init */ +#ifdef __CC65__ + clrscr(); + bc = cursor(1); +#else /* VT100-compatible terminal init */ + puts("\033c"); +#endif + printf("Welcome to Equi v" EQUI_VER " by Luxferre, 2022\nCLT: 0x%04X (%d bytes)\nGPD: 0x%04X (%d bytes)\nCommand buffer: 0x%04X (%d bytes)\nEqui ready\n\n> ", (unsigned int) ((uchar *)&ram.clt - (uchar *)&ram.main_stack), (unsigned int) ((uchar *)&ram.gpd - (uchar *)&ram.clt), ram.gpd_start, ram.cmd_start - ram.gpd_start, ram.cmd_start, ram.cmd_size); + } while(1) { /* Now, we're in the command mode loop */ instr = cgetc(); /* Fetch the next instruction from the keyboard/stdin */