commit 03f413becc8fe04d50d00b3b8d180f92e2179223
parent 080078cce5798640d8947fe1644083a69d9f6c91
Author: Luxferre <lux@ferre>
Date: Sat, 13 Aug 2022 23:19:55 +0300
Implemented round-robin task switching mechanism
Diffstat:
M | equi.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 */