equi

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

commit 03f413becc8fe04d50d00b3b8d180f92e2179223
parent 080078cce5798640d8947fe1644083a69d9f6c91
Author: Luxferre <lux@ferre>
Date:   Sat, 13 Aug 2022 23:19:55 +0300

Implemented round-robin task switching mechanism

Diffstat:
Mequi.c | 77++++++++++++++++++++++++++++++++++++++++++++++++++++++-----------------------
1 file changed, 54 insertions(+), 23 deletions(-)

diff --git a/equi.c b/equi.c @@ -97,13 +97,14 @@ struct CLTEntry { /* one entry in the compilation lookup table */ struct EquiCtx { /* one Equi program context */ ushort id; /* task ID */ + uchar active; /* 0 - inactive/quit, 1 - active */ + uchar CM; /* compilation mode flag */ + uchar IM; /* interpretation mode flag */ + uchar lsp; /* literal stack pointer */ ushort msp; /* main stack pointer */ ushort rsp; /* return stack pointer */ - ushort lsp; /* literal stack pointer */ ushort cltp; /* compilation lookup table pointer */ ushort cbp; /* compilation buffer pointer */ - uchar CM; /* compilation mode flag */ - uchar IM; /* interpretation mode flag */ ushort gpd_start; /* GPD area start for this task */ ushort cmd_start; /* command buffer start for this task */ ushort cmd_size; /* size of loaded code in bytes for this task */ @@ -123,8 +124,7 @@ struct EquiRAM { ushort ibp; /* input buffer pointer */ uchar II; /* instruction ignore mode flag */ uchar MM; /* minification bypass mode flag */ - ushort taskcount; /* count of active tasks */ - ushort taskid; /* currently running task ID - necessary? */ + ushort taskid; /* currently running task ID */ struct EquiCtx tasks[EQUI_TASKS_MAX]; uchar cmdbuf[CMD_BUF_SIZE]; }; @@ -151,13 +151,17 @@ enum EquiErrors { PORT_IO_ERROR, PERSIST_IO_ERROR, RESTRICTED_WRITE_ERROR, - OUT_OF_BOUNDS_JUMP + OUT_OF_BOUNDS_JUMP, + TASK_SLOTS_FULL }; /* Error reporting method */ void trapout(errcode) { if(errcode > 0) { - fprintf(stderr, "\nError %d at 0x%x (task 0x%x, instruction %c): ", errcode, curtask->pc, curtask->id, flatram[curtask->pc]); + if(curtask) + fprintf(stderr, "\nError %d at 0x%x (task 0x%x, instruction %c): ", errcode, curtask->pc, curtask->id, flatram[curtask->pc]); + else + fprintf(stderr, "\nSystem error %d: ", errcode); switch(errcode) { case STACK_OVERFLOW: cerr("Stack overflow\n"); @@ -192,6 +196,9 @@ void trapout(errcode) { case OUT_OF_BOUNDS_JUMP: cerr("Attempt to jump outside the task context\n"); break; + case TASK_SLOTS_FULL: + cerr("All task slots busy and active\n"); + break; } exit(errcode); } @@ -391,18 +398,43 @@ void portIO(ushort port, ushort p2, ushort p1) { pushMain(status); } +/* end of internal helper functions, start of main Equi functions */ + +/* Find free task slot among remaining inactive tasks */ +ushort equi_find_free_task_slot() { + ushort slotid = 0; + /* iterate over task table */ + for(;slotid < EQUI_TASKS_MAX;slotid++) + if(!ram.tasks[slotid].active) + return slotid; + /* and error out if all slots are busy and active */ + trapout(TASK_SLOTS_FULL); + return TASK_SLOTS_FULL; +} + +/* Find the next active task in the table, round-robin fashion */ +struct EquiCtx* equi_find_next_task() { + ushort oldtaskid = ram.taskid; /* save the current one to avoid deadlocks */ + while(!ram.tasks[ram.taskid].active) { + ++ram.taskid; + if(ram.taskid == EQUI_TASKS_MAX) ram.taskid = 0; + if(ram.taskid == oldtaskid) /* found no active tasks, bailing out */ + return NULL; + } + return &ram.tasks[ram.taskid]; +} + /* Task loader method */ struct EquiCtx* equi_load_task(ushort progStart, ushort len) { - struct EquiCtx *taskptr = &ram.tasks[ram.taskcount]; /* refer to the last available entry */ + ushort tid = equi_find_free_task_slot(); + struct EquiCtx *taskptr = &ram.tasks[tid]; /* refer to the next available entry */ taskptr->msp = taskptr->rsp = taskptr->lsp = taskptr->cltp = 0; /* init stacks and CLT */ taskptr->pc = progStart - 1U; /* init program counter for preincrement logic */ taskptr->cmd_start = progStart; /* actual command buffer start (from the start of vRAM) */ taskptr->cmd_size = len; /* actual command buffer size */ - taskptr->id = ram.taskcount; /* assign task ID */ + taskptr->id = tid; /* assign task ID */ taskptr->gpd_start = (ushort) (taskptr->gpd - flatram); /* assign GPD area start */ - taskptr->IM = 1; /* set interpretation mode flag */ taskptr->CM = 0; /* unset compilation mode flag */ - ++ram.taskcount; /* increase task count */ return taskptr; } @@ -413,9 +445,12 @@ void equi_main_loop() { /* try to open the persistent sandbox file */ FILE *pfd = fopen(PERSIST_FILE, "r+b"); while(1) { /* iterate over the instructions in the command buffer */ + curtask = equi_find_next_task(); /* attempt to switch the context on every iteration */ + if(curtask == NULL) break; /* exit the main loop when no tasks are left */ instr = flatram[++curtask->pc]; /* silently exit on zero or FF */ - if(instr == 0 || instr == 0xFFu) break; + if(instr == 0 || instr == 0xFFu) + curtask->active = 0; /* first, check for II mode */ if(ram.II) { if(instr == INS_IIEND) @@ -458,8 +493,9 @@ void equi_main_loop() { case '\t': case ' ': /* all nops in interpretation mode */ break; - case INS_QUIT: /* gracefully quit the interpretation mode */ - goto brx; + case INS_QUIT: /* gracefully quit the task */ + curtask->active = 0; + break; case INS_LITINT: /* literal stack -> main stack as short */ pushLitVal(); break; @@ -622,20 +658,14 @@ void equi_main_loop() { break; default: /* all characters not processed before are invalid instructions */ trapout(INVALID_INSTRUCTION); - goto brx; } if(curtask->msp >= STACK_SIZE_WORDS || curtask->rsp >= STACK_SIZE_WORDS) /* check for stack overflow after any operation */ trapout(STACK_OVERFLOW); - continue; - brx: break; } /* close the persistent sandbox file if open */ if(pfd) fclose(pfd); - /* unset interpretation mode flag and exit */ - curtask->IM = 0; - curtask->pc = ram.ibp = 65535U; - /* clear all stacks and CLT */ - curtask->msp = curtask->rsp = curtask->lsp = curtask->cltp = 0; + /* reset IBP and exit */ + ram.ibp = 65535U; } /* Equi VM entry point */ @@ -657,7 +687,6 @@ int main(int argc, char* argv[]) { ram.cmd_start = (uchar *)&ram.cmdbuf - (uchar *)&ram; ram.cmd_size = CMD_BUF_SIZE; ram.II = ram.MM = 0; /* reset all flags */ - ram.taskcount = 0; /* process command line params */ if(argc > 1 && argv[1][0] == 'm') /* enter minification mode, don't run the programs */ ram.MM = 1; @@ -709,6 +738,8 @@ int main(int argc, char* argv[]) { } ram.cmdbuf[++ram.ibp] = INS_QUIT; /* end program with INS_QUIT */ curtask = equi_load_task((ushort)ram.cmd_start, ram.ibp + 1); /* load the code as task 0 */ + ram.taskid = curtask->id; /* actualize the current task id */ + curtask->active = 1; /*activate the current task */ equi_main_loop(); /* and run the interpreter loop */ if(!smode) { cputc(CR); /* echo CR */