Implemented round-robin task switching mechanism

This commit is contained in:
Luxferre
2022-08-13 23:19:55 +03:00
parent 080078cce5
commit 03f413becc
+56 -25
View File
@@ -97,13 +97,14 @@ struct CLTEntry { /* one entry in the compilation lookup table */
struct EquiCtx { /* one Equi program context */
ushort id; /* task ID */
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 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 cltp; /* compilation lookup table pointer */
ushort cbp; /* compilation buffer pointer */
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 */