Implemented round-robin task switching mechanism
This commit is contained in:
@@ -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 */
|
||||
|
||||
Reference in New Issue
Block a user