memory revamp described, endianness checked

This commit is contained in:
Luxferre
2022-08-13 08:46:25 +03:00
parent df9d9b7cbc
commit 03cf5e95fe
2 changed files with 44 additions and 18 deletions
+23 -18
View File
@@ -19,6 +19,7 @@ Main features of an Equi machine:
- Interpretation mode (IM) flag; - Interpretation mode (IM) flag;
- Compilation mode (CM) flag; - Compilation mode (CM) flag;
- Instruction ignore (II) flag; - Instruction ignore (II) flag;
- Minification mode (MM) flag;
- 16-bit input buffer pointer; - 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; - 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); - 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 Address range|Size (bytes)|Purpose
-------------|------------|--------------------- -------------|------------|---------------------
0x0000-0x00ff|256 |Main stack 0x0000-0x0001|2 |Main/return stack size in words
0x0100-0x01ff|256 |Return stack 0x0002 |1 |Literal stack size in bytes (up to 255)
0x0200-0x021f|32 |Literal stack 0x0003 |1 |LSP (literal stack pointer)
0x0220-0x0221|2 |PC (program counter) 0x0004-0x0005|2 |MSP (main stack pointer)
0x0222-0x0223|2 |MSP (main stack pointer) 0x0006-0x0007|2 |RSP (return stack pointer)
0x0224-0x0225|2 |RSP (return stack pointer) 0x0008-0x0009|2 |CLT start address
0x0226 |1 |LSP (literal stack pointer) 0x000a-0x000b|2 |GPD area start address
0x0227-0x0228|2 |CBP (compilation buffer pointer) 0x000c-0x000d|2 |Command buffer start address
0x0229-0x022a|2 |CLTP (compilation lookup table pointer) 0x000e-0x000f|2 |Command buffer size in bytes
0x022b |1 |Flags (II/CM/IM) 0x0010-0x0011|2 |PC (program counter)
0x022c-0x022d|2 |GPD area start address 0x0012-0x0013|2 |CBP (compilation buffer pointer)
0x022e-0x022f|2 |Command buffer start address 0x0014-0x0015|2 |CLTP (compilation lookup table pointer)
0x0230-0x0231|2 |Command buffer size in bytes 0x0016-0x0017|2 |IBP (input buffer pointer)
0x0232-0x0233|2 |IBP (input buffer pointer) 0x0018 |1 |II (instruciton ignore mode flag)
0x0234-... |varies |Compilation lookup table (CLT) - up to 1024 compiled words by default 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 ... |varies |General purpose data (GPD) area
...-0x7dff |varies |Command buffer 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. 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.
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. 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.
+21
View File
@@ -34,6 +34,9 @@
#define cputc(c) (putchar(c)) #define cputc(c) (putchar(c))
#endif #endif
/* also, do our best to ensure short-packed value storage in our virtual RAM */
#pragma pack(2)
/* Definitions section */ /* Definitions section */
#define EQUI_VER "0.0.1" #define EQUI_VER "0.0.1"
@@ -312,6 +315,12 @@ ushort crc16(const uchar* data_p, uchar length) {
return crc; return crc;
} }
/* Short endianness conversion helper */
ushort tobig(ushort val) {
return (val>>8)|((val&255)<<8);
}
/* Persistent operation handler */ /* Persistent operation handler */
ushort persistOp(FILE *pfd, ushort maddr, ushort dataLen, ushort adl, ushort adh, uchar isWrite) { 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[]) { int main(int argc, char* argv[]) {
uchar instr, bc, smode = 0; 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 */ /* _attempt_ to disable buffering for char input/output */
#if !defined __CC65__ && !defined __TINYC__ #if !defined __CC65__ && !defined __TINYC__
setvbuf(stdin, NULL, _IONBF, 0); 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); 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 */ while(1) { /* Now, we're in the command mode loop */
instr = cgetc(); /* Fetch the next instruction from the keyboard/stdin */ instr = cgetc(); /* Fetch the next instruction from the keyboard/stdin */
if(instr == 0xFFU || instr == 0U) /* exit on zero byte */ if(instr == 0xFFU || instr == 0U) /* exit on zero byte */