kernel.c¶
KERNEL.C - Run Machine Kernel
Data Structures
Program Structure:
Created for invoked programs in k_call
struct PROGRAM {
struct PROGRAM* prev; **** IMPORTANT , link to nested program invocation
int16_t no_vars;
DESCRIPTOR* vars;
u_int32_t flags;
/* MS 16 bits, kernel specific; LS 16 bits from object header */
#define IS_EXECUTE 0x00010000L /* Started via EXECUTE */
#define IS_CLEXEC 0x00020000L /* Is pseudo CPROC from EXECUTE CURRENT.LEVEL */
#define IGNORE_ABORTS 0x00040000L /* Ignore aborts from EXECUTEd sentence */
#define PF_IS_TRIGGER 0x00080000L /* Is trigger program */
#define SORT_ACTIVE 0x00100000L /* Program has sort in progress */
#define PF_IS_VFS 0x00200000L /* Is VFS handler */
#define PF_CAPTURING 0x00400000L /* Capture data stacked for this CPROC */
#define PF_IN_TRIGGER 0x00800000L /* This or lower program is a trigger */
#define PF_PRINTER_ON 0x01000000L /* PRINTER ON? */
#define FLAG_COPY_MASK 0x01800000L /* Copy these flags to called program */
int32_t col1;
int32_t col2;
int16_t precision;
u_char* saved_c_base;
int32_t saved_pc_offset;
char saved_prompt_char;
STRING_CHUNK* saved_capture_head;
STRING_CHUNK* saved_capture_tail;
char* break_handler; /* Break handler name */
OBJDATA* objdata;
#define MAX_GOSUB_DEPTH 256
int32_t gosub_stack[MAX_GOSUB_DEPTH];
int16_t gosub_depth;
u_char arg_ct; /* Number of arguments passed */
int16_t e_stack_depth; /* Depth on entry to program */
};
PROCESS Structure:
Only one of these exist, defined and created in kernel.h.
Note it contains a pointer to the "Current" program's PROGRAM structure
struct PROCESS {
/* Dispatch loop control */
int16_t k_abort_code; /* @ABORT.CODE value */
int16_t user_no; /* -1 for cleanup, -2 for sdlnxd */
char username[MAX_USERNAME_LEN + 1];
/* Program control */
struct PROGRAM program; /* Current program state */
int call_depth;
bool debugging; /* Debugger active? */
/* Common areas */
ARRAY_HEADER* named_common; /* Head of named common header chain */
ARRAY_HEADER* syscom; /* $SYSCOM (also in named_common) */
/* Opcode actions */
bool for_init; /* Used by FORINIT and FORTEST */
int16_t break_inhibits; /* Count of BREAK OFF calls */
u_int16_t op_flags; /* Opcode prefix flags */
/* Exact meaning of these flags is opcode dependent, especially the top byte */
#define P_ON_ERROR 0x0001 /* ONERROR opcode executed */
#define P_LOCKED 0x0002 /* NOWAIT opcode executed */
#define P_LLOCK 0x0004 /* LLOCK opcode executed ** See int$keys.h */
#define P_ULOCK 0x0008 /* ULOCK opcode executed ** See int$keys.h */
#define P_READONLY 0x0010 /* READONLY opcode executed */
#define P_PICKREAD 0x0020 /* PICKREAD opcode executed */
#define P_REC_LOCKS (P_LLOCK | P_ULOCK) /* Either LLOCK or ULOCK */
bool numeric_array_allowed; /* Opcode allows numeric arrays? */
int32_t status; /* Value from STATUS() function */
int32_t inmat; /* Value of INMAT() function */
int32_t os_error; /* Operating system error number */
u_int32_t txn_id; /* Transaction id. 0 if none */
};
Vars of Interest:
Public void* object; /* Pointer to current OBJECT */
Public u_char* c_base; /* Base address of current object code */
Public u_char* pc; /* Next opcode byte */
Public u_char* op_pc; /* Address of current opcode */
init_kernel pseudocode:
init_kernel()
dio_init() - init selection arrays and record_cache
zero PROCESS structure process
c_base = NULL
init_program - Initialize PROGRAM structure in process
null pointers to common header chain
process.named_common = NULL
process.syscom = NULL
tio_init - initialize terminal i/o
if connection_type CN_NONE or SD API Srvr
do nothing
else
init_console()
Find open User Table Entry
Assign user number
username (from getpwuid(getuid))
process id
Phantom
VBSRVR
else
fail login message return status false
return
kernel pseudocode:
kernel()
setup signal handlers
signal(SIGSEGV, fatal_signal_handler);
signal(SIGILL, fatal_signal_handler);
signal(SIGBUS, fatal_signal_handler);
signal(SIGCHLD, sigchld_handler);
signal(SIGUSR1, sigusr1_handler);
load the command processor opcode object
k_call(command_processor, 0, NULL, 0);
Setup error processing path
setjmp(k_exit)) /* Abort, Quit, Logout *
recursion_depth = -1 ??
Run the command processor
k_run_program()
exit_kernel:
como_close();
return;
k_call pseudocode:
k_call(char* name, int num_args, u_char* code_ptr, int16_t stack_adj)
If the code_ptr argument is null, we perform a search for the object "name"
Otherwise we simply call the object at that address.
if code_ptr == null
Dynamically loaded object
hdr = pointer to loaded object (load_object() in object.c)
else
hdr = pointer code_ptr
if we are here processing a call from an parent / nested process (c_base != NULL)
save previous PROGRAM state
process.call_depth++;
init_program - Initialize PROGRAM structure on first entry or CALL
set c_base (points to program / object hdr structure)
Setup Hot Spot Monitor in hsm hsm = true
Calculate and allocate space required for program variables in descriptor area
Copy arguments currently on evaluation stack (e-stack) into new process.program.vars
if necessary resize e-stack
allocate memory for new e-stack
copy existing e-stack items and free old stack
return
k_call notes: Called from xxxx at system start up to execute command processor (CPROC or VBSRVR)
Called in op_codes op_call() and op_callv() to execute external subprograms (BASIC CALL statement).
k_run_program pseudocode:
k_run_program - Dispatch Loop
Run (interpret) the object code via the loop:
do {
while (!k_exit_cause) {
dispatch[*(op_pc = pc++)]();
dereference the the address location op_pc returning the OP Code index value into the dispatch array and call the p-code function stored there
}
Where op_pc is a pointer to the start of the OP Code in the object record (OP Code Object{linkID=650})
How this works:
There are two macro calls used in kernel.c that:
1) Build the op_code function prototypes:
/* Declare opcode functions */
#define _opc_(code, key, name, func, format, stack_use) void func(void);
#include "opcodes.h"
#undef _opc_
That result in the function prototype being generated for each entry in opcodes.h:
void op_stop(void);
void op_abort(void);
void op_return(void);
.
.
2) Build the dispatch table array (array of function pointers, see https://www.guru99.com/c-function-pointers.html):
#define _opc_(code, key, name, func, format, stack_use) func,
void (*dispatch[])(void) = {
#include "opcodes.h"
};
#undef _opc_
That results in an element for each op code function
void (*dispatch[])(void) = {
op_stop,
op_abort,
op_return,
.
.
}
We now have a mechanism to to invoke each op code function via it's index into the dispatch array.
These are the same numbers used to refernce OP Code functions used by the basic compiler when building
the program object record (see /GPL.BP/BCOMP)
Accessing Kernel Varaibles in Basic¶
BASIC programs Set / Get Kernel variables via the intrinsic function kernel() op_kernel.c.
Samples:
i = kernel(K$CPROC.LEVEL,0) returns CPROC nesting level note: it appears a value of 0 returns the current value a value > 0 sets the variable to the new value i = kernel(K$CPROC.LEVEL,1) sets CPROC nesting level to 1 Keys: K$INTERNAL Set or clear internal mode K$INTERNAL.QUERY Query internal mode K$PAGINATE Test or modify pagination flag K$FLAGS Test/return program header flags K$DATE.FORMAT European date format? K$CRTWIDE Return display width K$CRTHIGH Return display lines per page K$SET.DATE Set current date K$IS.PHANTOM Is this a phantom process K$TERM.TYPE Terminal type name K$USERNAME User name K$DATE.CONV Set default date conversion K$PPID Get parent process id K$USERS Get user list K$INIPATH Get ini file pathname K$FORCED.ACCOUNT Force entry to named account unless set in $LOGINS K$SDNET Get/set SDNet status flag K$CPROC.LEVEL Get/set command processor level K$HELP Invoke help system K$SUPPRESS.COMO Supress/resume como file output K$ADMINISTRATOR Get/set administrator rights K$SECURE Secure system? K$GET.OPTIONS Get options flags K$SET.OPTIONS Set options flags K$PRIVATE.CATALOGUE Set private catalogue pathname K$CLEANUP Clean up defunct users K$COMMAND.OPTIONS Get command line option flags K$CASE.SENSITIVE REMOVE.TOKEN() cases sensitivity K$SET.LANGUAGE Set language for message handler K$COLLATION Set/clear sort collation data K$GET.SDNET.CONNECTIONS Get details of open SDNet connections K$INVALIDATE.OBJECT Invalidate object cache K$MESSAGE Enable/disable message reception K$SET.EXIT.CAUSE Set k_exit_cause K$COLLATION.NAME Set primary collation map name K$AK.COLLATION Select AK collation map K$EXIT.STATUS Set exit status K$AUTOLOGOUT Set/retrieve autologout period K$MAP.DIR.IDS Enable/disable dir file id mapping