equi

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

commit 03cf5e95fe511bf42a8cceb4a64e633fc09def6d
parent df9d9b7cbcbea8a1b481a08db90c2f7a7cb7e2a8
Author: Luxferre <lux@ferre>
Date:   Sat, 13 Aug 2022 08:46:25 +0300

memory revamp described, endianness checked

Diffstat:
MREADME.md | 41+++++++++++++++++++++++------------------
Mequi.c | 21+++++++++++++++++++++
2 files changed, 44 insertions(+), 18 deletions(-)

diff --git a/README.md b/README.md @@ -19,6 +19,7 @@ Main features of an Equi machine: - Interpretation mode (IM) flag; - Compilation mode (CM) flag; - Instruction ignore (II) flag; +- Minification mode (MM) 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); @@ -29,27 +30,31 @@ The default Equi RAM layout is: Address range|Size (bytes)|Purpose -------------|------------|--------------------- -0x0000-0x00ff|256 |Main stack -0x0100-0x01ff|256 |Return stack -0x0200-0x021f|32 |Literal stack -0x0220-0x0221|2 |PC (program counter) -0x0222-0x0223|2 |MSP (main stack pointer) -0x0224-0x0225|2 |RSP (return stack pointer) -0x0226 |1 |LSP (literal stack pointer) -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 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 +0x0000-0x0001|2 |Main/return stack size in words +0x0002 |1 |Literal stack size in bytes (up to 255) +0x0003 |1 |LSP (literal stack pointer) +0x0004-0x0005|2 |MSP (main stack pointer) +0x0006-0x0007|2 |RSP (return stack pointer) +0x0008-0x0009|2 |CLT start address +0x000a-0x000b|2 |GPD area start address +0x000c-0x000d|2 |Command buffer start address +0x000e-0x000f|2 |Command buffer size in bytes +0x0010-0x0011|2 |PC (program counter) +0x0012-0x0013|2 |CBP (compilation buffer pointer) +0x0014-0x0015|2 |CLTP (compilation lookup table pointer) +0x0016-0x0017|2 |IBP (input buffer pointer) +0x0018 |1 |II (instruciton ignore mode flag) +0x0019 |1 |CM (compilation mode flag) +0x001a |1 |IM (interpretation mode flag) +0x001b |1 |MM (minification/bypass pseudo-mode flag) +0x001c-... |varies |Main stack - 256 bytes by default +... |varies |Return stack - 256 bytes by default +... |varies |Literal stack - 32 bytes by default +... |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 ~13K 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. 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. +To preserve runtime integrity, Equi implementations are allowed to (but not required to) restrict all writes to the addresses below the GPD area start. Note that command buffer area must not be write-restricted, i.e. Equi programs can be self-modifying. Also, when reading 16-bit from the area below GPD start, only the values at offsets 0x0000 (stack size), 0x0008 (CLT start address), 0x000a (GPD area start address), 0x000c (command buffer start address) and 0x000e (command buffer size) are guaranteed to be populated by the runtime in big-endian, all others will appear in the host endian byte order so the program must not rely on their values in any way. 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. diff --git a/equi.c b/equi.c @@ -34,6 +34,9 @@ #define cputc(c) (putchar(c)) #endif +/* also, do our best to ensure short-packed value storage in our virtual RAM */ +#pragma pack(2) + /* Definitions section */ #define EQUI_VER "0.0.1" @@ -312,6 +315,12 @@ ushort crc16(const uchar* data_p, uchar length) { return crc; } +/* Short endianness conversion helper */ + +ushort tobig(ushort val) { + return (val>>8)|((val&255)<<8); +} + /* Persistent operation handler */ ushort persistOp(FILE *pfd, ushort maddr, ushort dataLen, ushort adl, ushort adh, uchar isWrite) { @@ -587,6 +596,9 @@ void equi_main_loop() { 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; /* _attempt_ to disable buffering for char input/output */ #if !defined __CC65__ && !defined __TINYC__ setvbuf(stdin, NULL, _IONBF, 0); @@ -622,6 +634,15 @@ int main(int argc, char* argv[]) { ram.clt_start, ram.gpd_start - ram.clt_start, ram.gpd_start, ram.cmd_start - ram.gpd_start, ram.cmd_start, ram.cmd_size); } + /* now, if necessary, convert the values that could be read by the program to big-endian */ + if(host_islittle) { + ram.stack_size = tobig(ram.stack_size); + ram.clt_start = tobig(ram.clt_start); + ram.gpd_start = tobig(ram.gpd_start); + ram.cmd_start = tobig(ram.cmd_start); + ram.cmd_size = tobig(ram.cmd_size); + } + while(1) { /* Now, we're in the command mode loop */ instr = cgetc(); /* Fetch the next instruction from the keyboard/stdin */ if(instr == 0xFFU || instr == 0U) /* exit on zero byte */