_h˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙ü˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙A@“Œ:!˙AP“Œ:!¤˜“Œ:! ¤Í“Œ:! ¤@“Œ:! ¤J “Œ:!¤§"“Œ:!¤˙ “Œ:!¤Le“Œ:! !"#$%'¤§"“Œ:!:;<=>?@B¤b“Œ:!DEFGH¤¯&“Œ:!IJKLMNOQ¤ 3“Œ:!TUVWXYZ\¤ J“Œ:!bcdefghj¤:“Œ:!vw¤ÃŊŒ:!xyz{|}~€¤ąf“Œ:!¨ŠĒĢŦ­Ž°¤ķ=“Œ:!ÃÄÅÆĮČÉË˙AP“Œ:!Ô¤Ŗ “Œ:!ÕÖפ I“Œ:!ØŲÚÛÜŨŪ⤁ß)“Œ:!ėíîīđņōô˙AP>:!ø¤ “Œ:!ųúû˙Ap“Œ:!ü¤V “Œ:!ũū˙¤ɓŒ:!¤c“Œ:!¤ÖA“Œ:!  ¤x)“Œ:!˙A “Œ:!!¤“Œ:!"#¤]“Œ:!$¤ū“Œ:!%¤“Œ:!&'(¤=“Œ:!)*¤Ũ“Œ:!+,-./0¤ĩ“Œ:!1234¤0“Œ:!5678+ę!...kernelh...type.hproc.hglo.hconst.hclock.cdmp.c floppy.c main.c memory.c printer.c proc.csystem.ctable.ctty.cxt_wini.cat_wini.cMINIXPCIXC86/* The 'pc_psw' struct is machine dependent. It must contain the information * pushed onto the stack by an interrupt, in the same format as the hardware * creates and expects. It is used for storing the interrupt status after a * trap or interrupt, as well as for causing interrupts for signals. */ #ifdef i8088 struct pc_psw { int (*pc)(); /* storage for program counter */ phys_clicks cs; /* code segment register */ unsigned psw; /* program status word */ }; /* This struct is used to build data structure pushed by kernel upon signal. */ struct sig_info { int signo; /* sig number at end of stack */ struct pc_psw sigpcpsw; }; #endif /* Here is the declaration of the process table. Three assembly code routines * reference fields in it. They are restart(), save(), and csv(). When * changing 'proc', be sure to change the field offsets built into the code. * It contains the process' registers, memory map, accounting, and message * send/receive information. */ EXTERN struct proc { int p_reg[NR_REGS]; /* process' registers */ int *p_sp; /* stack pointer */ struct pc_psw p_pcpsw; /* pc and psw as pushed by interrupt */ int p_flags; /* P_SLOT_FREE, SENDING, RECEIVING, etc. */ struct mem_map p_map[NR_SEGS];/* memory map */ int *p_splimit; /* lowest legal stack value */ int p_pid; /* process id passed in from MM */ real_time user_time; /* user time in ticks */ real_time sys_time; /* sys time in ticks */ real_time child_utime; /* cumulative user time of children */ real_time child_stime; /* cumulative sys time of children */ real_time p_alarm; /* time of next alarm in ticks, or 0 */ struct proc *p_callerq; /* head of list of procs wishing to send */ struct proc *p_sendlink; /* link to next proc wishing to send */ message *p_messbuf; /* pointer to message buffer */ int p_getfrom; /* from whom does process want to receive? */ struct proc *p_nextready; /* pointer to next ready process */ int p_pending; /* bit map for pending signals 1-16 */ } proc[NR_TASKS+NR_PROCS]; /* Bits for p_flags in proc[]. A process is runnable iff p_flags == 0 */ #define P_SLOT_FREE 001 /* set when slot is not in use */ #define NO_MAP 002 /* keeps unmapped forked child from running */ #define SENDING 004 /* set when process blocked trying to send */ #define RECEIVING 010 /* set when process blocked trying to recv */ #define proc_addr(n) &proc[NR_TASKS + n] #define NIL_PROC (struct proc *) 0 EXTERN struct proc *proc_ptr; /* &proc[cur_proc] */ EXTERN struct proc *bill_ptr; /* ptr to process to bill for clock ticks */ EXTERN struct proc *rdy_head[NQ]; /* pointers to ready list headers */ EXTERN struct proc *rdy_tail[NQ]; /* pointers to ready list tails */ EXTERN unsigned busy_map; /* bit map of busy tasks */ EXTERN message *task_mess[NR_TASKS+1]; /* ptrs to messages for busy tasks */ /* Global variables used in the kernel. */ /* Clocks and timers */ EXTERN real_time realtime; /* real time clock */ EXTERN int lost_ticks; /* incremented when clock int can't send mess*/ /* Processes, signals, and messages. */ EXTERN int cur_proc; /* current process */ EXTERN int prev_proc; /* previous process */ EXTERN int sig_procs; /* number of procs with p_pending != 0 */ EXTERN message int_mess; /* interrupt routines build message here */ /* CPU type. */ EXTERN int olivetti; /* TRUE for Olivetti-style keyboard */ EXTERN int pc_at; /* PC-AT type diskette drives (360K/1.2M) ? */ /* The kernel and task stacks. */ EXTERN struct t_stack { int stk[TASK_STACK_BYTES/sizeof(int)]; } t_stack[NR_TASKS - 1]; /* task stacks; task = -1 never really runs */ EXTERN char k_stack[K_STACK_BYTES]; /* The kernel stack. */ /* General constants used by the kernel. */ #ifdef i8088 /* p_reg contains: ax, bx, cx, dx, si, di, bp, es, ds, cs, ss in that order. */ #define NR_REGS 11 /* number of general regs in each proc slot */ #define INIT_PSW 0x0200 /* initial psw */ #define INIT_SP (int*)0x0010 /* initial sp: 3 words pushed by kernel */ /* The following values are used in the assembly code. Do not change the * values of 'ES_REG', 'DS_REG', 'CS_REG', or 'SS_REG' without making the * corresponding changes in the assembly code. */ #define ES_REG 7 /* proc[i].p_reg[ESREG] is saved es */ #define DS_REG 8 /* proc[i].p_reg[DSREG] is saved ds */ #define CS_REG 9 /* proc[i].p_reg[CSREG] is saved cs */ #define SS_REG 10 /* proc[i].p_reg[SSREG] is saved ss */ #define VECTOR_BYTES 284 /* bytes of interrupt vectors to save */ #define MEM_BYTES 655360L /* memory size for /dev/mem */ /* Interrupt vectors */ #define DIVIDE_VECTOR 0 /* divide interrupt vector */ #define CLOCK_VECTOR 8 /* clock interrupt vector */ #define KEYBOARD_VECTOR 9 /* keyboard interrupt vector */ #define XT_WINI_VECTOR 13 /* xt winchester interrupt vector */ #define FLOPPY_VECTOR 14 /* floppy disk interrupt vector */ #define PRINTER_VECTOR 15 /* line printer interrupt vector */ #define SYS_VECTOR 32 /* system calls are made with int SYSVEC */ #define AT_WINI_VECTOR 118 /* at winchester interrupt vector */ /* The 8259A interrupt controller has to be re-enabled after each interrupt. */ #define INT_CTL 0x20 /* I/O port for interrupt controller */ #define INT_CTLMASK 0x21 /* setting bits in this port disables ints */ #define INT2_CTL 0xA0 /* I/O port for second interrupt controller */ #define INT2_MASK 0xA1 /* setting bits in this port disables ints */ #define ENABLE 0x20 /* code used to re-enable after an interrupt */ #endif #define TASK_STACK_BYTES 256 /* how many bytes for each task stack */ #define K_STACK_BYTES 256 /* how many bytes for the kernel stack */ #define RET_REG 0 /* system call return codes go in this reg */ #define IDLE -999 /* 'cur_proc' = IDLE means nobody is running */ /* The following items pertain to the 3 scheduling queues. */ #define NQ 3 /* # of scheduling queues */ #define TASK_Q 0 /* ready tasks are scheduled via queue 0 */ #define SERVER_Q 1 /* ready servers are scheduled via queue 1 */ #define USER_Q 2 /* ready users are scheduled via queue 2 */ #define printf printk /* the kernel really uses printk, not printf */ /* This file contains the code and data for the clock task. The clock task * has a single entry point, clock_task(). It accepts four message types: * * CLOCK_TICK: a clock interrupt has occurred * GET_TIME: a process wants the real time * SET_TIME: a process wants to set the real time * SET_ALARM: a process wants to be alerted after a specified interval * * The input message is format m6. The parameters are as follows: * * m_type CLOCK_PROC FUNC NEW_TIME * --------------------------------------------- * | SET_ALARM | proc_nr |f to call| delta | * |------------+----------+---------+---------| * | CLOCK_TICK | | | | * |------------+----------+---------+---------| * | GET_TIME | | | | * |------------+----------+---------+---------| * | SET_TIME | | | newtime | * --------------------------------------------- * * When an alarm goes off, if the caller is a user process, a SIGALRM signal * is sent to it. If it is a task, a function specified by the caller will * be invoked. This function may, for example, send a message, but only if * it is certain that the task will be blocked when the timer goes off. */ #include "../h/const.h" #include "../h/type.h" #include "../h/callnr.h" #include "../h/com.h" #include "../h/error.h" #include "../h/signal.h" #include "const.h" #include "type.h" #include "glo.h" #include "proc.h" /* Constant definitions. */ #define MILLISEC 100 /* how often to call the scheduler (msec) */ #define SCHED_RATE (MILLISEC*HZ/1000) /* number of ticks per schedule */ /* Clock parameters. */ #define TIMER0 0x40 /* port address for timer channel 0 */ #define TIMER_MODE 0x43 /* port address for timer channel 3 */ #define IBM_FREQ 1193182L /* IBM clock frequency for setting timer */ #define SQUARE_WAVE 0x36 /* mode for generating square wave */ /* Clock task variables. */ PRIVATE real_time boot_time; /* time in seconds of system boot */ PRIVATE real_time next_alarm; /* probable time of next alarm */ PRIVATE int sched_ticks = SCHED_RATE; /* counter: when 0, call scheduler */ PRIVATE struct proc *prev_ptr; /* last user process run by clock task */ PRIVATE message mc; /* message buffer for both input and output */ PRIVATE int (*watch_dog[NR_TASKS+1])(); /* watch_dog functions to call */ /*===========================================================================* * clock_task * *===========================================================================*/ PUBLIC clock_task() { /* Main program of clock task. It determines which of the 4 possible * calls this is by looking at 'mc.m_type'. Then it dispatches. */ int opcode; init_clock(); /* initialize clock tables */ /* Main loop of the clock task. Get work, process it, sometimes reply. */ while (TRUE) { receive(ANY, &mc); /* go get a message */ opcode = mc.m_type; /* extract the function code */ switch (opcode) { case SET_ALARM: do_setalarm(&mc); break; case GET_TIME: do_get_time(); break; case SET_TIME: do_set_time(&mc); break; case CLOCK_TICK: do_clocktick(); break; default: panic("clock task got bad message", mc.m_type); } /* Send reply, except for clock tick. */ mc.m_type = OK; if (opcode != CLOCK_TICK) send(mc.m_source, &mc); } } /*===========================================================================* * do_setalarm * *===========================================================================*/ PRIVATE do_setalarm(m_ptr) message *m_ptr; /* pointer to request message */ { /* A process wants an alarm signal or a task wants a given watch_dog function * called after a specified interval. Record the request and check to see * it is the very next alarm needed. */ register struct proc *rp; int proc_nr; /* which process wants the alarm */ long delta_ticks; /* in how many clock ticks does he want it? */ int (*function)(); /* function to call (tasks only) */ /* Extract the parameters from the message. */ proc_nr = m_ptr->CLOCK_PROC_NR; /* process to interrupt later */ delta_ticks = m_ptr->DELTA_TICKS; /* how many ticks to wait */ function = m_ptr->FUNC_TO_CALL; /* function to call (tasks only) */ rp = proc_addr(proc_nr); mc.SECONDS_LEFT = (rp->p_alarm == 0L ? 0 : (rp->p_alarm - realtime)/HZ ); rp->p_alarm = (delta_ticks == 0L ? 0L : realtime + delta_ticks); if (proc_nr < 0) watch_dog[-proc_nr] = function; /* Which alarm is next? */ next_alarm = MAX_P_LONG; for (rp = &proc[0]; rp < &proc[NR_TASKS+NR_PROCS]; rp++) if(rp->p_alarm != 0 && rp->p_alarm < next_alarm)next_alarm=rp->p_alarm; } /*===========================================================================* * do_get_time * *===========================================================================*/ PRIVATE do_get_time() { /* Get and return the current clock time in ticks. */ mc.m_type = REAL_TIME; /* set message type for reply */ mc.NEW_TIME = boot_time + realtime/HZ; /* current real time */ } /*===========================================================================* * do_set_time * *===========================================================================*/ PRIVATE do_set_time(m_ptr) message *m_ptr; /* pointer to request message */ { /* Set the real time clock. Only the superuser can use this call. */ boot_time = m_ptr->NEW_TIME - realtime/HZ; } /*===========================================================================* * do_clocktick * *===========================================================================*/ PRIVATE do_clocktick() { /* This routine called on every clock tick. */ register struct proc *rp; register int t, proc_nr; extern int pr_busy, pcount, cum_count, prev_ct; /* To guard against race conditions, first copy 'lost_ticks' to a local * variable, add this to 'realtime', and then subtract it from 'lost_ticks'. */ t = lost_ticks; /* 'lost_ticks' counts missed interrupts */ realtime += t + 1; /* update the time of day */ lost_ticks -= t; /* these interrupts are no longer missed */ if (next_alarm <= realtime) { /* An alarm may have gone off, but proc may have exited, so check. */ next_alarm = MAX_P_LONG; /* start computing next alarm */ for (rp = &proc[0]; rp < &proc[NR_TASKS+NR_PROCS]; rp++) { if (rp->p_alarm != (real_time) 0) { /* See if this alarm time has been reached. */ if (rp->p_alarm <= realtime) { /* A timer has gone off. If it is a user proc, * send it a signal. If it is a task, call the * function previously specified by the task. */ proc_nr = rp - proc - NR_TASKS; if (proc_nr >= 0) cause_sig(proc_nr, SIGALRM); else (*watch_dog[-proc_nr])(); rp->p_alarm = 0; } /* Work on determining which alarm is next. */ if (rp->p_alarm != 0 && rp->p_alarm < next_alarm) next_alarm = rp->p_alarm; } } } accounting(); /* keep track of who is using the cpu */ /* If a user process has been running too long, pick another one. */ if (--sched_ticks == 0) { if (bill_ptr == prev_ptr) sched(); /* process has run too long */ sched_ticks = SCHED_RATE; /* reset quantum */ prev_ptr = bill_ptr; /* new previous process */ /* Check if printer is hung up, and if so, restart it. */ if (pr_busy && pcount > 0 && cum_count == prev_ct) pr_char(); prev_ct = cum_count; /* record # characters printed so far */ } } /*===========================================================================* * accounting * *===========================================================================*/ PRIVATE accounting() { /* Update user and system accounting times. The variable 'bill_ptr' is always * kept pointing to the process to charge for CPU usage. If the CPU was in * user code prior to this clock tick, charge the tick as user time, otherwise * charge it as system time. */ if (prev_proc >= LOW_USER) bill_ptr->user_time++; /* charge CPU time */ else bill_ptr->sys_time++; /* charge system time */ } #ifdef i8088 /*===========================================================================* * init_clock * *===========================================================================*/ PRIVATE init_clock() { /* Initialize channel 2 of the 8253A timer to e.g. 60 Hz. */ unsigned int count, low_byte, high_byte; count = (unsigned) (IBM_FREQ/HZ); /* value to load into the timer */ low_byte = count & BYTE; /* compute low-order byte */ high_byte = (count >> 8) & BYTE; /* compute high-order byte */ port_out(TIMER_MODE, SQUARE_WAVE); /* set timer to run continuously */ port_out(TIMER0, low_byte); /* load timer low byte */ port_out(TIMER0, high_byte); /* load timer high byte */ } #endif /* This file contains some dumping routines for debugging. */ #include "../h/const.h" #include "../h/type.h" #include "../h/callnr.h" #include "../h/com.h" #include "../h/error.h" #include "const.h" #include "type.h" #include "glo.h" #include "proc.h" #define NSIZE 20 phys_bytes aout[NR_PROCS]; /* pointers to the program names */ char nbuff[NSIZE+1]; int vargv; /*===========================================================================* * DEBUG routines here * *===========================================================================*/ p_dmp() { /* Proc table dump */ register struct proc *rp; char *np; vir_bytes base, limit, first, last; phys_bytes ltmp, dst; int index; extern phys_bytes umap(); printf( "\nproc -pid- -pc- -sp- flag user -sys- base limit recv command\n"); dst = umap(proc_addr(SYSTASK), D, nbuff, NSIZE); for (rp = &proc[0]; rp < &proc[NR_PROCS+NR_TASKS]; rp++) { if (rp->p_flags & P_SLOT_FREE) continue; first = rp->p_map[T].mem_phys; last = rp->p_map[S].mem_phys + rp->p_map[S].mem_len; ltmp = ((long) first << 4) + 512L; base = (vir_bytes) (ltmp/1024L); ltmp = (((long) last << 4) + 512L); limit = (vir_bytes) (ltmp/1024L); prname(rp - proc); printf(" %4d %4x %4x %4x %6D %7D %3dK %3dK ", rp->p_pid, rp->p_pcpsw.pc, rp->p_sp, rp->p_flags, rp->user_time, rp->sys_time, base, limit); if (rp->p_flags == 0) printf(" "); else prname(NR_TASKS + rp->p_getfrom); /* Fetch the command string from the user process. */ index = rp - proc - NR_TASKS; if (index > LOW_USER && aout[index] != 0) { phys_copy(aout[index], dst, (long) NSIZE); aout[NSIZE] = 0; for (np = &nbuff[0]; np < &nbuff[NSIZE]; np++) if (*np <= ' ' || *np >= 0177) *np = 0; if (index == LOW_USER + 1) printf("/bin/sh"); else printf("%s", nbuff); } printf("\n"); } printf("\n"); } map_dmp() { register struct proc *rp; vir_bytes base, limit, first, last; phys_bytes ltmp; printf("\nPROC -----TEXT----- -----DATA----- ----STACK----- BASE SIZE\n"); for (rp = &proc[NR_TASKS]; rp < &proc[NR_TASKS+NR_PROCS]; rp++) { if (rp->p_flags & P_SLOT_FREE) continue; first = rp->p_map[T].mem_phys; last = rp->p_map[S].mem_phys + rp->p_map[S].mem_len; ltmp = ((long) first << 4) + 512L; base = (vir_bytes) (ltmp/1024L); ltmp = (((long) (last-first) << 4) + 512L); limit = (vir_bytes) (ltmp/1024L); prname(rp-proc); printf(" %4x %4x %4x %4x %4x %4x %4x %4x %4x %3dK %3dK\n", rp->p_map[T].mem_vir, rp->p_map[T].mem_phys, rp->p_map[T].mem_len, rp->p_map[D].mem_vir, rp->p_map[D].mem_phys, rp->p_map[D].mem_len, rp->p_map[S].mem_vir, rp->p_map[S].mem_phys, rp->p_map[S].mem_len, base, limit); } } char *nayme[]= {"PRINTR", "TTY ", "WINCHE", "FLOPPY", "RAMDSK", "CLOCK ", "SYS ", "HARDWR", "MM ", "FS ", "INIT "}; prname(i) int i; { if (i == ANY+NR_TASKS) printf("ANY "); else if (i >= 0 && i <= NR_TASKS+2) printf("%s",nayme[i]); else printf("%4d ", i-NR_TASKS); } set_name(proc_nr, ptr) int proc_nr; char *ptr; { /* When an EXEC call is done, the kernel is told about the stack pointer. * It uses the stack pointer to find the command line, for dumping * purposes. */ extern phys_bytes umap(); phys_bytes src, dst, count; if (ptr == (char *) 0) { aout[proc_nr] = (phys_bytes) 0; return; } src = umap(proc_addr(proc_nr), D, ptr + 2, 2); if (src == 0) return; dst = umap(proc_addr(SYSTASK), D, &vargv, 2); phys_copy(src, dst, 2L); aout[proc_nr] = umap(proc_addr(proc_nr), D, vargv, NSIZE); } /* This file contains a driver for a Floppy Disk Controller (FDC) using the * NEC PD765 chip. The driver supports two operations: read a block and * write a block. It accepts two messages, one for reading and one for * writing, both using message format m2 and with the same parameters: * * m_type DEVICE PROC_NR COUNT POSITION ADRRESS * ---------------------------------------------------------------- * | DISK_READ | device | proc nr | bytes | offset | buf ptr | * |------------+---------+---------+---------+---------+---------| * | DISK_WRITE | device | proc nr | bytes | offset | buf ptr | * ---------------------------------------------------------------- * * The file contains one entry point: * * floppy_task: main entry when system is brought up * * Changes: * 27 october 1986 by Jakob Schripsema: fdc_results fixed for 8 MHz */ #include "../h/const.h" #include "../h/type.h" #include "../h/callnr.h" #include "../h/com.h" #include "../h/error.h" #include "const.h" #include "type.h" #include "glo.h" #include "proc.h" /* I/O Ports used by floppy disk task. */ #define DOR 0x3F2 /* motor drive control bits */ #define FDC_STATUS 0x3F4 /* floppy disk controller status register */ #define FDC_DATA 0x3F5 /* floppy disk controller data register */ #define FDC_RATE 0x3F7 /* transfer rate register */ #define DMA_ADDR 0x004 /* port for low 16 bits of DMA address */ #define DMA_TOP 0x081 /* port for top 4 bits of 20-bit DMA addr */ #define DMA_COUNT 0x005 /* port for DMA count (count = bytes - 1) */ #define DMA_M2 0x00C /* DMA status port */ #define DMA_M1 0x00B /* DMA status port */ #define DMA_INIT 0x00A /* DMA init port */ /* Status registers returned as result of operation. */ #define ST0 0x00 /* status register 0 */ #define ST1 0x01 /* status register 1 */ #define ST2 0x02 /* status register 2 */ #define ST3 0x00 /* status register 3 (return by DRIVE_SENSE) */ #define ST_CYL 0x03 /* slot where controller reports cylinder */ #define ST_HEAD 0x04 /* slot where controller reports head */ #define ST_SEC 0x05 /* slot where controller reports sector */ #define ST_PCN 0x01 /* slot where controller reports present cyl */ /* Fields within the I/O ports. */ #define MASTER 0x80 /* used to see who is master */ #define DIRECTION 0x40 /* is FDC trying to read or write? */ #define CTL_BUSY 0x10 /* used to see when controller is busy */ #define CTL_ACCEPTING 0x80 /* bit pattern FDC gives when idle */ #define MOTOR_MASK 0xF0 /* these bits control the motors in DOR */ #define ENABLE_INT 0x0C /* used for setting DOR port */ #define ST0_BITS 0xF8 /* check top 5 bits of seek status */ #define ST3_FAULT 0x80 /* if this bit is set, drive is sick */ #define ST3_WR_PROTECT 0x40 /* set when diskette is write protected */ #define ST3_READY 0x20 /* set when drive is ready */ #define TRANS_ST0 0x00 /* top 5 bits of ST0 for READ/WRITE */ #define SEEK_ST0 0x20 /* top 5 bits of ST0 for SEEK */ #define BAD_SECTOR 0x05 /* if these bits are set in ST1, recalibrate */ #define BAD_CYL 0x1F /* if any of these bits are set, recalibrate */ #define WRITE_PROTECT 0x02 /* bit is set if diskette is write protected */ #define CHANGE 0xC0 /* value returned by FDC after reset */ /* Floppy disk controller command bytes. */ #define FDC_SEEK 0x0F /* command the drive to seek */ #define FDC_READ 0xE6 /* command the drive to read */ #define FDC_WRITE 0xC5 /* command the drive to write */ #define FDC_SENSE 0x08 /* command the controller to tell its status */ #define FDC_RECALIBRATE 0x07 /* command the drive to go to cyl 0 */ #define FDC_SPECIFY 0x03 /* command the drive to accept params */ /* DMA channel commands. */ #define DMA_READ 0x46 /* DMA read opcode */ #define DMA_WRITE 0x4A /* DMA write opcode */ /* Parameters for the disk drive. */ #define SECTOR_SIZE 512 /* physical sector size in bytes */ #define HC_SIZE 2400 /* # sectors on a high-capacity (1.2M) disk */ #define NR_HEADS 0x02 /* two heads (i.e., two tracks/cylinder) */ #define DTL 0xFF /* determines data length (sector size) */ #define SPEC1 0xDF /* first parameter to SPECIFY */ #define SPEC2 0x02 /* second parameter to SPECIFY */ #define MOTOR_OFF 3*HZ /* how long to wait before stopping motor */ /* Error codes */ #define ERR_SEEK -1 /* bad seek */ #define ERR_TRANSFER -2 /* bad transfer */ #define ERR_STATUS -3 /* something wrong when getting status */ #define ERR_RECALIBRATE -4 /* recalibrate didn't work properly */ #define ERR_WR_PROTECT -5 /* diskette is write protected */ #define ERR_DRIVE -6 /* something wrong with a drive */ /* Miscellaneous. */ #define MOTOR_RUNNING 0xFF /* message type for clock interrupt */ #define MAX_ERRORS 20 /* how often to try rd/wt before quitting */ #define MAX_RESULTS 8 /* max number of bytes controller returns */ #define NR_DRIVES 2 /* maximum number of drives */ #define DIVISOR 128 /* used for sector size encoding */ #define MAX_FDC_RETRY 100 /* max # times to try to output to FDC */ #define NT 6 /* number of diskette/drive combinations */ /* Variables. */ PRIVATE struct floppy { /* main drive struct, one entry per drive */ int fl_opcode; /* DISK_READ or DISK_WRITE */ int fl_curcyl; /* current cylinder */ int fl_procnr; /* which proc wanted this operation? */ int fl_drive; /* drive number addressed */ int fl_cylinder; /* cylinder number addressed */ int fl_sector; /* sector addressed */ int fl_head; /* head number addressed */ int fl_count; /* byte count */ vir_bytes fl_address; /* user virtual address */ char fl_results[MAX_RESULTS]; /* the controller can give lots of output */ char fl_calibration; /* CALIBRATED or UNCALIBRATED */ char fl_density; /* 0 = 360K/360K; 1 = 360K/1.2M; 2= 1.2M/1.2M */ } floppy[NR_DRIVES]; #define UNCALIBRATED 0 /* drive needs to be calibrated at next use */ #define CALIBRATED 1 /* no calibration needed */ PRIVATE int motor_status; /* current motor status is in 4 high bits */ PRIVATE int motor_goal; /* desired motor status is in 4 high bits */ PRIVATE int prev_motor; /* which motor was started last */ PRIVATE int need_reset; /* set to 1 when controller must be reset */ PRIVATE int initialized; /* set to 1 after first successful transfer */ PRIVATE int d; /* diskette/drive combination */ PRIVATE message mess; /* message buffer for in and out */ PRIVATE char len[] = {-1,0,1,-1,2,-1,-1,3,-1,-1,-1,-1,-1,-1,-1,4}; PRIVATE char interleave[] = {1,2,3,4,5,6,7,8,9,10,11,12,13,14,15}; /* Six combinations of diskette/drive are supported: * # Drive diskette Sectors Tracks Rotation Data-rate Comment * 0 360K 360K 9 40 300 RPM 250 kbps Standard PC DSDD * 1 1.2M 1.2M 15 80 360 RPM 500 kbps AT disk in AT drive * 2 720K 360K 9 40 300 RPM 250 kbps Quad density PC * 3 720K 720K 9 80 300 RPM 250 kbps Toshiba, et al. * 4 1.2M 360K 9 40 360 RPM 300 kbps PC disk in AT drive * 5 1.2M 720K 9 80 360 RPM 300 kbps Toshiba in AT drive */ PRIVATE int gap[NT] = {0x2A, 0x1B, 0x2A, 0x2A, 0x23, 0x23}; /* gap size */ PRIVATE int rate[NT] = {0x02, 0x00, 0x02, 0x02, 0x01, 0x01}; /* 250,300,500 kbps*/ PRIVATE int nr_sectors[NT] = {9, 15, 9, 9, 9, 9}; /* sectors/track */ PRIVATE int nr_blocks[NT] = {720, 2400, 720, 1440, 720, 1440}; /* sectors/diskette*/ PRIVATE int steps_per_cyl[NT] = {1, 1, 2, 1, 2, 1}; /* 2 = dbl step */ PRIVATE int mtr_setup[NT] = {HZ/4,3*HZ/4,HZ/4,HZ/4,3*HZ/4,3*HZ/4};/* in ticks */ /*===========================================================================* * floppy_task * *===================================================================&()*+,-./0123456789========*/ PUBLIC floppy_task() { /* Main program of the floppy disk driver task. */ int r, caller, proc_nr; /* Here is the main loop of the disk task. It waits for a message, carries * it out, and sends a reply. */ while (TRUE) { /* First wait for a request to read or write a disk block. */ receive(ANY, &mess); /* get a request to do some work */ if (mess.m_source < 0) panic("disk task got message from ", mess.m_source); caller = mess.m_source; proc_nr = mess.PROC_NR; /* Now carry out the work. */ switch(mess.m_type) { case DISK_READ: r = do_rdwt(&mess); break; case DISK_WRITE: r = do_rdwt(&mess); break; default: r = EINVAL; break; } /* Finally, prepare and send the reply message. */ mess.m_type = TASK_REPLY; mess.REP_PROC_NR = proc_nr; mess.REP_STATUS = r; /* # of bytes transferred or error code */ send(caller, &mess); /* send reply to caller */ } } /*===========================================================================* * do_rdwt * *===========================================================================*/ PRIVATE int do_rdwt(m_ptr) message *m_ptr; /* pointer to read or write message */ { /* Carry out a read or write request from the disk. */ register struct floppy *fp; int r, drive, errors, stop_motor(); long block; /* Decode the message parameters. */ drive = m_ptr->DEVICE; if (drive < 0 || drive >= NR_DRIVES) return(EIO); fp = &floppy[drive]; /* 'fp' points to entry for this drive */ fp->fl_drive = drive; /* save drive number explicitly */ fp->fl_opcode = m_ptr->m_type; /* DISK_READ or DISK_WRITE */ if (m_ptr->POSITION % BLOCK_SIZE != 0) return(EINVAL); block = m_ptr->POSITION/SECTOR_SIZE; if (block >= HC_SIZE) return(EOF); /* sector is beyond end of 1.2M disk */ d = fp->fl_density; /* diskette/drive combination */ fp->fl_cylinder = (int) (block / (NR_HEADS * nr_sectors[d])); fp->fl_sector = (int) interleave[block % nr_sectors[d]]; fp->fl_head = (int) (block % (NR_HEADS*nr_sectors[d]) )/nr_sectors[d]; fp->fl_count = m_ptr->COUNT; fp->fl_address = (vir_bytes) m_ptr->ADDRESS; fp->fl_procnr = m_ptr->PROC_NR; if (fp->fl_count != BLOCK_SIZE) return(EINVAL); errors = 0; /* This loop allows a failed operation to be repeated. */ while (errors <= MAX_ERRORS) { /* If a lot of errors occur when 'initialized' is 0, it probably * means that we are trying at the wrong density. Try another one. * Increment 'errors' here since loop is aborted on error. */ errors++; /* increment count once per loop cycle */ if (errors % (MAX_ERRORS/NT) == 0) { d = (d + 1) % NT; /* try next density */ fp->fl_density = d; need_reset = 1; } if (block >= nr_blocks[d]) continue; /* First check to see if a reset is needed. */ if (need_reset) reset(); /* Now set up the DMA chip. */ dma_setup(fp); /* See if motor is running; if not, turn it on and wait */ start_motor(fp); /* If we are going to a new cylinder, perform a seek. */ r = seek(fp); if (r != OK) continue; /* if error, try again */ /* Perform the transfer. */ r = transfer(fp); if (r == OK) break; /* if successful, exit loop */ if (r == ERR_WR_PROTECT) break; /* retries won't help */ } /* Start watch_dog timer to turn motor off in a few seconds */ motor_goal = ENABLE_INT; /* when timer goes off, kill all motors */ clock_mess(MOTOR_OFF, stop_motor); if (r == OK && fp->fl_cylinder > 0) initialized = 1; /* seek works */ return(r == OK ? BLOCK_SIZE : EIO); } /*===========================================================================* * dma_setup * *===========================================================================*/ PRIVATE dma_setup(fp) struct floppy *fp; /* pointer to the drive struct */ { /* The IBM PC can perform DMA operations by using the DMA chip. To use it, * the DMA (Direct Memory Access) chip is loaded with the 20-bit memory address * to be read from or written to, the byte count minus 1, and a read or write * opcode. This routine sets up the DMA chip. Note that the chip is not * capable of doing a DMA across a 64K boundary (e.g., you can't read a * 512-byte block starting at physical address 65520). */ int mode, low_addr, high_addr, top_addr, low_ct, high_ct, top_end; vir_bytes vir, ct; phys_bytes user_phys; extern phys_bytes umap(); mode = (fp->fl_opcode == DISK_READ ? DMA_READ : DMA_WRITE); vir = (vir_bytes) fp->fl_address; ct = (vir_bytes) fp->fl_count; user_phys = umap(proc_addr(fp->fl_procnr), D, vir, ct); low_addr = (int) (user_phys >> 0) & BYTE; high_addr = (int) (user_phys >> 8) & BYTE; top_addr = (int) (user_phys >> 16) & BYTE; low_ct = (int) ( (ct - 1) >> 0) & BYTE; high_ct = (int) ( (ct - 1) >> 8) & BYTE; /* Check to see if the transfer will require the DMA address counter to * go from one 64K segment to another. If so, do not even start it, since * the hardware does not carry from bit 15 to bit 16 of the DMA address. * Also check for bad buffer address. These errors mean FS contains a bug. */ if (user_phys == 0) panic("FS gave floppy disk driver bad addr", (int) vir); top_end = (int) (((user_phys + ct - 1) >> 16) & BYTE); if (top_end != top_addr) panic("Trying to DMA across 64K boundary", top_addr); /* Now set up the DMA registers. */ lock(); port_out(DMA_M2, mode); /* set the DMA mode */ port_out(DMA_M1, mode); /* set it again */ port_out(DMA_ADDR, low_addr); /* output low-order 8 bits */ port_out(DMA_ADDR, high_addr);/* output next 8 bits */ port_out(DMA_TOP, top_addr); /* output highest 4 bits */ port_out(DMA_COUNT, low_ct); /* output low 8 bits of count - 1 */ port_out(DMA_COUNT, high_ct); /* output high 8 bits of count - 1 */ unlock(); port_out(DMA_INIT, 2); /* initialize DMA */ } /*===========================================================================* * start_motor * *===========================================================================*/ PRIVATE start_motor(fp) struct floppy *fp; /* pointer to the drive struct */ { /* Control of the floppy disk motors is a big pain. If a motor is off, you * have to turn it on first, which takes 1/2 second. You can't leave it on * all the time, since that would wear out the diskette. However, if you turn * the motor off after each operation, the system performance will be awful. * The compromise used here is to leave it on for a few seconds after each * operation. If a new operation is started in that interval, it need not be * turned on again. If no new operation is started, a timer goes off and the * motor is turned off. I/O port DOR has bits to control each of 4 drives. * Interrupts must be disabled temporarily to prevent clock interrupt from * turning off motors while we are testing the bits. */ int motor_bit, running, send_mess(); lock(); /* no interrupts while checking out motor */ motor_bit = 1 << (fp->fl_drive + 4); /* bit mask for this drive */ motor_goal = motor_bit | ENABLE_INT | fp->fl_drive; if (motor_status & prev_motor) motor_goal |= prev_motor; running = motor_status & motor_bit; /* nonzero if this motor is running */ port_out(DOR, motor_goal); motor_status = motor_goal; prev_motor = motor_bit; /* record motor started for next time */ unlock(); /* If the motor was already running, we don't have to wait for it. */ if (running) return; /* motor was already running */ clock_mess(mtr_setup[d], send_mess); /* motor was not running */ receive(CLOCK, &mess); /* wait for clock interrupt */ } /*===========================================================================* * stop_motor * *===========================================================================*/ PRIVATE stop_motor() { /* This routine is called by the clock interrupt after several seconds have * elapsed with no floppy disk activity. It checks to see if any drives are * supposed to be turned off, and if so, turns them off. */ if ( (motor_goal & MOTOR_MASK) != (motor_status & MOTOR_MASK) ) { port_out(DOR, motor_goal); motor_status = motor_goal; } } /*===========================================================================* * seek * *===========================================================================*/ PRIVATE int seek(fp) struct floppy *fp; /* pointer to the drive struct */ { /* Issue a SEEK command on the indicated drive unless the arm is already * positioned on the correct cylinder. */ int r; /* Are we already on the correct cylinder? */ if (fp->fl_calibration == UNCALIBRATED) if (recalibrate(fp) != OK) return(ERR_SEEK); if (fp->fl_curcyl == fp->fl_cylinder) return(OK); /* No. Wrong cylinder. Issue a SEEK and wait for interrupt. */ fdc_out(FDC_SEEK); /* start issuing the SEEK command */ fdc_out( (fp->fl_head << 2) | fp->fl_drive); fdc_out(fp->fl_cylinder * steps_per_cyl[d]); if (need_reset) return(ERR_SEEK); /* if controller is sick, abort seek */ receive(HARDWARE, &mess); /* Interrupt has been received. Check drive status. */ fdc_out(FDC_SENSE); /* probe FDC to make it return status */ r = fdc_results(fp); /* get controller status bytes */ if ( (fp->fl_results[ST0] & ST0_BITS) != SEEK_ST0) r = ERR_SEEK; if (fp->fl_results[ST1] != fp->fl_cylinder * steps_per_cyl[d]) r = ERR_SEEK; if (r != OK) if (recalibrate(fp) != OK) return(ERR_SEEK); return(r); } /*===========================================================================* * transfer * *===========================================================================*/ PRIVATE int transfer(fp) register struct floppy *fp; /* pointer to the drive struct */ { /* The drive is now on the proper cylinder. Read or write 1 block. */ int r, s, op; extern int olivetti; /* Never attempt a transfer if the drive is uncalibrated or motor is off. */ if (fp->fl_calibration == UNCALIBRATED) return(ERR_TRANSFER); if ( ( (motor_status>>(fp->fl_drive+4)) & 1) == 0) return(ERR_TRANSFER); /* The PC-AT requires the date rate to be set to 250 or 500 kbps */ if (pc_at) port_out(FDC_RATE, rate[d]); /* The command is issued by outputing 9 bytes to the controller chip. */ op = (fp->fl_opcode == DISK_READ ? FDC_READ : FDC_WRITE); fdc_out(op); /* issue the read or write command */ fdc_out( (fp->fl_head << 2) | fp->fl_drive); fdc_out(fp->fl_cylinder); /* tell controller which cylinder */ fdc_out(fp->fl_head); /* tell controller which head */ fdc_out(fp->fl_sector); /* tell controller which sector */ fdc_out( (int) len[SECTOR_SIZE/DIVISOR]); /* sector size */ fdc_out(nr_sectors[d]); /* tell controller how big a track is */ fdc_out(gap[d]); /* tell controller how big sector gap is */ fdc_out(DTL); /* tell controller about data length */ /* Block, waiting for disk interrupt. */ if (need_reset) return(ERR_TRANSFER); /* if controller is sick, abort op */ receive(HARDWARE, &mess); /* Get controller status and check for errors. */ r = fdc_results(fp); if (r != OK) return(r); if ( (fp->fl_results[ST1] & BAD_SECTOR) || (fp->fl_results[ST2] & BAD_CYL) ) fp->fl_calibration = UNCALIBRATED; if (fp->fl_results[ST1] & WRITE_PROTECT) { printf("Diskette in drive %d is write protected.\n", fp->fl_drive); return(ERR_WR_PROTECT); } if ((fp->fl_results[ST0] & ST0_BITS) != TRANS_ST0) return(ERR_TRANSFER); if (fp->fl_results[ST1] | fp->fl_results[ST2]) return(ERR_TRANSFER); /* Compare actual numbers of sectors transferred with expected number. */ s = (fp->fl_results[ST_CYL] - fp->fl_cylinder) * NR_HEADS * nr_sectors[d]; s += (fp->fl_results[ST_HEAD] - fp->fl_head) * nr_sectors[d]; s += (fp->fl_results[ST_SEC] - fp->fl_sector); if (s * SECTOR_SIZE != fp->fl_count) return(ERR_TRANSFER); return(OK); } /*===========================================================================* * fdc_results * *===========================================================================*/ PRIVATE int fdc_results(fp) register struct floppy *fp; /* pointer to the drive struct */ { /* Extract results from the controller after an operation. */ int i, j, status, ready; /* Loop, extracting bytes from FDC until it says it has no more. */ for (i = 0; i < MAX_RESULTS; i++) { ready = FALSE; for (j = 0; j < MAX_FDC_RETRY; j++) { port_in(FDC_STATUS, &status); if (status & MASTER) { ready = TRUE; break; } } if (ready == FALSE) return(ERR_STATUS); /* time out */ if ((status & CTL_BUSY) == 0) return(OK); if ((status & DIRECTION) == 0) return(ERR_STATUS); port_in(FDC_DATA, &status); fp->fl_results[i] = status & BYTE; } /* FDC is giving back too many results. */ need_reset = TRUE; /* controller chip must be reset */ return(ERR_STATUS); } /*===========================================================================* * fdc_out * *===========================================================================*/ PRIVATE fdc_out(val) int val; /* write this byte to floppy disk controller */ { /* Output a byte to the controller. This is not entirely trivial, since you * can only write to it when it is listening, and it decides when to listen. * If the controller refuses to listen, the FDC chip is given a hard reset. */ int retries, r; if (need_reset) return; /* if controller is not listening, return */ retries = MAX_FDC_RETRY; /* It may take several tries to get the FDC to accept a command. */ while (retries-- > 0) { port_in(FDC_STATUS, &r); r &= (MASTER | DIRECTION); /* just look at bits 2 and 3 */ if (r != CTL_ACCEPTING) continue; /* FDC is not listening */ port_out(FDC_DATA, val); return; } /* Controller is not listening. Hit it over the head with a hammer. */ need_reset = TRUE; } /*===========================================================================* * recalibrate * *===========================================================================*/ PRIVATE int recalibrate(fp) register struct floppy *fp; /* pointer tot he drive struct */ { /* The floppy disk controller has no way of determining its absolute arm * position (cylinder). Instead, it steps the arm a cylinder at a time and * keeps track of where it thinks it is (in software). However, after a * SEEK, the hardware reads information from the diskette telling where the * arm actually is. If the arm is in the wrong place, a recalibration is done, * which forces the arm to cylinder 0. This way the controller can get back * into sync with reality. */ int r; /* Issue the RECALIBRATE command and wait for the interrupt. */ start_motor(fp); /* can't recalibrate with motor off */ fdc_out(FDC_RECALIBRATE); /* tell drive to recalibrate itself */ fdc_out(fp->fl_drive); /* specify drive */ if (need_reset) return(ERR_SEEK); /* don't wait if controller is sick */ receive(HARDWARE, &mess); /* wait for interrupt message */ /* Determine if the recalibration succeeded. */ fdc_out(FDC_SENSE); /* issue SENSE command to see where we are */ r = fdc_results(fp); /* get results of the SENSE command */ fp->fl_curcyl = -1; /* force a SEEK next time */ if (r != OK || /* controller would not respond */ (fp->fl_results[ST0]&ST0_BITS) != SEEK_ST0 || fp->fl_results[ST_PCN] !=0){ /* Recalibration failed. FDC must be reset. */ need_reset = TRUE; fp->fl_calibration = UNCALIBRATED; return(ERR_RECALIBRATE); } else { /* Recalibration succeeded. */ fp->fl_calibration = CALIBRATED; return(OK); } } /*===========================================================================* * reset * *===========================================================================*/ PRIVATE reset() { /* Issue a reset to the controller. This is done after any catastrophe, * like the controller refusing to respond. */ int i, r, status; register struct floppy *fp; /* Disable interrupts and strobe reset bit low. */ need_reset = FALSE; lock(); motor_status = 0; motor_goal = 0; port_out(DOR, 0); /* strobe reset bit low */ port_out(DOR, ENABLE_INT); /* strobe it high again */ unlock(); /* interrupts allowed again */ receive(HARDWARE, &mess); /* collect the RESET interrupt */ /* Interrupt from the reset has been received. Continue resetting. */ fp = &floppy[0]; /* use floppy[0] for scratch */ fp->fl_results[0] = 0; /* this byte will be checked shortly */ fdc_out(FDC_SENSE); /* did it work? */ r = fdc_results(fp); /* get results */ status = fp->fl_results[0] & BYTE; /* Tell FDC drive parameters. */ fdc_out(FDC_SPECIFY); /* specify some timing parameters */ fdc_out(SPEC1); /* step-rate and head-unload-time */ fdc_out(SPEC2); /* head-load-time and non-dma */ for (i = 0; i < NR_DRIVES; i++) floppy[i].fl_calibration = UNCALIBRATED; } /*===========================================================================* * clock_mess * *===========================================================================*/ PRIVATE clock_mess(ticks, func) int ticks; /* how many clock ticks to wait */ int (*func)(); /* function to call upon time out */ { /* Send the clock task a message. */ mess.m_type = SET_ALARM; mess.CLOCK_PROC_NR = FLOPPY; mess.DELTA_TICKS = ticks; mess.FUNC_TO_CALL = func; sendrec(CLOCK, &mess); } /*===========================================================================* * send_mess * *===========================================================================*/ PRIVATE send_mess() { /* This routine is called when the clock task has timed out on motor startup.*/ mess.m_type = MOTOR_RUNNING; send(FLOPPY, &mess); } /* This file contains the main program of MINIX. The routine main() * initializes the system and starts the ball rolling by setting up the proc * table, interrupt vectors, and scheduling each task to run to initialize * itself. * * The entries into this file are: * main: MINIX main program * unexpected_int: called when an interrupt to an unused vector < 16 occurs * trap: called when an unexpected trap to a vector >= 16 occurs * panic: abort MINIX due to a fatal error */ #include "../h/const.h" #include "../h/type.h" #include "../h/callnr.h" #include "../h/com.h" #include "../h/error.h" #include "const.h" #include "type.h" #include "glo.h" #include "proc.h" #define SAFETY 8 /* margin of safety for stack overflow (ints)*/ #define VERY_BIG 39328 /* must be bigger than kernel size (clicks) */ #define BASE 1536 /* address where MINIX starts in memory */ #define SIZES 8 /* sizes array has 8 entries */ #define CPU_TY1 0xFFFF /* BIOS segment that tells CPU type */ #define CPU_TY2 0x000E /* BIOS offset that tells CPU type */ #define PC_AT 0xFC /* IBM code for PC-AT (in BIOS at 0xFFFFE) */ /*===========================================================================* * main * *===========================================================================*/ PUBLIC main() { /* Start the ball rolling. */ register struct proc *rp; register int t; vir_clicks size; phys_clicks base_click, mm_base, previous_base; phys_bytes phys_b; extern unsigned sizes[8]; /* table filled in by build */ extern int color, vec_table[], get_chrome(), (*task[])(); extern int s_call(), disk_int(), tty_int(), clock_int(), disk_int(); extern int wini_int(), lpr_int(), surprise(), trp(), divide(); extern phys_bytes umap(); /* Set up proc table entry for user processes. Be very careful about * sp, since the 3 words prior to it will be clobbered when the kernel pushes * pc, cs, and psw onto the USER's stack when starting the user the first * time. This means that with initial sp = 0x10, user programs must leave * the words at 0x000A, 0x000C, and 0x000E free. */ lock(); /* we can't handle interrupts yet */ base_click = BASE >> CLICK_SHIFT; size = sizes[0] + sizes[1]; /* kernel text + data size in clicks */ mm_base = base_click + size; /* place where MM starts (in clicks) */ for (rp = &proc[0]; rp <= &proc[NR_TASKS+LOW_USER]; rp++) { for (t=0; t< NR_REGS; t++) rp->p_reg[t] = 0100*t; /* debugging */ t = rp - proc - NR_TASKS; /* task number */ rp->p_sp = (rp < &proc[NR_TASKS] ? t_stack[NR_TASKS+t+1].stk : INIT_SP); rp->p_splimit = rp->p_sp; if (rp->p_splimit != INIT_SP) rp->p_splimit -= (TASK_STACK_BYTES - SAFETY)/sizeof(int); rp->p_pcpsw.pc = task[t + NR_TASKS]; if (rp->p_pcpsw.pc != 0 || t >= 0) ready(rp); rp->p_pcpsw.psw = INIT_PSW; rp->p_flags = 0; /* Set up memory map for tasks and MM, FS, INIT. */ if (t < 0) { /* I/O tasks. */ rp->p_map[T].mem_len = VERY_BIG; rp->p_map[T].mem_phys = base_click; rp->p_map[D].mem_len = VERY_BIG; rp->p_map[D].mem_phys = base_click + sizes[0]; rp->p_map[S].mem_len = VERY_BIG; rp->p_map[S].mem_phys = base_click + sizes[0] + sizes[1]; rp->p_map[S].mem_vir = sizes[0] + sizes[1]; } else { /* MM, FS, and INIT. */ previous_base = proc[NR_TASKS + t - 1].p_map[S].mem_phys; rp->p_map[T].mem_len = sizes[2*t + 2]; rp->p_map[T].mem_phys = (t == 0 ? mm_base : previous_base); rp->p_map[D].mem_len = sizes[2*t + 3]; rp->p_map[D].mem_phys = rp->p_map[T].mem_phys + sizes[2*t + 2]; rp->p_map[S].mem_vir = sizes[2*t + 3]; rp->p_map[S].mem_phys = rp->p_map[D].mem_phys + sizes[2*t + 3]; } #ifdef i8088 rp->p_reg[CS_REG] = rp->p_map[T].mem_phys; rp->p_reg[DS_REG] = rp->p_map[D].mem_phys; rp->p_reg[SS_REG] = rp->p_map[D].mem_phys; rp->p_reg[ES_REG] = rp->p_map[D].mem_phys; #endif } proc[NR_TASKS+(HARDWARE)].p_sp = (int *) k_stack; proc[NR_TASKS+(HARDWARE)].p_sp += K_STACK_BYTES/2; proc[NR_TASKS+(HARDWARE)].p_splimit = (int *) k_stack; proc[NR_TASKS+(HARDWARE)].p_splimit += SAFETY/2; for (rp = proc_addr(LOW_USER+1); rp < proc_addr(NR_PROCS); rp++) rp->p_flags = P_SLOT_FREE; /* Determine if display is color or monochrome and CPU type (from BIOS). */ color = get_chrome(); /* 0 = mono, 1 = color */ t = get_byte(CPU_TY1, CPU_TY2); /* is this PC, XT, AT ... ? */ if (t == PC_AT) pc_at = TRUE; /* Save the old interrupt vectors. */ phys_b = umap(proc_addr(HARDWARE), D, (vir_bytes) vec_table, VECTOR_BYTES); phys_copy(0L, phys_b, (long) VECTOR_BYTES); /* save all the vectors */ /* Set up the new interrupt vectors. */ for (t = 0; t < 16; t++) set_vec(t, surprise, base_click); for (t = 16; t < 256; t++) set_vec(t, trp, base_click); set_vec(DIVIDE_VECTOR, divide, base_click); set_vec(SYS_VECTOR, s_call, base_click); set_vec(CLOCK_VECTOR, clock_int, base_click); set_vec(KEYBOARD_VECTOR, tty_int, base_click); set_vec(FLOPPY_VECTOR, disk_int, base_click); set_vec(PRINTER_VECTOR, lpr_int, base_click); if (pc_at) set_vec(AT_WINI_VECTOR, wini_int, base_click); else set_vec(XT_WINI_VECTOR, wini_int, base_click); /* Put a ptr to proc table in a known place so it can be found in /dev/mem */ set_vec( (BASE - 4)/4, proc, (phys_clicks) 0); bill_ptr = proc_addr(HARDWARE); /* it has to point somewhere */ pick_proc(); /* Now go to the assembly code to start running the current process. */ port_out(INT_CTLMASK, 0); /* do not mask out any interrupts in 8259A */ port_out(INT2_MASK, 0); /* same for second interrupt controller */ restart(); } /*===========================================================================* * unexpected_int * *===========================================================================*/ PUBLIC unexpected_int() { /* A trap or interrupt has occurred that was not expected. */ printf("Unexpected trap: vector < 16\n"); printf("pc = 0x%x text+data+bss = 0x%x\n",proc_ptr->p_pcpsw.pc, proc_ptr->p_map[D].mem_len<<4); } /*===========================================================================* * trap * *===========================================================================*/ PUBLIC trap() { /* A trap (vector >= 16) has occurred. It was not expected. */ printf("\nUnexpected trap: vector >= 16 "); printf("This may be due to accidentally including\n"); printf("a non-MINIX library routine that is trying to make a system call.\n"); printf("pc = 0x%x text+data+bss = 0x%x\n",proc_ptr->p_pcpsw.pc, proc_ptr->p_map[D].mem_len<<4); } /*===========================================================================* * div_trap * *===========================================================================*/ PUBLIC div_trap() { /* The divide intruction traps to vector 0 upon overflow. */ printf("Trap to vector 0: divide overflow. "); printf("pc = 0x%x text+data+bss = 0x%x\n",proc_ptr->p_pcpsw.pc, proc_ptr->p_map[D].mem_len<<4); } /*===========================================================================* * panic * *===========================================================================*/ PUBLIC panic(s,n) char *s; int n; { /* The system has run aground of a fatal error. Terminate execution. * If the panic originated in MM or FS, the string will be empty and the * file system already syncked. If the panic originates in the kernel, we are * kind of stuck. */ if (*s != 0) { printf("\nKernel panic: %s",s); if (n != NO_NUM) printf(" %d", n); printf("\n"); } printf("\nType space to reboot\n"); wreboot(); } #ifdef i8088 /*===========================================================================* * set_vec * *=========================AC==================================================*/ PRIVATE set_vec(vec_nr, addr, base_click) int vec_nr; /* which vector */ int (*addr)(); /* where to start */ phys_clicks base_click; /* click where kernel sits in memory */ { /* Set up an interrupt vector. */ unsigned vec[2]; unsigned u; phys_bytes phys_b; extern unsigned sizes[8]; /* Build the vector in the array 'vec'. */ vec[0] = (unsigned) addr; vec[1] = (unsigned) base_click; u = (unsigned) vec; /* Copy the vector into place. */ phys_b = ( (phys_bytes) base_click + (phys_bytes) sizes[0]) << CLICK_SHIFT; phys_b += u; phys_copy(phys_b, (phys_bytes) 4*vec_nr, (phys_bytes) 4); } #endif /* This file contains the drivers for four special files: * /dev/null - null device (data sink) * /dev/mem - absolute memory * /dev/kmem - kernel virtual memory * /dev/ram - RAM disk * It accepts three messages, for reading, for writing, and for * control. All use message format m2 and with these parameters: * * m_type DEVICE PROC_NR COUNT POSITION ADRRESS * ---------------------------------------------------------------- * | DISK_READ | device | proc nr | bytes | offset | buf ptr | * |------------+---------+---------+---------+---------+---------| * | DISK_WRITE | device | proc nr | bytes | offset | buf ptr | * |------------+---------+---------+---------+---------+---------| * | DISK_IOCTL | device | | blocks | ram org | | * ---------------------------------------------------------------- * * * The file contains one entry point: * * mem_task: main entry when system is brought up * */ #include "../h/const.h" #include "../h/type.h" #include "../h/callnr.h" #include "../h/com.h" #include "../h/error.h" #include "const.h" #include "type.h" #include "proc.h" #define NR_RAMS 4 /* number of RAM-type devices */ PRIVATE message mess; /* message buffer */ PRIVATE phys_bytes ram_origin[NR_RAMS]; /* origin of each RAM disk */ PRIVATE phys_bytes ram_limit[NR_RAMS]; /* limit of RAM disk per minor dev. */ /*===========================================================================* * mem_task * *===========================================================================*/ PUBLIC mem_task() { /* Main program of the disk driver task. */ int r, caller, proc_nr; extern unsigned sizes[8]; extern phys_clicks get_base(); /* Initialize this task. */ ram_origin[KMEM_DEV] = (phys_bytes) get_base() << CLICK_SHIFT; ram_limit[KMEM_DEV] = (sizes[0] + sizes[1]) << CLICK_SHIFT; ram_limit[MEM_DEV] = MEM_BYTES; /* Here is the main loop of the memory task. It waits for a message, carries * it out, and sends a reply. */ while (TRUE) { /* First wait for a request to read or write. */ receive(ANY, &mess); if (mess.m_source < 0) panic("mem task got message from ", mess.m_source); caller = mess.m_source; proc_nr = mess.PROC_NR; /* Now carry out the work. It depends on the opcode. */ switch(mess.m_type) { case DISK_READ: r = do_mem(&mess); break; case DISK_WRITE: r = do_mem(&mess); break; case DISK_IOCTL: r = do_setup(&mess); break; default: r = EINVAL; break; } /* Finally, prepare and send the reply message. */ mess.m_type = TASK_REPLY; mess.REP_PROC_NR = proc_nr; mess.REP_STATUS = r; send(caller, &mess); } } /*===========================================================================* * do_mem * *===========================================================================*/ PRIVATE int do_mem(m_ptr) register message *m_ptr; /* pointer to read or write message */ { /* Read or write /dev/null, /dev/mem, /dev/kmem, or /dev/ram. */ int device, count; phys_bytes mem_phys, user_phys; struct proc *rp; extern phys_clicks get_base(); extern phys_bytes umap(); /* Get minor device number and check for /dev/null. */ device = m_ptr->DEVICE; if (device < 0 || device >= NR_RAMS) return(ENXIO); /* bad minor device */ if (device==NULL_DEV) return(m_ptr->m_type == DISK_READ ? EOF : m_ptr->COUNT); /* Set up 'mem_phys' for /dev/mem, /dev/kmem, or /dev/ram. */ if (m_ptr->POSITION < 0) return(ENXIO); mem_phys = ram_origin[device] + m_ptr->POSITION; if (mem_phys >= ram_limit[device]) return(EOF); count = m_ptr->COUNT; if(mem_phys + count > ram_limit[device]) count = ram_limit[device] - mem_phys; /* Determine address where data is to go or to come from. */ rp = proc_addr(m_ptr->PROC_NR); user_phys = umap(rp, D, (vir_bytes) m_ptr->ADDRESS, (vir_bytes) count); if (user_phys == 0) return(E_BAD_ADDR); /* Copy the data. */ if (m_ptr->m_type == DISK_READ) phys_copy(mem_phys, user_phys, (long) count); else phys_copy(user_phys, mem_phys, (long) count); return(count); } /*===========================================================================* * do_setup * *===========================================================================*/ PRIVATE int do_setup(m_ptr) message *m_ptr; /* pointer to read or write message */ { /* Set parameters for one of the disk RAMs. */ int device; device = m_ptr->DEVICE; if (device < 0 || device >= NR_RAMS) return(ENXIO); /* bad minor device */ ram_origin[device] = m_ptr->POSITION; ram_limit[device] = m_ptr->POSITION + (long) m_ptr->COUNT * BLOCK_SIZE; return(OK); } /* This file contains the printer driver. It is a fairly simple driver, * supporting only one printer. Characters that are written to the driver * are written to the printer without any changes at all. * * The valid messages and their parameters are: * * TTY_O_DONE: output completed * TTY_WRITE: a process wants to write on a terminal * CANCEL: terminate a previous incomplete system call immediately * * m_type TTY_LINE PROC_NR COUNT ADDRESS * ------------------------------------------------------- * | TTY_O_DONE |minor dev| | | | * |-------------+---------+---------+---------+---------| * | TTY_WRITE |minor dev| proc nr | count | buf ptr | * |-------------+---------+---------+---------+---------| * | CANCEL |minor dev| proc nr | | | * ------------------------------------------------------- * * Note: since only 1 printer is supported, minor dev is not used at present. */ #include "../h/const.h" #include "../h/type.h" #include "../h/callnr.h" #include "../h/com.h" #include "../h/error.h" #include "const.h" #include "type.h" #include "glo.h" #include "proc.h" #define NORMAL_STATUS 0x90 /* printer gives this status when idle */ #define BUSY_STATUS 0x10 /* printer gives this status when busy */ #define ASSERT_STROBE 0x1D /* strobe a character to the interface */ #define NEGATE_STROBE 0x1C /* enable interrupt on interface */ #define SELECT 0x0C /* select printer bit */ #define INIT_PRINTER 0x08 /* init printer bits */ #define NO_PAPER 0x20 /* status bit saying that paper is up */ #define OFF_LINE 0x10 /* status bit saying that printer not online*/ #define PR_ERROR 0x08 /* something is wrong with the printer */ #define PR_COLOR_BASE 0x378 /* printer port when color display used */ #define PR_MONO_BASE 0x3BC /* printer port when mono display used */ #define LOW_FOUR 0xF /* mask for low-order 4 bits */ #define CANCELED -999 /* indicates that command has been killed */ #define DELAY_COUNT 100 /* regulates delay between characters */ #define DELAY_LOOP 1000 /* delay when printer is busy */ #define MAX_REP 1000 /* controls max delay when busy */ #define STATUS_MASK 0xB0 /* mask to filter out status bits */ PRIVATE int port_base; /* I/O port for printer: 0x 378 or 0x3BC */ PRIVATE int caller; /* process to tell when printing done (FS) */ PRIVATE int proc_nr; /* user requesting the printing */ PRIVATE int orig_count; /* original byte count */ PRIVATE int es; /* (es, offset) point to next character to */ PRIVATE int offset; /* print, i.e., in the user's buffer */ PUBLIC int pcount; /* number of bytes left to print */ PUBLIC int pr_busy; /* TRUE when printing, else FALSE */ PUBLIC int cum_count; /* cumulative # characters printed */ PUBLIC int prev_ct; /* value of cum_count 100 msec ago */ /*===========================================================================* * printer_task * *===========================================================================*/ PUBLIC printer_task() { /* Main routine of the printer task. */ message print_mess; /* buffer for all incoming messages */ print_init(); /* initialize */ while (TRUE) { receive(ANY, &print_mess); switch(print_mess.m_type) { case TTY_WRITE: do_write(&print_mess); break; case CANCEL : do_cancel(&print_mess); break; case TTY_O_DONE: do_done(&print_mess); break; default: break; } } } /*===========================================================================* * do_write * *===========================================================================*/ PRIVATE do_write(m_ptr) message *m_ptr; /* pointer to the newly arrived message */ { /* The printer is used by sending TTY_WRITE messages to it. Process one. */ int i, j, r, value; struct proc *rp; phys_bytes phys; extern phys_bytes umap(); r = OK; /* so far, no errors */ /* Reject command if printer is busy or count is not positive. */ if (pr_busy) r = EAGAIN; if (m_ptr->COUNT <= 0) r = EINVAL; /* Compute the physical address of the data buffer within user space. */ rp = proc_addr(m_ptr->PROC_NR); phys = umap(rp, D, (vir_bytes) m_ptr->ADDRESS, m_ptr->COUNT); if (phys == 0) r = E_BAD_ADDR; if (r == OK) { /* Save information needed later. */ lock(); /* no interrupts now please */ caller = m_ptr->m_source; proc_nr = m_ptr->PROC_NR; pcount = m_ptr->COUNT; orig_count = m_ptr->COUNT; es = (int) (phys >> CLICK_SHIFT); offset = (int) (phys & LOW_FOUR); /* Start the printer. */ for (i = 0; i < MAX_REP; i++) { port_in(port_base + 1, &value); if ((value&STATUS_MASK) == NORMAL_STATUS) { pr_busy = TRUE; pr_char(); /* print first character */ r = SUSPEND; /* tell FS to suspend user until done */ break; } else { if ((value&STATUS_MASK) == BUSY_STATUS) { for (j = 0; j m_source, m_ptr->PROC_NR, r); } /*===========================================================================* * do_done * *===========================================================================*/ PRIVATE do_done(m_ptr) message *m_ptr; /* pointer to the newly arrived message */ { /* Printing is finished. Reply to caller (FS). */ int status; status = (m_ptr->REP_STATUS == OK ? orig_count : EIO); if (proc_nr != CANCELED) { reply(REVIVE, caller, proc_nr, status); if (status == EIO) pr_error(m_ptr->REP_STATUS); } pr_busy = FALSE; } /*===========================================================================* * do_cancel * *===========================================================================*/ PRIVATE do_cancel(m_ptr) message *m_ptr; /* pointer to the newly arrived message */ { /* Cancel a print request that has already started. Usually this means that * the process doing the printing has been killed by a signal. */ if (pr_busy == FALSE) return; /* this statement avoids race conditions */ pr_busy = FALSE; /* mark printer as idle */ pcount = 0; /* causes printing to stop at next interrupt*/ proc_nr = CANCELED; /* marks process as canceled */ reply(TASK_REPLY, m_ptr->m_source, m_ptr->PROC_NR, EINTR); } /*===========================================================================* * reply * *===========================================================================*/ PRIVATE reply(code, replyee, process, status) int code; /* TASK_REPLY or REVIVE */ int replyee; /* destination for message (normally FS) */ int process; /* which user requested the printing */ int status; /* number of chars printed or error code */ { /* Send a reply telling FS that printing has started or stopped. */ message pr_mess; pr_mess.m_type = code; /* TASK_REPLY or REVIVE */ pr_mess.REP_STATUS = status; /* count or EIO */ pr_mess.REP_PROC_NR = process; /* which user does this pertain to */ send(replyee, &pr_mess); /* send the message */ } /*===========================================================================* * pr_error * *===========================================================================*/ PRIVATE pr_error(status) int status; /* printer status byte */ { /* The printer is not ready. Display a message on the console telling why. */ if (status & NO_PAPER) printf("Printer is out of paper\n"); if ((status & OFF_LINE) == 0) printf("Printer is not on line\n"); if ((status & PR_ERROR) == 0) printf("Printer error\n"); } /*===========================================================================* * print_init * *===========================================================================*/ PRIVATE print_init() { /* Color display uses 0x378 for printer; mono display uses 0x3BC. */ int i; extern int color; port_base = (color ? PR_COLOR_BASE : PR_MONO_BASE); pr_busy = FALSE; PRSport_out(port_base + 2, INIT_PRINTER); for (i = 0; i < DELAY_COUNT; i++) ; /* delay loop */ port_out(port_base + 2, SELECT); } /*===========================================================================* * pr_char * *===========================================================================*/ PUBLIC pr_char() { /* This is the interrupt handler. When a character has been printed, an * interrupt occurs, and the assembly code routine trapped to calls pr_char(). * One annoying problem is that the 8259A controller sometimes generates * spurious interrupts to vector 15, which is the printer vector. Ignore them. */ int value, ch, i; char c; extern char get_byte(); if (pcount != orig_count) port_out(INT_CTL, ENABLE); if (pr_busy == FALSE) return; /* spurious 8259A interrupt */ while (pcount > 0) { port_in(port_base + 1, &value); /* get printer status */ if ((value&STATUS_MASK) == NORMAL_STATUS) { /* Everything is all right. Output another character. */ c = get_byte(es, offset); /* fetch char from user buf */ ch = c & BYTE; port_out(port_base, ch); /* output character */ port_out(port_base + 2, ASSERT_STROBE); port_out(port_base + 2, NEGATE_STROBE); offset++; pcount--; cum_count++; /* count characters output */ for (i = 0; i < DELAY_COUNT; i++) ; /* delay loop */ } else if ((value&STATUS_MASK) == BUSY_STATUS) { return; /* printer is busy; wait for interrupt*/ } else { break; /* err: send message to printer task */ } } /* Count is 0 or an error occurred; send message to printer task. */ int_mess.m_type = TTY_O_DONE; int_mess.REP_STATUS = (pcount == 0 ? OK : value); interrupt(PRINTER, &int_mess); } /* This file contains essentially all of the process and message handling. * It has two main entry points from the outside: * * sys_call: called when a process or task does SEND, RECEIVE or SENDREC * interrupt: called by interrupt routines to send a message to task * * It also has five minor entry points: * * ready: put a process on one of the ready queues so it can be run * unready: remove a process from the ready queues * sched: a process has run too long; schedule another one * mini_send: send a message (used by interrupt signals, etc.) * pick_proc: pick a process to run (used by system initialization) */ #include "../h/const.h" #include "../h/type.h" #include "../h/callnr.h" #include "../h/com.h" #include "../h/error.h" #include "const.h" #include "type.h" #include "glo.h" #include "proc.h" /*===========================================================================* * interrupt * *===========================================================================*/ PUBLIC interrupt(task, m_ptr) int task; /* number of task to be started */ message *m_ptr; /* interrupt message to send to the task */ { /* An interrupt has occurred. Schedule the task that handles it. */ int i, n, old_map, this_bit; #ifdef i8088 /* Re-enable the 8259A interrupt controller. */ port_out(INT_CTL, ENABLE); /* this re-enables the 8259A controller chip */ if (pc_at && task == WINCHESTER) /* this re-enables the second controller chip */ port_out(INT2_CTL, ENABLE); #endif /* Try to send the interrupt message to the indicated task. */ this_bit = 1 << (-task); if (mini_send(HARDWARE, task, m_ptr) != OK) { /* The message could not be sent to the task; it was not waiting. */ old_map = busy_map; /* save original map of busy tasks */ if (task == CLOCK) { lost_ticks++; } else { busy_map |= this_bit; /* mark task as busy */ task_mess[-task] = m_ptr; /* record message pointer */ } } else { /* Hardware interrupt was successfully sent as a message. */ busy_map &= ~this_bit; /* turn off the bit in case it was on */ old_map = busy_map; } /* See if any tasks that were previously busy are now listening for msgs. */ if (old_map != 0) { for (i = 2; i <= NR_TASKS; i++) { /* Check each task looking for one with a pending interrupt. */ if ( (old_map>>i) & 1) { /* Task 'i' has a pending interrupt. */ n = mini_send(HARDWARE, -i, task_mess[i]); if (n == OK) busy_map &= ~(1 << i); } } } /* If a task has just been readied and a user is running, run the task. */ if (rdy_head[TASK_Q] != NIL_PROC && (cur_proc >= 0 || cur_proc == IDLE)) pick_proc(); } /*===========================================================================* * sys_call * *===========================================================================*/ PUBLIC sys_call(function, caller, src_dest, m_ptr) int function; /* SEND, RECEIVE, or BOTH */ int caller; /* who is making this call */ int src_dest; /* source to receive from or dest to send to */ message *m_ptr; /* pointer to message */ { /* The only system calls that exist in MINIX are sending and receiving * messages. These are done by trapping to the kernel with an INT instruction. * The trap is caught and sys_call() is called to send or receive a message (or * both). */ register struct proc *rp; int n; /* Check for bad system call parameters. */ rp = proc_addr(caller); if (src_dest < -NR_TASKS || (src_dest >= NR_PROCS && src_dest != ANY) ) { rp->p_reg[RET_REG] = E_BAD_SRC; return; } if (function != BOTH && caller >= LOW_USER) { rp->p_reg[RET_REG] = E_NO_PERM; /* users only do BOTH */ return; } /* The parameters are ok. Do the call. */ if (function & SEND) { n = mini_send(caller, src_dest, m_ptr); /* func = SEND or BOTH */ if (function == SEND || n != OK) rp->p_reg[RET_REG] = n; if (n != OK) return; /* SEND failed */ } if (function & RECEIVE) { n = mini_rec(caller, src_dest, m_ptr); /* func = RECEIVE or BOTH */ rp->p_reg[RET_REG] = n; } } /*===========================================================================* * mini_send * *===========================================================================*/ PUBLIC int mini_send(caller, dest, m_ptr) int caller; /* who is trying to send a message? */ int dest; /* to whom is message being sent? */ message *m_ptr; /* pointer to message buffer */ { /* Send a message from 'caller' to 'dest'. If 'dest' is blocked waiting for * this message, copy the message to it and unblock 'dest'. If 'dest' is not * waiting at all, or is waiting for another source, queue 'caller'. */ register struct proc *caller_ptr, *dest_ptr, *next_ptr; vir_bytes vb; /* message buffer pointer as vir_bytes */ vir_clicks vlo, vhi; /* virtual clicks containing message to send */ vir_clicks len; /* length of data segment in clicks */ /* User processes are only allowed to send to FS and MM. Check for this. */ if (caller >= LOW_USER && (dest != FS_PROC_NR && dest != MM_PROC_NR)) return(E_BAD_DEST); caller_ptr = proc_addr(caller); /* pointer to source's proc entry */ dest_ptr = proc_addr(dest); /* pointer to destination's proc entry */ if (dest_ptr->p_flags & P_SLOT_FREE) return(E_BAD_DEST); /* dead dest */ /* Check for messages wrapping around top of memory or outside data seg. */ len = caller_ptr->p_map[D].mem_len; vb = (vir_bytes) m_ptr; vlo = vb >> CLICK_SHIFT; /* vir click for bottom of message */ vhi = (vb + MESS_SIZE - 1) >> CLICK_SHIFT; /* vir click for top of message */ if (vhi < vlo || vhi - caller_ptr->p_map[D].mem_vir >= len)return(E_BAD_ADDR); /* Check to see if 'dest' is blocked waiting for this message. */ if ( (dest_ptr->p_flags & RECEIVING) && (dest_ptr->p_getfrom == ANY || dest_ptr->p_getfrom == caller) ) { /* Destination is indeed waiting for this message. */ cp_mess(caller, caller_ptr->p_map[D].mem_phys, m_ptr, dest_ptr->p_map[D].mem_phys, dest_ptr->p_messbuf); dest_ptr->p_flags &= ~RECEIVING; /* deblock destination */ if (dest_ptr->p_flags == 0) ready(dest_ptr); } else { /* Destination is not waiting. Block and queue caller. */ if (caller == HARDWARE) return(E_OVERRUN); caller_ptr->p_messbuf = m_ptr; caller_ptr->p_flags |= SENDING; unready(caller_ptr); /* Process is now blocked. Put in on the destination's queue. */ if ( (next_ptr = dest_ptr->p_callerq) == NIL_PROC) { dest_ptr->p_callerq = caller_ptr; } else { while (next_ptr->p_sendlink != NIL_PROC) next_ptr = next_ptr->p_sendlink; next_ptr->p_sendlink = caller_ptr; } caller_ptr->p_sendlink = NIL_PROC; } return(OK); } /*===========================================================================* * mini_rec * *===========================================================================*/ PRIVATE int mini_rec(caller, src, m_ptr) int caller; /* process trying to get message */ int src; /* which message source is wanted (or ANY) */ message *m_ptr; /* pointer to message buffer */ { /* A process or task wants to get a message. If one is already queued, * acquire it and deblock the sender. If no message from the desired source * is available, block the caller. No need to check parameters for validity. * Users calls are always sendrec(), and mini_send() has checked already. * Calls from the tasks, MM, and FS are trusted. */ register struct proc *caller_ptr, *sender_ptr, *prev_ptr; int sender; caller_ptr = proc_addr(caller); /* pointer to caller's proc structure */ /* Check to see if a message from desired source is already available. */ sender_ptr = caller_ptr->p_callerq; while (sender_ptr != NIL_PROC) { sender = sender_ptr - proc - NR_TASKS; if (src == ANY || src == sender) { /* An acceptable message has been found. */ cp_mess(sender, sender_ptr->p_map[D].mem_phys, sender_ptr->p_messbuf, caller_ptr->p_map[D].mem_phys, m_ptr); sender_ptr->p_flags &= ~SENDING; /* deblock sender */ if (sender_ptr->p_flags == 0) ready(sender_ptr); if (sender_ptr == caller_ptr->p_callerq) caller_ptr->p_callerq = sender_ptr->p_sendlink; else prev_ptr->p_sendli[]^_`ank = sender_ptr->p_sendlink; return(OK); } prev_ptr = sender_ptr; sender_ptr = sender_ptr->p_sendlink; } /* No suitable message is available. Block the process trying to receive. */ caller_ptr->p_getfrom = src; caller_ptr->p_messbuf = m_ptr; caller_ptr->p_flags |= RECEIVING; unready(caller_ptr); /* If MM has just blocked and there are kernel signals pending, now is the * time to tell MM about them, since it will be able to accept the message. */ if (sig_procs > 0 && caller == MM_PROC_NR && src == ANY) inform(MM_PROC_NR); return(OK); } /*===========================================================================* * pick_proc * *===========================================================================*/ PUBLIC pick_proc() { /* Decide who to run now. */ register int q; /* which queue to use */ if (rdy_head[TASK_Q] != NIL_PROC) q = TASK_Q; else if (rdy_head[SERVER_Q] != NIL_PROC) q = SERVER_Q; else q = USER_Q; /* Set 'cur_proc' and 'proc_ptr'. If system is idle, set 'cur_proc' to a * special value (IDLE), and set 'proc_ptr' to point to an unused proc table * slot, namely, that of task -1 (HARDWARE), so save() will have somewhere to * deposit the registers when a interrupt occurs on an idle machine. * Record previous process so that when clock tick happens, the clock task * can find out who was running just before it began to run. (While the * clock task is running, 'cur_proc' = CLOCKTASK. In addition, set 'bill_ptr' * to always point to the process to be billed for CPU time. */ prev_proc = cur_proc; if (rdy_head[q] != NIL_PROC) { /* Someone is runnable. */ cur_proc = rdy_head[q] - proc - NR_TASKS; proc_ptr = rdy_head[q]; if (cur_proc >= LOW_USER) bill_ptr = proc_ptr; } else { /* No one is runnable. */ cur_proc = IDLE; proc_ptr = proc_addr(HARDWARE); bill_ptr = proc_ptr; } } /*===========================================================================* * ready * *===========================================================================*/ PUBLIC ready(rp) register struct proc *rp; /* this process is now runnable */ { /* Add 'rp' to the end of one of the queues of runnable processes. Three * queues are maintained: * TASK_Q - (highest priority) for runnable tasks * SERVER_Q - (middle priority) for MM and FS only * USER_Q - (lowest priority) for user processes */ register int q; /* TASK_Q, SERVER_Q, or USER_Q */ int r; lock(); /* disable interrupts */ r = (rp - proc) - NR_TASKS; /* task or proc number */ q = (r < 0 ? TASK_Q : r < LOW_USER ? SERVER_Q : USER_Q); /* See if the relevant queue is empty. */ if (rdy_head[q] == NIL_PROC) rdy_head[q] = rp; /* add to empty queue */ else rdy_tail[q]->p_nextready = rp; /* add to tail of nonempty queue */ rdy_tail[q] = rp; /* new entry has no successor */ rp->p_nextready = NIL_PROC; restore(); /* restore interrupts to previous state */ } /*===========================================================================* * unready * *===========================================================================*/ PUBLIC unready(rp) register struct proc *rp; /* this process is no longer runnable */ { /* A process has blocked. */ register struct proc *xp; int r, q; lock(); /* disable interrupts */ r = rp - proc - NR_TASKS; q = (r < 0 ? TASK_Q : r < LOW_USER ? SERVER_Q : USER_Q); if ( (xp = rdy_head[q]) == NIL_PROC) return; if (xp == rp) { /* Remove head of queue */ rdy_head[q] = xp->p_nextready; pick_proc(); } else { /* Search body of queue. A process can be made unready even if it is * not running by being sent a signal that kills it. */ while (xp->p_nextready != rp) if ( (xp = xp->p_nextready) == NIL_PROC) return; xp->p_nextready = xp->p_nextready->p_nextready; while (xp->p_nextready != NIL_PROC) xp = xp->p_nextready; rdy_tail[q] = xp; } restore(); /* restore interrupts to previous state */ } /*===========================================================================* * sched * *===========================================================================*/ PUBLIC sched() { /* The current process has run too long. If another low priority (user) * process is runnable, put the current process on the end of the user queue, * possibly promoting another user to head of the queue. */ lock(); /* disable interrupts */ if (rdy_head[USER_Q] == NIL_PROC) { restore(); /* restore interrupts to previous state */ return; } /* One or more user processes queued. */ rdy_tail[USER_Q]->p_nextready = rdy_head[USER_Q]; rdy_tail[USER_Q] = rdy_head[USER_Q]; rdy_head[USER_Q] = rdy_head[USER_Q]->p_nextready; rdy_tail[USER_Q]->p_nextready = NIL_PROC; pick_proc(); restore(); /* restore interrupts to previous state */ } /* This task handles the interface between file system and kernel as well as * between memory manager and kernel. System services are obtained by sending * sys_task() a message specifying what is needed. To make life easier for * MM and FS, a library is provided with routines whose names are of the * form sys_xxx, e.g. sys_xit sends the SYS_XIT message to sys_task. The * message types and parameters are: * * SYS_FORK informs kernel that a process has forked * SYS_NEWMAP allows MM to set up a process memory map * SYS_EXEC sets program counter and stack pointer after EXEC * SYS_XIT informs kernel that a process has exited * SYS_GETSP caller wants to read out some process' stack pointer * SYS_TIMES caller wants to get accounting times for a process * SYS_ABORT MM or FS cannot go on; abort MINIX * SYS_SIG send a signal to a process * SYS_COPY requests a block of data to be copied between processes * * Message type m1 is used for all except SYS_SIG and SYS_COPY, both of * which need special parameter types. * * m_type PROC1 PROC2 PID MEM_PTR * ------------------------------------------------------ * | SYS_FORK | parent | child | pid | | * |------------+---------+---------+---------+---------| * | SYS_NEWMAP | proc nr | | | map ptr | * |------------+---------+---------+---------+---------| * | SYS_EXEC | proc nr | | new sp | | * |------------+---------+---------+---------+---------| * | SYS_XIT | parent | exitee | | | * |------------+---------+---------+---------+---------| * | SYS_GETSP | proc nr | | | | * |------------+---------+---------+---------+---------| * | SYS_TIMES | proc nr | | buf ptr | | * |------------+---------+---------+---------+---------| * | SYS_ABORT | | | | | * ------------------------------------------------------ * * * m_type m6_i1 m6_i2 m6_i3 m6_f1 * ------------------------------------------------------ * | SYS_SIG | proc_nr | sig | | handler | * ------------------------------------------------------ * * * m_type m5_c1 m5_i1 m5_l1 m5_c2 m5_i2 m5_l2 m5_l3 * -------------------------------------------------------------------------- * | SYS_COPY |src seg|src proc|src vir|dst seg|dst proc|dst vir| byte ct | * -------------------------------------------------------------------------- * * In addition to the main sys_task() entry point, there are three other minor * entry points: * cause_sig: take action to cause a signal to occur, sooner or later * inform: tell MM about pending signals * umap: compute the physical address for a given virtual address */ #include "../h/const.h" #include "../h/type.h" #include "../h/callnr.h" #include "../h/com.h" #include "../h/error.h" #include "../h/signal.h" #include "const.h" #include "type.h" #include "glo.h" #include "proc.h" #define COPY_UNIT 65534L /* max bytes to copy at once */ extern phys_bytes umap(); PRIVATE message m; PRIVATE char sig_stuff[SIG_PUSH_BYTES]; /* used to send signals to processes */ /*===========================================================================* * sys_task * *===========================================================================*/ PUBLIC sys_task() { /* Main entry point of sys_task. Get the message and dispatch on type. */ register int r; while (TRUE) { receive(ANY, &m); switch (m.m_type) { /* which system call */ case SYS_FORK: r = do_fork(&m); break; case SYS_NEWMAP: r = do_newmap(&m); break; case SYS_EXEC: r = do_exec(&m); break; case SYS_XIT: r = do_xit(&m); break; case SYS_GETSP: r = do_getsp(&m); break; case SYS_TIMES: r = do_times(&m); break; case SYS_ABORT: r = do_abort(&m); break; case SYS_SIG: r = do_sig(&m); break; case SYS_COPY: r = do_copy(&m); break; default: r = E_BAD_FCN; } m.m_type = r; /* 'r' reports status of call */ send(m.m_source, &m); /* send reply to caller */ } } /*===========================================================================* * do_fork * *===========================================================================*/ PRIVATE int do_fork(m_ptr) message *m_ptr; /* pointer to request message */ { /* Handle sys_fork(). 'k1' has forked. The child is 'k2'. */ register struct proc *rpc; register char *sptr, *dptr; /* pointers for copying proc struct */ int k1; /* number of parent process */ int k2; /* number of child process */ int pid; /* process id of child */ int bytes; /* counter for copying proc struct */ k1 = m_ptr->PROC1; /* extract parent slot number from msg */ k2 = m_ptr->PROC2; /* extract child slot number */ pid = m_ptr->PID; /* extract child process id */ if (k1 < 0 || k1 >= NR_PROCS || k2 < 0 || k2 >= NR_PROCS)return(E_BAD_PROC); rpc = proc_addr(k2); /* Copy parent 'proc' struct to child. */ sptr = (char *) proc_addr(k1); /* parent pointer */ dptr = (char *) proc_addr(k2); /* child pointer */ bytes = sizeof(struct proc); /* # bytes to copy */ while (bytes--) *dptr++ = *sptr++; /* copy parent struct to child */ rpc->p_flags |= NO_MAP; /* inhibit the process from running */ rpc->p_pid = pid; /* install child's pid */ rpc->p_reg[RET_REG] = 0; /* child sees pid = 0 to know it is child */ rpc->user_time = 0; /* set all the accounting times to 0 */ rpc->sys_time = 0; rpc->child_utime = 0; rpc->child_stime = 0; return(OK); } /*===========================================================================* * do_newmap * *===========================================================================*/ PRIVATE int do_newmap(m_ptr) message *m_ptr; /* pointer to request message */ { /* Handle sys_newmap(). Fetch the memory map from MM. */ register struct proc *rp, *rsrc; phys_bytes src_phys, dst_phys, pn; vir_bytes vmm, vsys, vn; int caller; /* whose space has the new map (usually MM) */ int k; /* process whose map is to be loaded */ int old_flags; /* value of flags before modification */ struct mem_map *map_ptr; /* virtual address of map inside caller (MM) */ /* Extract message parameters and copy new memory map from MM. */ caller = m_ptr->m_source; k = m_ptr->PROC1; map_ptr = (struct mem_map *) m_ptr->MEM_PTR; if (k < -NR_TASKS || k >= NR_PROCS) return(E_BAD_PROC); rp = proc_addr(k); /* ptr to entry of user getting new map */ rsrc = proc_addr(caller); /* ptr to MM's proc entry */ vn = NR_SEGS * sizeof(struct mem_map); pn = vn; vmm = (vir_bytes) map_ptr; /* careful about sign extension */ vsys = (vir_bytes) rp->p_map; /* again, careful about sign extension */ if ( (src_phys = umap(rsrc, D, vmm, vn)) == 0) panic("bad call to sys_newmap (src)", NO_NUM); if ( (dst_phys = umap(proc_addr(SYSTASK), D, vsys, vn)) == 0) panic("bad call to sys_newmap (dst)", NO_NUM); phys_copy(src_phys, dst_phys, pn); #ifdef i8088 /* On 8088, set segment registers. */ rp->p_reg[CS_REG] = rp->p_map[T].mem_phys; /* set cs */ rp->p_reg[DS_REG] = rp->p_map[D].mem_phys; /* set ds */ rp->p_reg[SS_REG] = rp->p_map[D].mem_phys; /* set ss */ rp->p_reg[ES_REG] = rp->p_map[D].mem_phys; /* set es */ #endif old_flags = rp->p_flags; /* save the previous value of the flags */ rp->p_flags &= ~NO_MAP; if (old_flags != 0 && rp->p_flags == 0) ready(rp); return(OK); } /*===========================================================================* * do_exec * *===========================================================================*/ PRIVATE int do_exec(m_ptr) message *m_ptr; /* pointer to request message */ { /* Handle sys_exec(). A process has done a successful EXEC. Patch it up. */ register struct proc *rp; int k; /* which process */ int *sp; /* new sp */ k = m_ptr->PROC1; /* 'k' tells which process did EXEC */ sp = (int *) m_ptr->STACK_PTR; if (k < 0 || k >= NR_PROCS) return(E_BAD_PROC); rp = proc_addr(k); rp->p_sp = sp; /* siklmnopqrstuet the stack pointer */ rp->p_pcpsw.pc = (int (*)()) 0; /* reset pc */ rp->p_alarm = 0; /* reset alarm timer */ rp->p_flags &= ~RECEIVING; /* MM does not reply to EXEC call */ if (rp->p_flags == 0) ready(rp); set_name(k, sp); /* save command string for F1 display */ return(OK); } /*===========================================================================* * do_xit * *===========================================================================*/ PRIVATE int do_xit(m_ptr) message *m_ptr; /* pointer to request message */ { /* Handle sys_xit(). A process has exited. */ register struct proc *rp, *rc; struct proc *np, *xp; int parent; /* number of exiting proc's parent */ int proc_nr; /* number of process doing the exit */ parent = m_ptr->PROC1; /* slot number of parent process */ proc_nr = m_ptr->PROC2; /* slot number of exiting process */ if (parent < 0 || parent >= NR_PROCS || proc_nr < 0 || proc_nr >= NR_PROCS) return(E_BAD_PROC); rp = proc_addr(parent); rc = proc_addr(proc_nr); rp->child_utime += rc->user_time + rc->child_utime; /* accum child times */ rp->child_stime += rc->sys_time + rc->child_stime; unready(rc); rc->p_alarm = 0; /* turn off alarm timer */ set_name(proc_nr, (char *) 0); /* disable command printing for F1 */ /* If the process being terminated happens to be queued trying to send a * message (i.e., the process was killed by a signal, rather than it doing an * EXIT), then it must be removed from the message queues. */ if (rc->p_flags & SENDING) { /* Check all proc slots to see if the exiting process is queued. */ for (rp = &proc[0]; rp < &proc[NR_TASKS + NR_PROCS]; rp++) { if (rp->p_callerq == NIL_PROC) continue; if (rp->p_callerq == rc) { /* Exiting process is on front of this queue. */ rp->p_callerq = rc->p_sendlink; break; } else { /* See if exiting process is in middle of queue. */ np = rp->p_callerq; while ( ( xp = np->p_sendlink) != NIL_PROC) if (xp == rc) { np->p_sendlink = xp->p_sendlink; break; } else { np = xp; } } } } rc->p_flags = P_SLOT_FREE; return(OK); } /*===========================================================================* * do_getsp * *===========================================================================*/ PRIVATE int do_getsp(m_ptr) message *m_ptr; /* pointer to request message */ { /* Handle sys_getsp(). MM wants to know what sp is. */ register struct proc *rp; int k; /* whose stack pointer is wanted? */ k = m_ptr->PROC1; if (k < 0 || k >= NR_PROCS) return(E_BAD_PROC); rp = proc_addr(k); m.STACK_PTR = (char *) rp->p_sp; /* return sp here */ return(OK); } /*===========================================================================* * do_times * *===========================================================================*/ PRIVATE int do_times(m_ptr) message *m_ptr; /* pointer to request message */ { /* Handle sys_times(). Retrieve the accounting information. */ register struct proc *rp; int k; k = m_ptr->PROC1; /* k tells whose times are wanted */ if (k < 0 || k >= NR_PROCS) return(E_BAD_PROC); rp = proc_addr(k); /* Insert the four times needed by the TIMES system call in the message. */ m_ptr->USER_TIME = rp->user_time; m_ptr->SYSTEM_TIME = rp->sys_time; m_ptr->CHILD_UTIME = rp->child_utime; m_ptr->CHILD_STIME = rp->child_stime; return(OK); } /*===========================================================================* * do_abort * *===========================================================================*/ PRIVATE int do_abort(m_ptr) message *m_ptr; /* pointer to request message */ { /* Handle sys_abort. MINIX is unable to continue. Terminate operation. */ panic("", NO_NUM); } /*===========================================================================* * do_sig * *===========================================================================*/ PRIVATE int do_sig(m_ptr) message *m_ptr; /* pointer to request message */ { /* Handle sys_sig(). Signal a process. The stack is known to be big enough. */ register struct proc *rp; phys_bytes src_phys, dst_phys; vir_bytes vir_addr, sig_size, new_sp; int proc_nr; /* process number */ int sig; /* signal number 1-16 */ int (*sig_handler)(); /* pointer to the signal handler */ /* Extract parameters and prepare to build the words that get pushed. */ proc_nr = m_ptr->PR; /* process being signalled */ sig = m_ptr->SIGNUM; /* signal number, 1 to 16 */ sig_handler = m_ptr->FUNC; /* run time system addr for catching sigs */ if (proc_nr < LOW_USER || proc_nr >= NR_PROCS) return(E_BAD_PROC); rp = proc_addr(proc_nr); vir_addr = (vir_bytes) sig_stuff; /* info to be pushed is in 'sig_stuff' */ new_sp = (vir_bytes) rp->p_sp; /* Actually build the block of words to push onto the stack. */ build_sig(sig_stuff, rp, sig); /* build up the info to be pushed */ /* Prepare to do the push, and do it. */ sig_size = SIG_PUSH_BYTES; new_sp -= sig_size; src_phys = umap(proc_addr(SYSTASK), D, vir_addr, sig_size); dst_phys = umap(rp, S, new_sp, sig_size); if (dst_phys == 0) panic("do_sig can't signal; SP bad", NO_NUM); phys_copy(src_phys, dst_phys, (phys_bytes) sig_size); /* push pc, psw */ /* Change process' sp and pc to reflect the interrupt. */ rp->p_sp = (int *) new_sp; rp->p_pcpsw.pc = sig_handler; return(OK); } /*===========================================================================* * do_copy * *===========================================================================*/ PRIVATE int do_copy(m_ptr) message *m_ptr; /* pointer to request message */ { /* Handle sys_copy(). Copy data for MM or FS. */ int src_proc, dst_proc, src_space, dst_space; vir_bytes src_vir, dst_vir; phys_bytes src_phys, dst_phys, bytes; /* Dismember the command message. */ src_proc = m_ptr->SRC_PROC_NR; dst_proc = m_ptr->DST_PROC_NR; src_space = m_ptr->SRC_SPACE; dst_space = m_ptr->DST_SPACE; src_vir = (vir_bytes) m_ptr->SRC_BUFFER; dst_vir = (vir_bytes) m_ptr->DST_BUFFER; bytes = (phys_bytes) m_ptr->COPY_BYTES; /* Compute the source and destination addresses and do the copy. */ if (src_proc == ABS) src_phys = (phys_bytes) m_ptr->SRC_BUFFER; else src_phys = umap(proc_addr(src_proc),src_space,src_vir,(vir_bytes)bytes); if (dst_proc == ABS) dst_phys = (phys_bytes) m_ptr->DST_BUFFER; else dst_phys = umap(proc_addr(dst_proc),dst_space,dst_vir,(vir_bytes)bytes); if (src_phys == 0 || dst_phys == 0) return(EFAULT); phys_copy(src_phys, dst_phys, bytes); return(OK); } /*===========================================================================* * cause_sig * *===========================================================================*/ PUBLIC cause_sig(proc_nr, sig_nr) int proc_nr; /* process to be signalled */ int sig_nr; /* signal to be sent in range 1 - 16 */ { /* A task wants to send a signal to a process. Examples of such tasks are: * TTY wanting to cause SIGINT upon getting a DEL * CLOCK wanting to cause SIGALRM when timer expires * Signals are handled by sending a message to MM. The tasks don't dare do * that directly, for fear of what would happen if MM were busy. Instead they * call cause_sig, which sets bits in p_pending, and then carefully checks to * see if MM is free. If so, a message is sent to it. If not, when it becomes * free, a message is sent. The calling task always gets control back from * cause_sig() immediately. */ register struct proc *rp; rp = proc_addr(proc_nr); if (rp->p_pending == 0) sig_procs++; /* incr if a new proc is now pending */ rp->p_pending |= 1 << (sig_nr - 1); inform(MM_PROC_NR); /* see if MM is free */ } /*===========================================================================* * inform * *===========================================================================*/ PUBLIC inform(proc_nr) int proc_nr; /* MM_PROC_NR or FS_PROC_NR */ { /* When a signal is detected by the kernel (e.g., DEL), or generated by a task * (e.g. clock task for SIGALRM), cause_sig() is called to set a bit in the * p_pending field of the process to signal. Then inform() is called to see * if MM is idle and can be told about it. Whenever MM blocks, a check is * made to see if 'sig_procs' is nonzero; if so, inform() is called. */ register struct proc *rp, *mmp; /* If MM is not waiting for new input, forget it. */ mmp = proc_addr(proc_nr); if ( ((mmp->p_flags & RECEIVING) == 0) || mmp->p_getfrom != ANY) return; /* MM is waiting for new input. Find a process with pending signals. */ for (rp = proc_addr(0); rp < proc_addr(NR_PROCS); rp++) if (rp->p_pending != 0) { m.m_type = KSIG; m.PROC1 = rp - proc - NR_TASKS; m.SIG_MAP = rp->p_pending; sig_procs--; if (mini_send(HARDWARE, proc_nr, &m) != OK) panic("can't inform MM", NO_NUM); rp->p_pending = 0; /* the ball is now in MM's court */ return; } } /*===========================================================================* * umap * *===========================================================================*/ PUBLIC phys_bytes umap(rp, seg, vir_addr, bytes) register struct proc *rp; /* pointer to proc table entry for process */ int seg; /* T, D, or S segment */ vir_bytes vir_addr; /* virtual address in bytes within the seg */ vir_bytes bytes; /* # of bytes to be copied */ { /* Calculate the physical memory address for a given virtual address. */ vir_clicks vc; /* the virtual address in clicks */ phys_bytes seg_base, pa; /* intermediate variables as phys_bytes */ /* If 'seg' is D it could really be S and vice versa. T really means T. * If the virtual address falls in the gap, it causes a problem. On the * 8088 it is probably a legal stack reference, since "stackfaults" are * not detected by the hardware. On 8088s, the gap is called S and * accepted, but on other machines it is called D and rejected. */ if (bytes <= 0) return( (phys_bytes) 0); vc = (vir_addr + bytes - 1) >> CLICK_SHIFT; /* last click of data */ #ifdef i8088 if (seg != T) seg = (vc < rp->p_map[D].mem_vir + rp->p_map[D].mem_len ? D : S); #else if (seg != T) seg = (vc < rp->p_map[S].mem_vir ? D : S); #endif if((vir_addr>>CLICK_SHIFT) >= rp->p_map[seg].mem_vir + rp->p_map[seg].mem_len) return( (phys_bytes) 0 ); seg_base = (phys_bytes) rp->p_map[seg].mem_phys; seg_base = seg_base << CLICK_SHIFT; /* segment orgin in bytes */ pa = (phys_bytes) vir_addr; pa -= rp->p_map[seg].mem_vir << CLICK_SHIFT; return(seg_base + pa); } /* The object file of "table.c" contains all the data. In the *.h files, * declared variables appear with EXTERN in front of them, as in * * EXTERN int x; * * Normally EXTERN is defined as extern, so when they are included in another * file, no storage is allocated. If the EXTERN were not present, but just * say, * * int x; * * then including this file in several source files would cause 'x' to be * declared several times. While some linkers accept this, others do not, * so they are declared extern when included normally. However, it must * be declared for real somewhere. That is done here, but redefining * EXTERN as the null string, so the inclusion of all the *.h files in * table.c actually generates storage for them. All the initialized * variables are also declared here, since * * extern int x = 4; * * is not allowed. If such variables are shared, they must also be declared * in one of the *.h files without the initialization. */ #include "../h/const.h" #include "../h/type.h" #include "const.h" #include "type.h" #undef EXTERN #define EXTERN #include "glo.h" #include "proc.h" extern int sys_task(), clock_task(), mem_task(), floppy_task(), winchester_task(), tty_task(), printer_task(); /* The startup routine of each task is given below, from -NR_TASKS upwards. * The order of the names here MUST agree with the numerical values assigned to * the tasks in ../h/com.h. */ int (*task[NR_TASKS+INIT_PROC_NR+1])() = { printer_task, tty_task, winchester_task, floppy_task, mem_task, clock_task, sys_task, 0, 0, 0, 0 }; /* This file contains the terminal driver, both for the IBM console and regular * ASCII terminals. It is split into two sections, a device-independent part * and a device-dependent part. The device-independent part accepts * characters to be printed from programs and queues them in a standard way * for device-dependent output. It also accepts input and queues it for * programs. This file contains 2 main entry points: tty_task() and keyboard(). * When a key is struck on a terminal, an interrupt to an assembly language * routine is generated. This routine saves the machine state and registers * and calls keyboard(), which enters the character in an internal table, and * then sends a message to the terminal task. The main program of the terminal * task is tty_task(). It accepts not only messages about typed input, but * also requests to read and write from terminals, etc. * * The device-dependent part interfaces with the IBM console and ASCII * terminals. The IBM keyboard is unusual in that keystrokes yield key numbers * rather than ASCII codes, and furthermore, an interrupt is generated when a * key is depressed and again when it is released. The IBM display is memory * mapped, so outputting characters such as line feed, backspace and bell are * tricky. * * The valid messages and their parameters are: * * TTY_CHAR_INT: a character has been typed on a terminal (input interrupt) * TTY_O_DONE: a character has been output (output completed interrupt) * TTY_READ: a process wants to read from a terminal * TTY_WRITE: a process wants to write on a terminal * TTY_IOCTL: a process wants to change a terminal's parameters * CANCEL: terminate a previous incomplete system call immediately * * m_type TTY_LINE PROC_NR COUNT TTY_SPEK TTY_FLAGS ADDRESS * --------------------------------------------------------------------------- * | TTY_CHAR_INT| | | | | |array ptr| * |-------------+---------+---------+---------+---------+---------+---------| * | TTY_O_DONE |minor dev| | | | | | * |-------------+---------+---------+---------+---------+---------+---------| * | TTY_READ |minor dev| proc nr | count | | | buf ptr | * |-------------+---------+---------+---------+---------+---------+---------| * | TTY_WRITE |minor dev| proc nr | count | | | buf ptr | * |-------------+---------+---------+---------+---------+---------+---------| * | TTY_IOCTL |minor dev| proc nr |func code|erase etc| flags | | * |-------------+---------+---------+---------+---------+---------+---------| * | CANCEL |minor dev| proc nr | | | | | * --------------------------------------------------------------------------- */ #include "../h/const.h" #include "../h/type.h" #include "../h/callnr.h" #include "../h/com.h" #include "../h/error.h" #include "../h/sgtty.h" #include "../h/signal.h" #include "const.h" #include "type.h" #include "glo.h" #include "proc.h" #define NR_TTYS 1 /* how many terminals can system handle */ #define TTY_IN_BYTES 200 /* input queue size */ #define TTY_RAM_WORDS 320 /* ram buffer size */ #define TTY_BUF_SIZE 256 /* unit for copying to/from queues */ #define TAB_SIZE 8 /* distance between tabs */ #define TAB_MASK 07 /* mask for tty_column when tabbing */ #define MAX_OVERRUN 16 /* size of overrun input buffer */ #define ERASE_CHAR '\b' /* default erase character */ #define KILL_CHAR '@' /* default kill character */ #define INTR_CHAR (char)0177 /* default interrupt character */ #define QUIT_CHAR (char) 034 /* default quit character */ #define XOFF_CHAR (char) 023 /* default x-off character (CTRL-S) */ #define XON_CHAR (char) 021 /* default x-on character (CTRL-Q) */ #define EOT_CHAR (char) 004 /* CTRL-D */ #define MARKER (char) 000 /* non-escaped CTRL-D stored as MARKER */ #define DEL_CODE (char) 83 /* DEL for use in CTRL-ALT-DEL reboot */ #define AT_SIGN 0220 /* code to yield for CTRL-@ */ #define F1 59 /* scan code for function key F1 */ #define F2 60 /* scan code for function key F2 */ #define F9 67 /* scan code for function key F9 */ #define F10 68 /* scan code for function key F10 */ #define TOP_ROW 14 /* codes below this are shifted if CTRL */ PRIVATE struct tty_struct { /* Input queue. Typed characters are stored here until read by a program. */ char tty_inqueue[TTY_IN_BYTES]; /* array used to store the characters */ char *tty_inhead; /* pointer to place where next char goes */ char *tty_intail; /* pointer to next char to be given to prog */ int tty_incount; /* # chars in tty_inqueue */ int tty_lfct; /* # line feeds in tty_inqueue */ /* Output section. */ int tty_ramqueue[TTY_RAM_WORDS]; /* buffer for video RAM */ int tty_rwords; /* number of WORDS (not bytes) in outqueue */ int tty_org; /* location in RAM where 6845 base points */ int tty_vid; /* current position of cursor in video RAM */ char tty_esc_state; /* 0=normal, 1 = ESC seen, 2 = ESC + x seen */ char tty_echar; /* first character following an ESC */ int tty_attribute; /* current attribute byte << 8 */ int (*tty_devstart)(); /* routine to start actual device output */ /* Terminal parameters and status. */ int tty_mode; /* terminal mode set by IOCTL */ int tty_column; /* current column number (0-origin) */ int tty_row; /* current row (0 at bottom of screen) */ char tty_busy; /* 1 when output in progress, else 0 */ char tty_escaped; /* 1 when '\' just seen, else 0 */ char tty_inhibited; /* 1 when CTRL-S just seen (stops output) */ char tty_makebreak; /* 1 for terminals that interrupt twice/key */ char tty_waiting; /* 1 when output process waiting for reply */ /* User settable characters: erase, kill, interrupt, quit, x-on; x-off. */ char tty_erase; /* char used to erase 1 char (init ^H) */ char tty_kill; /* char used to erase a line (init @) */ char tty_intr; /* char used to send SIGINT (init DEL) */ char tty_quit; /* char used for core dump (init CTRL-\) */ char tty_xon; /* char used to start output (init CTRL-Q)*/ char tty_xoff; /* char used to stop output (init CTRL-S) */ char tty_eof; /* char used to stop output (init CTRL-D) */ /* Information about incomplete I/O requests is stored here. */ char tty_incaller; /* process that made the call (usually FS) */ char tty_inproc; /* process that wants to read from tty */ char *tty_in_vir; /* virtual address where data is to go */ int tty_inleft; /* how many chars are still needed */ char tty_otcaller; /* process that made the call (usually FS) */ char tty_outproc; /* process that wants to write to tty */ char *tty_out_vir; /* virtual address where data comes from */ phys_bytes tty_phys; /* physical address where data comes from */ int tty_outleft; /* # chars yet to be copied to tty_outqueue */ int tty_cum; /* # chars copied to tty_outqueue so far */ /* Miscellaneous. */ int tty_ioport; /* I/O port number for this terminal */ } tty_struct[NR_TTYS]; /* Values for the fields. */ #define NOT_ESCAPED 0 /* previous character on this line not '\' */ #define ESCAPED 1 /* previous character on this line was '\' */ #define RUNNING 0 /* no CRTL-S has been typed to stop the tty */ #define STOPPED 1 /* CTRL-S has been typed to stop the tty */ #define INACTIVE 0 /* the tty is not printing */ #define BUSY 1 /* the tty is printing */ #define ONE_INT 0 /* regular terminals interrupt once per char */ #define TWO_INTS 1 /* IBM console interrupts two times per char */ #define NOT_WAITING 0 /* no output process is hanging */ #define WAITING 1 /* an output process is waiting for a reply */ PRIVATE char tty_driver_buf[2*MAX_OVERRUN+2]; /* driver collects chars here */ PRIVATE char tty_copy_buf[2*MAX_OVERRUN]; /* copy buf used to av‚ƒ„…†‡ˆ‰Š‹ŒŽ‘’“”•–—˜™š›œžŸ ĄĸŖ¤Ĩϧ9oid races */ PRIVATE char tty_buf[TTY_BUF_SIZE]; /* scratch buffer to/from user space */ PRIVATE int shift1, shift2, capslock, numlock; /* keep track of shift keys */ PRIVATE int control, alt; /* keep track of key statii */ PRIVATE caps_off = 1; /* 1 = normal position, 0 = depressed */ PRIVATE num_off = 1; /* 1 = normal position, 0 = depressed */ PUBLIC int color; /* 1 if console is color, 0 if it is mono */ PUBLIC scan_code; /* scan code for '=' saved by bootstrap */ /* Scan codes to ASCII for unshifted keys */ PRIVATE char unsh[] = { 0,033,'1','2','3','4','5','6', '7','8','9','0','-','=','\b','\t', 'q','w','e','r','t','y','u','i', 'o','p','[',']',015,0202,'a','s', 'd','f','g','h','j','k','l',';', 047,0140,0200,0134,'z','x','c','v', 'b','n','m',',','.','/',0201,'*', 0203,' ',0204,0241,0242,0243,0244,0245, 0246,0247,0250,0251,0252,0205,0210,0267, 0270,0271,0211,0264,0265,0266,0214 ,0261,0262,0263,'0',0177 }; /* Scan codes to ASCII for shifted keys */ PRIVATE char sh[] = { 0,033,'!','@','#','$','%','^', '&','*','(',')','_','+','\b','\t', 'Q','W','E','R','T','Y','U','I', 'O','P','{','}',015,0202,'A','S', 'D','F','G','H','J','K','L',':', 042,'~',0200,'|','Z','X','C','V', 'B','N','M','<','>','?',0201,'*', 0203,' ',0204,0221,0222,0223,0224,0225, 0226,0227,0230,0231,0232,0204,0213,'7', '8','9',0211,'4','5','6',0214,'1', '2','3','0','.' }; /* Scan codes to ASCII for Olivetti M24 for unshifted keys. */ PRIVATE char unm24[] = { 0,033,'1','2','3','4','5','6', '7','8','9','0','-','^','\b','\t', 'q','w','e','r','t','y','u','i', 'o','p','@','[','\r',0202,'a','s', 'd','f','g','h','j','k','l',';', ':',']',0200,'\\','z','x','c','v', 'b','n','m',',','.','/',0201,'*', 0203,' ',0204,0241,0242,0243,0244,0245, 0246,0247,0250,0251,0252,023,0210,0267,0270,0271,0211,0264,0265,0266,0214,0261, 0262,0263,'0','.',' ',014,0212,'\r', 0264,0262,0266,0270,032,0213,' ','/', 0253,0254,0255,0256,0257,0215,0216,0217 }; /* Scan codes to ASCII for Olivetti M24 for shifted keys. */ PRIVATE char m24[] = { 0,033,'!','"','#','$','%','&', 047,'(',')','_','=','~','\b','\t', 'Q','W','E','R' ,'T','Y','U','I', 'O','P',0140,'{','\r',0202,'A','S', 'D','F','G','H','J','K','L','+', '*','}',0200,'|','Z','X','C','V', 'B','N','M','<','>','?',0201,'*', 0203,' ',0204,0221,0222,0223,0224,0225, 0226,0227,0230,0231,0232,0270,023,'7', '8','9',0211,'4','5','6',0214,'1', '2','3',0207,0177,0271,014,0272,'\r', '\b','\n','\f',036,032,0273,0274,'/', 0233,0234,0235,0236,0237,0275,0276,0277 }; /*===========================================================================* * tty_task * *===========================================================================*/ PUBLIC tty_task() { /* Main routine of the terminal task. */ message tty_mess; /* buffer for all incoming messages */ register struct tty_struct *tp; tty_init(); /* initialize */ while (TRUE) { receive(ANY, &tty_mess); tp = &tty_struct[tty_mess.TTY_LINE]; switch(tty_mess.m_type) { case TTY_CHAR_INT: do_charint(&tty_mess); break; case TTY_READ: do_read(tp, &tty_mess); break; case TTY_WRITE: do_write(tp, &tty_mess); break; case TTY_IOCTL: do_ioctl(tp, &tty_mess); break; case CANCEL : do_cancel(tp, &tty_mess); break; case TTY_O_DONE: /* reserved for future use (RS-232 terminals)*/ default: tty_reply(TASK_REPLY, tty_mess.m_source, tty_mess.PROC_NR, EINVAL, 0L, 0L); } } } /*===========================================================================* * do_charint * *===========================================================================*/ PRIVATE do_charint(m_ptr) message *m_ptr; /* message containing pointer to char(s) */ { /* A character has been typed. If a character is typed and the tty task is * not able to service it immediately, the character is accumulated within * the tty driver. Thus multiple chars may be accumulated. A single message * to the tty task may have to process several characters. */ int m, n, count, replyee, caller; char *ptr, *copy_ptr, ch; struct tty_struct *tp; lock(); /* prevent races by disabling interrupts */ ptr = m_ptr->ADDRESS; /* pointer to accumulated char array */ copy_ptr = tty_copy_buf; /* ptr to shadow array where chars copied */ n = *ptr; /* how many chars have been accumulated */ count = n; /* save the character count */ n = n + n; /* each char occupies 2 bytes */ ptr += 2; /* skip count field at start of array */ while (n-- > 0) *copy_ptr++ = *ptr++; /* copy the array to safety */ ptr = m_ptr->ADDRESS; *ptr = 0; /* accumulation count set to 0 */ unlock(); /* re-enable interrupts */ /* Loop on the accumulated characters, processing each in turn. */ copy_ptr = tty_copy_buf; while (count-- > 0) { ch = *copy_ptr++; /* get the character typed */ n = *copy_ptr++; /* get the line number it came in on */ in_char(n, ch); /* queue the char and echo it */ /* See if a previously blocked reader can now be satisfied. */ tp = &tty_struct[n]; /* pointer to struct for this character */ if (tp->tty_inleft > 0 ) { /* does anybody want input? */ m = tp->tty_mode & (CBREAK | RAW); if (tp->tty_lfct > 0 || (m != 0 && tp->tty_incount > 0)) { m = rd_chars(tp); /* Tell hanging reader that chars have arrived. */ replyee = (int) tp->tty_incaller; caller = (int) tp->tty_inproc; tty_reply(REVIVE, replyee, caller, m, 0L, 0L); } } } } /*===========================================================================* * in_char * *===========================================================================*/ PRIVATE in_char(line, ch) int line; /* line number on which char arrived */ char ch; /* scan code for character that arrived */ { /* A character has just been typed in. Process, save, and echo it. */ register struct tty_struct *tp; int mode, sig; char make_break(); tp = &tty_struct[line]; /* set 'tp' to point to proper struct */ /* Function keys are temporarily being used for debug dumps. */ if (ch >= F1 && ch <= F10) { /* Check for function keys F1, F2, ... F10 */ func_key(ch); /* process function key */ return; } if (tp->tty_incount >= TTY_IN_BYTES) return; /* no room, discard char */ mode = tp->tty_mode & (RAW | CBREAK); if (tp->tty_makebreak) ch = make_break(ch); /* console give 2 ints/ch */ else if (mode != RAW) ch &= 0177; /* 7-bit chars except in raw mode */ if (ch == 0) return; /* Processing for COOKED and CBREAK mode contains special checks. */ if (mode == COOKED || mode == CBREAK) { /* Handle erase, kill and escape processing. */ if (mode == COOKED) { /* First erase processing (rub out of last character). */ if (ch == tp->tty_erase && tp->tty_escaped == NOT_ESCAPED) { if (chuck(tp) != -1) { /* remove last char entered */ echo(tp, '\b'); /* remove it from the screen */ echo(tp, ' '); echo(tp, '\b'); } return; } /* Now do kill processing (remove current line). */ if (ch == tp->tty_kill && tp->tty_escaped == NOT_ESCAPED) { while( chuck(tp) == OK) /* keep looping */ ; echo(tp, tp->tty_kill); echo (tp, '\n'); return; } /* Handle EOT and the escape symbol (backslash). */ if (tp->tty_escaped == NOT_ESCAPED) { /* Normal case: previous char was not backslash. */ if (ch == '\\') { /* An escaped symbol has just been typed. */ tp->tty_escaped = ESCAPED; echo(tp, ch); return; /* do not store the '\' */ } /* CTRL-D means end-of-file, unless it is escaped. It * is stored in the text as MARKER, and counts as a * line feed in terms of knowing whether a full line * has been typed already. */ if (ch == tp->tty_eof) ch = MARKER; } else { /* Previous character was backslash. */ tp->tty_escaped = NOT_ESCAPED; /* turn escaping off */ if (ch != tp->tty_erase && ch != tp->tty_kill && ch != tp->tty_eof) { /* Store the escape previously skipped over */ *tp->tty_inhead++ = '\\'; tp->tty_incount++; if (tp->tty_inhead == &tp->tty_inqueue[TTY_IN_BYTES]) tp->tty_inhead = tp->tty_inqueue; } } } /* Both COOKED and CBREAK modes come here; first map CR to LF. */ if (ch == '\r' && (tp->tty_mode & CRMOD)) ch = '\n'; /* Check for interrupt and quit characters. */ if (ch == tp->tty_intr || ch == tp->tty_quit) { sig = (ch == tp->tty_intr ? SIGINT : SIGQUIT); sigchar(tp, line, sig); return; } /* Check for and process CTRL-S (terminal stop). */ if (ch == tp->tty_xoff) { tp->tty_inhibited = STOPPED; return; } /* Check for and process CTRL-Q (terminal start). */ if (ch == tp->tty_xon) { tp->tty_inhibited = RUNNING; (*tp->tty_devstart)(tp); /* resume output */ return; } } /* All 3 modes come here. */ if (ch == '\n' || ch == MARKER) tp->tty_lfct++; /* count line feeds */ *tp->tty_inhead++ = ch; /* save the character in the input queue */ if (tp->tty_inhead == &tp->tty_inqueue[TTY_IN_BYTES]) tp->tty_inhead = tp->tty_inqueue; /* handle wraparound */ tp->tty_incount++; echo(tp, ch); } #ifdef i8088 /*===========================================================================* * make_break * *===========================================================================*/ PRIVATE char make_break(ch) char ch; /* scan code of key just struck or released */ { /* This routine can handle keyboards that interrupt only on key depression, * as well as keyboards that interrupt on key depression and key release. * For efficiency, the interrupt routine filters out most key releases. */ int c, make, code; c = ch & 0177; /* high-order bit set on key release */ make = (ch & 0200 ? 0 : 1); /* 1 when key depressed, 0 when key released */ if (olivetti == FALSE) { /* Standard IBM keyboard. */ code = (shift1 || shift2 ? sh[c] : unsh[c]); if (control && c < TOP_ROW) code = sh[c]; /* CTRL-(top row) */ if (c > 70 && numlock) /* numlock depressed */ code = (shift1 || shift2 ? unsh[c] : sh[c]); } else { /* (Olivetti M24 or AT&T 6300) with Olivetti-style keyboard. */ code = (shift1 || shift2 ? m24[c] : unm24[c]); if (control && c < TOP_ROW) code = sh[c]; /* CTRL-(top row) */ if (c > 70 && numlock) /* numlock depressed */ code = (shift1 || shift2 ? unm24[c] : m24[c]); } code &= BYTE; if (code < 0200 || code >= 0206) { /* Ordinary key, i.e. not shift, control, alt, etc. */ if (capslock) if (code >= 'A' && code <= 'Z') code += 'a' - 'A'; else if (code >= 'a' && code <= 'z') code -= 'a' - 'A'; if (alt) code |= 0200; /* alt key ORs 0200 into code */ if (control) code &= 037; if (code == 0) code = AT_SIGN; /* @ is 0100, so CTRL-@ = 0 */ if (make == 0) code = 0; /* key release */ return(code); } /* Table entries 0200 - 0206 denote special actions. */ switch(code - 0200) { case 0: shift1 = make; break; /* shift key on left */ case 1: shift2 = make; break; /* shift key on right */ case 2: control = make; break; /* control */ case 3: alt = make; break; /* alt key */ case 4: if (make && caps_off) capslock = 1 - capslock; caps_off = 1 - make; break; /* caps lock */ case 5: if (make && num_off) numlock = 1 - numlock; num_off = 1 - make; break; /* num lock */ } return(0); } #endif /*===========================================================================* * echo * *===========================================================================*/ PRIVATE echo(tp, c) register struct tty_struct *tp; /* terminal on which to echo */ register char c; /* character to echo */ { /* Echo a character on the terminal. */ if ( (tp->tty_mode & ECHO) == 0) return; /* if no echoing, don't echo */ if (c != MARKER) out_char(tp, c); flush(tp); /* force character out onto the screen */ } /*===========================================================================* * chuck * *===========================================================================*/ PRIVATE int chuck(tp) register struct tty_struct *tp; /* from which tty should chars be removed */ { /* Delete one character from the input queue. Used for erase and kill. */ char *prev; /* If input queue is empty, don't delete anything. */ if (tp->tty_incount == 0) return(-1); /* Don't delete '\n' or '\r'. */ prev = (tp->tty_inhead != tp->tty_inqueue ? tp->tty_inhead - 1 : &tp->tty_inqueue[TTY_IN_BYTES-1]); if (*prev == '\n' || *prev == '\r') return(-1); tp->tty_inhead = prev; tp->tty_incount--; return(OK); /* char erasure was possible */ } /*===========================================================================* * do_read * *===========================================================================*/ PRIVATE do_read(tp, m_ptr) register struct tty_struct *tp; /* pointer to tty struct */ message *m_ptr; /* pointer to message sent to the task */ { /* A process wants to read from a terminal. */ int code, caller; if (tp->tty_inleft > 0) { /* if someone else is hanging, give up */ tty_reply(TASK_REPLY,m_ptr->m_source,m_ptr->PROC_NR, E_TRY_AGAIN,0L,0L); return; } /* Copy information from the message to the tty struct. */ tp->tty_incaller = m_ptr->m_source; tp->tty_inproc = m_ptr->PROC_NR; tp->tty_in_vir = m_ptr->ADDRESS; tp->tty_inleft = m_ptr->COUNT; /* Try to get chars. This call either gets enough, or gets nothing. */ code = rd_chars(tp); caller = (int) tp->tty_inproc; tty_reply(TASK_REPLY, m_ptr->m_source, caller, code, 0L, 0L); } /*===========================================================================* * rd_chars * *===========================================================================*/ PRIVATE int rd_chars(tp) register struct tty_struct *tp; /* pointer to terminal to read from */ { /* A process wants to read from a terminal. First check if enough data is * available. If so, pass it to the user. If not, send FS a message telling * it to suspend the user. When enough data arrives later, the tty driver * copies it to the user space directly and notifies FS with a message. */ int cooked, ct, user_ct, buf_ct, cum, enough, eot_seen; vir_bytes in_vir, left; phys_bytes user_phys, tty_phys; char ch, *tty_ptr; struct proc *rp; extern phys_bytes umap(); cooked = ( (tp->tty_mode & (RAW | CBREAK)) ? 0 : 1); /* 1 iff COOKED mode */ if (tp->tty_incount == 0 || (cooked && tp->tty_lfct == 0)) return(SUSPEND); rp = proc_addr(tp->tty_inproc); in_vir = (vir_bytes) tp-> tty_in_vir; left = (vir_bytes) tp->tty_inleft; if ( (user_phys = umap(rp, D, in_vir, left)) == 0) return(E_BAD_ADDR); tty_phys = umap(proc_addr(TTY), D, (vir_bytes) tty_buf, TTY_BUF_SIZE); cum = 0; enough = 0; eot_seen = 0; /* The outer loop iterates on buffers, one buffer load per iteration. */ while (tp->tty_inleft > 0) { buf_ct = MIN(tp->tty_inleft, tp->tty_incount); buf_ct = MIN(buf_ct, TTY_BUF_SIZE); ct = 0; tty_ptr = tty_buf; /* The inner loop fills one buffer. */ while(buf_ct-- > 0) { ch = *tp->tty_intail++; if (tp->tty_intail == &tp->tty_inqueue[TTY_IN_BYTES]) tp->tty_intail = tp->tty_inqueue; *tty_ptr++ = ch; ct++; if (ch == '\n' || ch == MARKER) { tp->tty_lfct--; if (cooked && ch == MARKER) eot_seen++; enough++; /* exit loop */ if (cooked) break; /* only provide 1 line */ } } /* Copy one buffer to user space. Be careful about CTRL-D. In cooked * mode it is not transmitted to user programs, and is not counted as * a character as far as the count goes, but it does occupy space in * the driver's tables and must be counted there. */ user_ct = (eot_seen ? ct - 1 : ct); /* bytes to copy to user */ phys_copy(tty_phys, user_phys, (phys_bytes) user_ct); user_phys += user_ct; cum += user_ct; tp->tty_inleft -= ct; tp->tty_incount -= ct; if (tp->tty_incount == 0 || enough) break; } tp->tty_inleft = 0; return(cum); } /*===========================================================================* * finish * *===========================================================================*/ PRIVATE finish(tp, code) register struct tty_struct *tp; /* pointer to tty struct */ int code; /* reply code */ { /* A command has terminated (possibly due to DEL). Tell caller. */ int replyee, caller; tp->tty_rwords = 0; tp->tty_outleft = 0; if (tp->tty_waiting == NOT_WAITING) return; replyee = (int) tp->tty_otcaller; caller = (int) tp->tty_outproc; tty_reply(TASK_REPLY, replyee, caller, code, 0L, 0L); tp->tty_waiting = NOT_WAITING; } /*===========================================================================* * do_write * *===========================================================================*/ PRIVATE do_write(tp, m_ptr) register struct tty_struct *tp; /* pointer to tty struct */ message *m_ptr; /* pointer to message sent to the task */ { /* A process wants to write on a terminal. */ vir_bytes out_vir, out_left; struct proc *rp; extern phys_bytes umap(); /* Copy message parameters to the tty structure. */ tp->tty_otcaller = m_ptr->m_source; tp->tty_outproc = m_ptr->PROC_NR; tp->tty_out_vir = m_ptr->ADDRESS; tp->tty_outleft = m_ptr->COUNT; tp->tty_waiting = WAITING; tp->tty_cum = 0; /* Compute the physical address where the data is in user space. */ rp = proc_addr(tp->tty_outproc); out_vir = (vir_bytes) tp->tty_out_vir; out_left = (vir_bytes) tp->tty_outleft; if ( (tp->tty_phys = umap(rp, D, out_vir, out_left)) == 0) { /* Buffer address provided by user is outside its address space. */ tp->tty_cum = E_BAD_ADDR; tp->tty_outleft = 0; } /* Copy characters from the user process to the terminal. */ (*tp->tty_devstart)(tp); /* copy data to queue and start I/O */ } /*===========================================================================* * do_ioctl * *===========================================================================*/ PRIVATE do_ioctl(tp, m_ptr) register struct tty_struct *tp; /* pointer to tty_struct */ message *m_ptr; /* pointer to message sent to task */ { /* Perform IOCTL on this terminal. */ long flags, erki, erase, kill, intr, quit, xon, xoff, eof; int r; r = OK; flags = 0; erki = 0; switch(m_ptr->TTY_REQUEST) { case TIOCSETP: /* Set erase, kill, and flags. */ tp->tty_erase = (char) ((m_ptr->TTY_SPEK >> 8) & BYTE); /* erase */ tp->tty_kill = (char) ((m_ptr->TTY_SPEK >> 0) & BYTE); /* kill */ tp->tty_mode = (int) m_ptr->TTY_FLAGS; /* mode word */ break; case TIOCSETC: /* Set intr, quit, xon, xoff, eof (brk not used). */ tp->tty_intr = (char) ((m_ptr->TTY_SPEK >> 24) & BYTE); /* interrupt */ tp->tty_quit = (char) ((m_ptr->TTY_SPEK >> 16) & BYTE); /* quit */ tp->tty_xon = (char) ((m_ptr->TTY_SPEK >> 8) & BYTE); /* CTRL-S */ tp->tty_xoff = (char) ((m_ptr->TTY_SPEK >> 0) & BYTE); /* CTRL-Q */ tp->tty_eof = (char) ((m_ptr->TTY_FLAGS >> 8) & BYTE); /* CTRL-D */ break; case TIOCGETP: /* Get erase, kill, and flags. */ erase = ((long) tp->tty_erase) & BYTE; kill = ((long) tp->tty_kill) & BYTE; erki = (erase << 8) | kill; flags = (long) tp->tty_mode; break; case TIOCGETC: /* Get intr, quit, xon, xoff, eof. */ intr = ((long) tp->tty_intr) & BYTE; quit = ((long) tp->tty_quit) & BYTE; xon = ((long) tp->tty_xon) & BYTE; xoff = ((long) tp->tty_xoff) & BYTE; eof = ((long) tp->tty_eof) & BYTE; erki = (intr << 24) | (quit << 16) | (xon << 8) | (xoff << 0); flags = (eof <<8); break; default: r = EINVAL; } /* Send the reply. */ tty_reply(TASK_REPLY, m_ptr->m_source, m_ptr->PROC_NR, r, flags, erki); } /*===========================================================================* * do_cancel * *===========================================================================*/ PRIVATE do_cancel(tp, m_ptr) register struct tty_struct *tp; /* pointer to tty_struct */ message *m_ptr; /* pointer to message sent to task */ { /* A signal has been sent to a process that is hanging trying to read or write. * The pending read or write must be finished off immediately. */ /* First check to see if the process is indeed hanging. If it is not, don't * reply (to avoid race conditions). */ if (tp->tty_inleft == 0 && tp->tty_outleft == 0) return; /* Kill off input and output. */ tp->tty_inhead = tp->tty_inqueue; /* discard all input */ tp->tty_intail = tp->tty_inqueue; tp->tty_incount = 0; tp->tty_lfct = 0; tp->tty_inleft = 0; tp->tty_outleft = 0; tp->tty_waiting = NOT_WAITING; /* don't send reply */ tp->tty_inhibited = RUNNING; tty_reply(TASK_REPLY, m_ptr->m_source, m_ptr->PROC_NR, EINTR, 0L, 0L); } /*===========================================================================* * tty_reply * *===========================================================================*/ PRIVATE tty_reply(code, replyee, proc_nr, status, extra, other) int code; /* TASK_REPLY or REVIVE */ int replyee; /* destination address for the reply */ int proc_nr; /* to whom should the reply go? */ int status; /* reply code */ long extra; /* extra value */ long other; /* used for IOCTL replies */ { /* Send a reply to a process that wanted to read or write data. */ message tty_mess; tty_mess.m_type = code; tty_mess.REP_PROC_NR = proc_nr; tty_mess.REP_STATUS = status; tty_mess.TTY_FLAGS = extra; /* used by IOCTL for flags (mode) */ tty_mess.TTY_SPEK = other; /* used by IOCTL for erase and kill chars */ send(replyee, &tty_mess); } /*===========================================================================* * sigchar * *===========================================================================*/ PRIVATE sigchar(tp, line, sig) register struct tty_struct *tp; /* pointer to tty_struct */ int line; /* line on which signal arrived */ int sig; /* SIGINT, SIGQUIT, or SIGKILL */ { /* Process a SIGINT, SIGQUIT or SIGKILL char from the keyboard */ tp->tty_inhibited = RUNNING; /* do implied CRTL-Q */ finish(tp, EINTR); /* send reply */ tp->tty_inhead = tp->tty_inqueue; /* discard input */ tp->tty_intail = tp->tty_inqueue; tp->tty_incount = 0; tp->tty_lfct = 0; cause_sig(LOW_USER + 1 + line, sig); } /*****************************************************************************/ /*****************************************************************************/ /*****************************************************************************/ /*****************************************************************************/ /*****************************************************************************/ #ifdef i8088 /* Now begins the code and data for the device-dependent tty drivers. */ /* Definitions used by the console driver. */ #define COLOR_BASE 0xB800 /* video ram paragraph for color display */ #define MONO_BASE 0xB000 /* video ram address for mono display */ #define C_VID_MASK 0x3FFF /* mask for 16K video RAM */ #define M_VID_MASK 0x0FFF /* mask for 4K video RAM */ #define C_RETRACE 0x0300 /* how many characters to display at once */ #define M_RETRACE 0x7000 /* how many characters to display at once */ #define WORD_MASK 0xFFFF /* mask for 16 bits */ #define OFF_MASK 0x000F /* mask for 4 bits */ #define BEEP_FREQ 0x0533 /* value to put into timer to set beep freq */ #define B_TIME 0x2000 /* how long to sound the CTRL-G beep tone */ #define BLANK 0x0700 /* determines cursor color on blank screen */ #define LINE_WIDTH 80 /* # characters on a line */ #define SCR_LINES 25 /* # lines on the screen */ #define CTRL_S 31 /* scan code for letter S (for CRTL-S) */ #define MONOCHROME 1 /* value for tty_ioport tells color vs. mono */ #define CONSOLE 0 /* line number for console */ #define GO_FORWARD 0 /* scroll forward */ #define GO_BACKWARD 1 /* scroll backward */ #define TIMER2 0x42 /* I/O port for timer channel 2 */ #define TIMER3 0x43 /* I/O port for timer channel 3 */ #define KEYBD 0x60 /* I/O port for keyboard data */ #define PORT_B 0x61 /* I/O port for 8255 port B */ #define KBIT 0x80 /* bit used to ack characters to keyboard */ /* Constants relating to the video RAM and 6845. */ #define M_6845 0x3B0 /* port for 6845 mono */ #define C_6845 0x3D0 /* port for 6845 color */ #define EGA 0x3C0 /* port for EGA card */ #define INDEX 4 /* 6845's index register */ #define DATA 5 /* 6845's data register */ #define CUR_SIZE 10 /* 6845's cursor size register */ #define VID_ORG 12 /* 6845's origin register */ #define CURSOR 14 /* 6845's cursor register */ /* Definitions used for determining if the keyboard is IBM or Olivetti type. */ #define KB_STATUS 0x64 /* Olivetti keyboard status port */ #define BYTE_AVAIL 0x01 /* there is something in KEYBD port */ #define KB_BUSY 0x02 /* KEYBD port ready to accept a command */ #define DELUXE 0x01 /* this bit is set up iff deluxe keyboard */ #define GET_TYPE 5 /* command to get keyboard type */ #define OLIVETTI_EQUAL 12 /* the '=' key is 12 on olivetti, 13 on IBM */ /* Global variables used by the console driver. */ PUBLIC message keybd_mess; /* message used for console input chars */ PRIVATE vid_retrace; /* how many characters to display per burst */ PRIVATE unsigned vid_base; /* base of video ram (0xB000 or 0xB800) */ PUBLIC int vid_mask; /* 037777 for color (16K) or 07777 for mono */ PRIVATE int vid_port; /* I/O port for accessing 6845 */ /*===========================================================================* * keyboard * *===========================================================================*/ PUBLIC keyboard() { /* A keyboard interrupt has occurred. Process it. */ int val, code, k, raw_bit; char stopc; /* Fetch the character from the keyboard hardware and acknowledge it. */ port_in(KEYBD, &code); /* get the scan code for the key struck */ port_in(PORT_B, &val); /* strobe the keyboard to ack the char */ port_out(PORT_B, val | KBIT); /* strobe the bit high */ port_out(PORT_B, val); /* now strobe it low */ /* The IBM keyboard interrupts twice per key, once when depressed, once when * released. Filter out the latter, ignoring all but the shift-type keys. * The shift-type keys, 29, 42, 54, 56, 58, and 69 must be processed normally. */ k = code - 0200; /* codes > 0200 mean key release */ if (k > 0) { /* A key has been released. */ if (k != 29 && k != 42 && k != 54 && k != 56 && k != 58 && k != 69) { port_out(INT_CTL, ENABLE); /* re-enable interrupts */ return; /* don't call tty_task() */ } } else { /* Check to see if character is CTRL-S, to stop output. Setting xoff * to anything other than CTRL-S will not be detected here, but will * be detected later, in the driver. A general routine to detect any * xoff character here would be complicated since we only have the * scan code here, not the ASCII character. */ raw_bit = tty_struct[CONSOLE].tty_mode & RAW; stopc = tty_struct[CONSOLE].tty_xoff; if (raw_bit == 0 && control && code == CTRL_S && stopc == XOFF_CHAR) { tty_struct[CONSOLE].tty_inhibited = STOPPED; port_out(INT_CTL, ENABLE); return; } } /* Check for CTRL-ALT-DEL, and if found, reboot the computer. */ if (control && alt && code == DEL_CODE) reboot(); /* CTRL-ALT-DEL */ /* Store the character in memory so the task can get at it later. */ if ( (k = tty_driver_buf[0]) < tty_driver_buf[1]) { /* There is room to store this character; do it. */ k = k + k; /* each entry contains two bytes */ tty_driver_buf[k+2] = code; /* store the scan code */ tty_driver_buf[k+3] = CONSOLE; /* tell which line it came from */ tty_driver_buf[0]++; /* increment counter */ /* Build and send the interrupt message. */ keybd_mess.m_type = TTY_CHAR_INT; keybd_mess.ADDRESS = tty_driver_buf; interrupt(TTY, &keybd_mess); /* send a message to the tty task */ } else { /* Too many characters have been buffered. Discard excess. */ port_out(INT_CTL, ENABLE); /* re-enable 8259A controller */ } } /*===========================================================================* * console * *===========================================================================*/ PRIVATE console(tp) register struct tty_struct *tp; /* tells which terminal is to be used */ { /* Copy as much data as possible to the output queue, then start I/O. On * memory-mapped terminals, such as the IBM console, the I/O will also be * finished, and the counts updated. Keep repeating until all I/O done. */ int count; char c; unsigned segment, offset, offset1; /* Loop over the user bytes one at a time, outputting each one. */ segment = (tp->tty_phys >> 4) & WORD_MASK; offset = tp->tty_phys & OFF_MASK; offset1 = offset; count = 0; while (tp->tty_outleft > 0 && tp->tty_inhibited == RUNNING) { c = get_byte(segment, offset); /* fetch 1 byte from user space */ out_char(tp, c); /* write 1 byte to terminal */ offset++; /* advance one character in user buffer */ tp->tty_outleft--; /* decrement count */ } flush(tp); /* clear out the pending characters */ /* Update terminal data structure. */ count = offset - offset1; /* # characters printed */ tp->tty_phys += count; /* advance physical data pointer */ tp->tty_cum += count; /* number of characters printed */ /* If all data has been copied to the terminal, send the reply. */ if (tp->tty_outleft == 0) finish(tp, tp->tty_cum); } /*===========================================================================* * out_char * *===========================================================================*/ PRIVATE out_char(tp, c) register struct tty_struct *tp; /* pointer to tty struct */ char c; /* character to be output */ { /* Output a character on the console. Check for escape sequences, including * ESC 32+x 32+y to move cursor to (x, y) * ESC ~ 0 to clear from cursor to end of screen * ESC ~ 1 to reverse scroll the screen 1 line * ESC z x to set the attribute byte to x (z is a literal here) */ /* Check to see if we are part way through an escape sequence. */ if (tp->tty_esc_state == 1) { tp->tty_echar = c; tp->tty_esc_state = 2; return; } if (tp->tty_esc_state == 2) { escape(tp, tp->tty_echar, c); tp->tty_esc_state = 0; return; } switch(c) { case 007: /* ring the bell */ flush(tp); /* print any chars queued for output */ beep(BEEP_FREQ);/* BEEP_FREQ gives bell tone */ return; case 013: /* CTRL-K */ move_to(tp, tp->tty_column, tp->tty_row + 1); return; case 014: /* CTRL-L */ move_to(tp, tp->tty_column + 1, tp->tty_row); return; case 016: /* CTRL-N */ move_to(tp, tp->tty_column + 1, tp->tty_row); return; case '\b': /* backspace */ move_to(tp, tp->tty_column - 1, tp->tty_row); return; case '\n': /* line feed */ if (tp->tty_mode & CRMOD) out_char(tp, '\r'); if (tp->tty_row == 0) scroll_screen(tp, GO_FORWARD); else tp->tty_row--; move_to(tp, tp->tty_column, tp->tty_row); return; case '\r': /* carriage return */ move_to(tp, 0, tp->tty_row); return; case '\t': /* tab */ if ( (tp->tty_mode & XTABS) == XTABS) { do { out_char(tp, ' '); } while (tp->tty_column & TAB_MASK); return; } /* Ignore tab is XTABS is off--video RAM has no hardware tab */ return; case 033: /* ESC - start of an escape sequence */ flush(tp); /* print any chars queued for output */ tp->tty_esc_state = 1; /* mark ESC as seen */ return; default: /* printable chars are stored in ramqueue */ if (tp->tty_column >= LINE_WIDTH) return; /* long line */ if (tp->tty_rwords == TTY_RAM_WORDS) flush(tp); tp->tty_ramqueue[tp->tty_rwords++] = tp->tty_attribute | c; tp->tty_column++; /* next column */ return; } } /*===========================================================================* * scroll_screen * *===========================================================================*/ PRIVATE scroll_screen(tp, dir) register struct tty_struct *tp; /* pointer to tty struct */ int dir; /* GO_FORWARD or GO_BACKWARD */ { int amount, offset; amount = (dir == GO_FORWARD ? 2 * LINE_WIDTH : -2 * LINE_WIDTH); tp->tty_org = (tp->tty_org + amount) & vid_mask; if (dir == GO_FORWARD) offset = (tp->tty_org + 2 * (SCR_LINES - 1) * LINE_WIDTH) & vid_mask; else offset = tp->tty_org; /* Blank the new line at top or bottom. */ vid_copy(NIL_PTR, vid_base, offset, LINE_WIDTH); set_6845(VID_ORG, tp->tty_org >> 1); /* 6845 thinks in words */ } /*===========================================================================* * flush * *===========================================================================*/ PRIVATE flush(tp) register struct tty_struct *tp; /* pointer to tty struct */ { /* Have the characters in 'ramqueue' transferred to the screen. */ if (tp->tty_rwords == 0) return; vid_copy(tp->tty_ramqueue, vid_base, tp->tty_vid, tp->tty_rwords); /* Update the video parameters and cursor. */ tp->tty_vid = (tp->tty_vid + 2 * tp->tty_rwords); set_6845(CURSOR, tp->tty_vid >> 1); /* cursor counts in words */ tp->tty_rwords = 0; } /*===========================================================================* * move_to * *===========================================================================*/ PRIVATE move_to(tp, x, y) struct tty_struct *tp; /* pointer to tty struct */ int x; /* column (0 <= x <= 79) */ int y; /* row (0 <= y <= 24, 0 at bottom) */ { /* Move the cursor to (x, y). */ flush(tp); /* flush any pending characters */ if (x < 0 || x >= LINE_WIDTH || y < 0 || y >= SCR_LINES) return; tp->tty_column = x; /* set x co-ordinate */ tp->tty_row = y; /* set y co-ordinate */ tp->tty_vid = (tp->tty_org + 2*(SCR_LINES-1-y)*LINE_WIDTH + 2*x); set_6845(CURSOR, tp->tty_vid >> 1); /* cursor counts in words */ } /*===========================================================================* * escape * *===========================================================================*/ PRIVATE escape(tp, x, y) register struct tty_struct *tp; /* pointer to tty struct */ char x; /* escape sequence is ESC x y; this is x */ char y; /* escape sequence is ESC x y; this is y */ { /* Handle an escape sequence. */ int n, ct, vx; /* Check for ESC z attribute - used to change attribute byte. */ if (x == 'z') { /* Set attribute byte */ tp->tty_attribute = y << 8; return; } /* Check for ESC ~ n - used for clear screen, reverse scroll. */ if (x == '~') { if (y == '0') { /* Clear from cursor to end of screen */ n = 2 * LINE_WIDTH * (tp->tty_row + 1) - 2 * tp->tty_column; vx = tp->tty_vid; while (n > 0) { ct = MIN(n, vid_retrace); vid_copy(NIL_PTR, vid_base, vx, ct/2); vx += ct; n -= ct; } } else if (y == '1') { /* Reverse scroll. */ scroll_screen(tp, GO_BACKWARD); } return; } /* Must be cursor movement (or invalid). */ move_to(tp, x - 32, y - 32); } /*===========================================================================* * set_6845 * *===========================================================================*/ PRIVATE set_6845(reg, val) int reg; /* which register pair to set */ int val; /* 16-bit value to set it to */ { /* Set a register pair inside the 6845. * Registers 10-11 control the format of the cursor (how high it is, etc). * Registers 12-13 tell the 6845 where in video ram to start (in WORDS) * Registers 14-15 tell the 6845 where to put the cursor (in WORDS) * * Note that registers 12-15 work in words, i.e. 0x0000 is the top left * character, but 0x0001 (not 0x0002) is the next character. This addressing * is different from the way the 8088 addresses the video ram, where 0x0002 * is the address of the next character. */ port_out(vid_port + INDEX, reg); /* set the index register */ port_out(vid_port + DATA, (val>>8) & BYTE); /* output high byte */ port_out(vid_port + INDEX, reg + 1); /* again */ port_out(vid_port + DATA, val&BYTE); /* output low byte */ } /*===========================================================================* * beep * *===========================================================================*/ PRIVATE beep(f) int f; /* this value determines beep frequency */ { /* Making a beeping sound on the speaker (output for CRTL-G). The beep is * kept short, because interrupts must be disabled during beeping, and it * is undesirable to keep them off too long. This routine works by turning * on the bits in port B of the 8255 chip that drive the speaker. */ int x, k; lock(); /* disable interrupts */ port_out(TIMER3,0xB6); /* set up timer channel 2 mode */ port_out(TIMER2, f&BYTE); /* load low-order bits of frequency in timer */ port_out(TIMER2,(f>>8)&BYTE); /* now high-order bits of frequency in timer */ port_in(PORT_B,&x); /* acquire status of port B */ port_out(PORT_B, x|3); /* turn bits 0 and 1 on to beep */ for (k = 0; k < B_TIME; k++); /* delay loop while beeper sounding */ port_out(PORT_B, x); /* restore port B the way it was */ unlock(); /* re-enable interrupts */ } /*===========================================================================* * tty_init * *===========================================================================*/ PRIVATE tty_init() { /* Initialize the tty tables. */ register struct tty_struct *tp; int i; phys_bytes phy1, phy2, vram; /* Tell the EGA card, if any, to simulate a 16K CGA card. */ port_out(EGA + INDEX, 4); /* register select */ port_out(EGA + DATA, 1); /* no extended memory to be used */ for (tp = &tty_struct[0]; tp < &tty_struct[NR_TTYS]; tp++) { tp->tty_inhead = tp->tty_inqueue; tp->tty_intail = tp->tty_inqueue; tp->tty_mode = CRMOD | XTABS | ECHO; tp->tty_devstart = console; tp->tty_erase = ERASE_CHAR; tp->tty_kill = KILL_CHAR; tp->tty_intr = INTR_CHAR; tp->tty_quit = QUIT_CHAR; tp->tty_xon = XON_CHAR; tp->tty_xoff = XOFF_CHAR; tp->tty_eof = EOT_CHAR; } tty_struct[0].tty_makebreak = TWO_INTS; /* tty 0 is console */ if (color) { vid_base = COLOR_BASE; vid_mask = C_VID_MASK; vid_port = C_6845; vid_retrace = C_RETRACE; } else { vid_base = MONO_BASE; vid_mask = M_VID_MASK; vid_port = M_6845; vid_retrace = M_RETRACE; } tty_struct[0].tty_attribute = BLANK; tty_driver_buf[1] = MAX_OVERRUN; /* set up limit on keyboard buffering */ set_6845(CUR_SIZE, 31); /* set cursor shape */ set_6845(VID_ORG, 0); /* use page 0 of video ram */ move_to(&tty_struct[0], 0, 0); /* move cursor to lower left corner */ /* Determine which keyboard type is attached. The bootstrap program asks * the user to type an '='. The scan codes for '=' differ depending on the * keyboard in use. */ if (scan_code == OLIVETTI_EQUAL) olivetti = TRUE; } /*===========================================================================* * putc * *===========================================================================*/ PUBLIC putc(c) char c; /* character to print */ { /* This procedure is used by the version of printf() that is linked with * the kernel itself. The one in the library sends a message to FS, which is * not what is needed for printing within the kernel. This version just queues * the character and starts the output. */ out_char(&tty_struct[0], c); } /*===========================================================================* * func_key * *===/* This file contains a driver for the WD winchester controller from * Western Digital (WX-2 and related controllers). * * Original code written by Adri Koppes. * Patches from Gary Oliver for use with the Western Digital WX-2. * Patches from Harry McGavran for robust operation on turbo clones. * Patches from Mike Mitchell for WX-2 auto configure operation. * * The driver supports two operations: read a block and * write a block. It accepts two messages, one for reading and one for * writing, both using message format m2 and with the same parameters: * * m_type DEVICE PROC_NR COUNT POSITION ADRRESS * ---------------------------------------------------------------- * | DISK_READ | device | proc nr | bytes | offset | buf ptr | * |------------+---------+---------+---------+---------+---------| * | DISK_WRITE | device | proc nr | bytes | offset | buf ptr | * ---------------------------------------------------------------- * * The file contains one entry point: * * winchester_task: main entry when system is brought up * */ #include "../h/const.h" #include "../h/type.h" #include "../h/callnr.h" #include "../h/com.h" #include "../h/error.h" #include "const.h" #include "type.h" #include "proc.h" #define AU TO_BIOS TRUE /* TRUE: use Western's autoconfig BIOS */ #define DEBUG FALSE /* TRUE: enable debug messages */ #define MONITOR TRUE /* TRUE: monitor performance of busy loops */ #define MAX_DRIVES 1 /* I/O Ports used by winchester disk task. */ #define WIN_DATA 0x320 /* winchester disk controller data register */ #define WIN_STATUS 0x321 /* winchester disk controller status register */ #define WST_REQ 0x001 /* Request bit */ #define WST_INPUT 0x002 /* Set if controller is writing to cpu */ #define WST_BUS 0x004 /* Command/status bit */ #define WST_BUSY 0x008 /* Busy */ #define WST_INTERRUPT 0x020 /* Interrupt generated ?? */ #define WIN_SELECT 0x322 /* winchester disk controller select port */ #define WIN_DMA 0x323 /* winchester disk controller dma register */ #define DMA_ADDR 0x006 /* port for low 16 bits of DMA address */ #define DMA_TOP 0x082 /* port for top 4 bits of 20-bit DMA addr */ #define DMA_COUNT 0x007 /* port for DMA count (count = bytes - 1) */ #define DMA_M2 0x00C /* DMA status port */ #define DMA_M1 0x00B /* DMA status port */ #define DMA_INIT 0x00A /* DMA init port */ /* Winchester disk controller command bytes. */ #define WIN_RECALIBRATE 0x01 /* command for the drive to recalibrate */ #define WIN_SENSE 0x03 /* command for the controller to get its status */ #define WIN_READ 0x08 /* command for the drive to read */ #define WIN_WRITE 0x0a /* command for the drive to write */ #define WIN_SPECIFY 0x0C /* command for the controller to accept params */ #define WIN_ECC_READ 0x0D /* command for the controller to read ecc length */ #define DMA_INT 3 /* Command with dma and interrupt */ #define INT 2 /* Command with interrupt, no dma */ #define NO_DMA_INT 0 /* Command without dma and interrupt */ /* DMA channel commands. */ #define DMA_READ 0x47 /* DMA read opcode */ #define DMA_WRITE 0x4B /* DMA write opcode */ /* Parameters for the disk drive. */ #define SECTOR_SIZE 512 /* physical sector size in bytes */ #define NR_SECTORS 0x11 /* number of sectors per track */ /* Error codes */ #define ERR -1 /* general error */ /* Miscellaneous. */ #define MAX_ERRORS 4 /* how often to try rd/wt before quitting */ #define MAX_RESULTS 4 /* max number of bytes controller returns */ #define NR_DEVICES 10 /* maximum number of drives */ #define MAX_WIN_RETRY 32000 /* max # times to try to output to WIN */ #define PART_TABLE 0x1C6 /* IBM partition table starts here in sect 0 */ #define DEV_PER_DRIVE 5 /* hd0 + hd1 + hd2 + hd3 + hd4 = 5 */ #if AUTO_BIOS #define AUTO_PARAM 0x1AD /* drive parameter table starts here in sect 0 */ #define AUTO_ENABLE 0x10 /* auto bios enabled bit from status reg */ /* some start up parameters in order to extract the drive parameter table */ /* from the winchester. these should not need changed. */ #define AUTO_CYLS 306 /* default number of cylinders */ #define AUTO_HEADS 4 /* default number of heads */ #define AUTO_RWC 307 /* default reduced write cylinder */ #define AUTO_WPC 307 /* default write precomp cylinder */ #define AUTO_ECC 11 /* default ecc burst */ #define AUTO_CTRL 5 /* default winchester stepping speed byte */ #endif /* Variables. */ PRIVATE struct wini { /* main drive struct, one entry per drive */ int wn_opcode; /* DISK_READ or DISK_WRITE */ int wn_procnr; /* which proc wanted this operation? */ int wn_drive; /* drive number addressed (<< 5) */ int wn_cylinder; /* cylinder number addressed */ int wn_sector; /* sector addressed */ int wn_head; /* head number addressed */ int wn_heads; /* maximum number of heads */ int wn_ctrl_byte; /* Control byte for COMMANDS (10-Apr-87 GO) */ long wn_low; /* lowest cylinder of partition */ long wn_size; /* size of partition in blocks */ int wn_count; /* byte count */ vir_bytes wn_address; /* user virtual address */ char wn_results[MAX_RESULTS]; /* the controller can give lots of output */ } wini[NR_DEVICES]; PRIVATE int w_need_reset = FALSE; /* set to 1 when controller must be reset */ PRIVATE int nr_drives; /* Number of drives */ PRIVATE message w_mess; /* message buffer for in and out */ PRIVATE int command[6]; /* Common command block */ PRIVATE unsigned char buf[BLOCK_SIZE]; /* Buffer used by the startup routine */ PRIVATE struct param { int nr_cyl; /* Number of cylinders */ int nr_heads; /* Number of heads */ int reduced_wr; /* First cylinder with reduced write current */ int wr_precomp; /* First cylinder with write precompensation */ int max_ecc; /* Maximum ECC burst length */ int ctrl_byte; /* Copied control-byte from bios tables */ } param0, param1; /*=========================================================================* * winchester_task * *=========================================================================*/ PUBLIC winchester_task() { /* Main program of the winchester disk driver task. */ int r, caller, proc_nr; /* First initialize the controller */ init_params(); /* Here is the main loop of the disk task. It waits for a message, carries * it out, and sends a reply. */ while (TRUE) { /* First wait for a request to read or write a disk block. */ receive(ANY, &w_mess); /* get a request to do some work */ if (w_mess.m_source < 0) { printf("winchester task got message from %d ", w_mess.m_source); continue; } caller = w_mess.m_source; proc_nr = w_mess.PROC_NR; /* Now carry out the work. */ switch(w_mess.m_type) { case DISK_READ: case DISK_WRITE: r = w_do_rdwt(&w_mess); break; default: r = EINVAL; break; } /* Finally, prepare and send the reply message. */ w_mess.m_type = TASK_REPLY; w_mess.REP_PROC_NR = proc_nr; w_mess.REP_STATUS = r; /* # of bytes transferred or error code */ send(caller, &w_mess); /* send reply to caller */ } } /*==========================================================================* * w_do_rdwt * *==========================================================================*/ PRIVATE int w_do_rdwt(m_ptr) message *m_ptr; /* pointer to read or write w_message */ { /* Carry out a read or write request from the disk. */ register struct wini *wn; int r, device, errors = 0; long sector; /* Decode the w_message parameters. */ device = m_ptr->DEVICE; if (device < 0 || device >= NR_DEVICES) return(EIO); if (m_ptr->COUNT != BLOCK_SIZE) return(EINVAL); wn = &wini[device]; /* 'wn' points to entry for this drive */ wn->wn_opcode = m_ptr->m_type; /* DISK_READ or DISK_WRITE */ if (m_ptr->POSITION % BLOCK_SIZE != 0) return(EINVAL); sector = m_ptr->POSITION/SECTOR_SIZE; if ((sector+BLOCK_SIZE/SECTOR_SIZE) > wn->wn_size) return(EOF); sector += wn->wn_low; wn->wn_cylinder = sector / (wn->wn_heads * NR_SECTORS); wn->wn_sector = (sector % NR_SECTORS); wn->wn_head = ¯ą˛ŗ´ĩšēģŧŊžŋĀÁÂ(sector % (wn->wn_heads * NR_SECTORS) )/NR_SECTORS; wn->wn_count = m_ptr->COUNT; wn->wn_address = (vir_bytes) m_ptr->ADDRESS; wn->wn_procnr = m_ptr->PROC_NR; /* This loop allows a failed operation to be repeated. */ while (errors <= MAX_ERRORS) { errors++; /* increment count once per loop cycle */ if (errors >= MAX_ERRORS) return(EIO); /* First check to see if a reset is needed. */ if (w_need_reset) w_reset(); /* Now set up the DMA chip. */ w_dma_setup(wn); /* Perform the transfer. */ r = w_transfer(wn); if (r == OK) break; /* if successful, exit loop */ } return(r == OK ? BLOCK_SIZE : EIO); } /*==========================================================================* * w_dma_setup * *==========================================================================*/ PRIVATE w_dma_setup(wn) struct wini *wn; /* pointer to the drive struct */ { /* The IBM PC can perform DMA operations by using the DMA chip. To use it, * the DMA (Direct Memory Access) chip is loaded with the 20-bit memory address * to by read from or written to, the byte count minus 1, and a read or write * opcode. This routine sets up the DMA chip. Note that the chip is not * capable of doing a DMA across a 64K boundary (e.g., you can't read a * 512-byte block starting at physical address 65520). */ int mode, low_addr, high_addr, top_addr, low_ct, high_ct, top_end; vir_bytes vir, ct; phys_bytes user_phys; extern phys_bytes umap(); mode = (wn->wn_opcode == DISK_READ ? DMA_READ : DMA_WRITE); vir = (vir_bytes) wn->wn_address; ct = (vir_bytes) wn->wn_count; user_phys = umap(proc_addr(wn->wn_procnr), D, vir, ct); low_addr = (int) user_phys & BYTE; high_addr = (int) (user_phys >> 8) & BYTE; top_addr = (int) (user_phys >> 16) & BYTE; low_ct = (int) (ct - 1) & BYTE; high_ct = (int) ( (ct - 1) >> 8) & BYTE; /* Check to see if the transfer will require the DMA address counter to * go from one 64K segment to another. If so, do not even start it, since * the hardware does not carry from bit 15 to bit 16 of the DMA address. * Also check for bad buffer address. These errors mean FS contains a bug. */ if (user_phys == 0) panic("FS gave winchester disk driver bad addr", (int) vir); top_end = (int) (((user_phys + ct - 1) >> 16) & BYTE); if (top_end != top_addr) panic("Trying to DMA across 64K boundary", top_addr); /* Now set up the DMA registers. */ lock(); port_out(DMA_M2, mode); /* set the DMA mode */ port_out(DMA_M1, mode); /* set it again */ port_out(DMA_ADDR, low_addr); /* output low-order 8 bits */ port_out(DMA_ADDR, high_addr);/* output next 8 bits */ port_out(DMA_TOP, top_addr); /* output highest 4 bits */ port_out(DMA_COUNT, low_ct); /* output low 8 bits of count - 1 */ port_out(DMA_COUNT, high_ct); /* output high 8 bits of count - 1 */ unlock(); } /*=========================================================================* * w_transfer * *=========================================================================*/ PRIVATE int w_transfer(wn) register struct wini *wn; /* pointer to the drive struct */ { /* The drive is now on the proper cylinder. Read or write 1 block. */ /* The command is issued by outputing 6 bytes to the controller chip. */ command[0] = (wn->wn_opcode == DISK_READ ? WIN_READ : WIN_WRITE); command[1] = wn->wn_head | wn->wn_drive; command[2] = (((wn->wn_cylinder & 0x0300) >> 2) | wn->wn_sector); command[3] = (wn->wn_cylinder & 0xFF); command[4] = BLOCK_SIZE/SECTOR_SIZE; command[5] = wn->wn_ctrl_byte; if (com_out(DMA_INT) != OK) return(ERR); port_out(DMA_INIT, 3); /* initialize DMA */ /* Block, waiting for disk interrupt. */ w_wait_int(); /* Get controller status and check for errors. */ if (win_results(wn) == OK) return(OK); if ((wn->wn_results[0] & 63) == 24) read_ecc(); else w_need_reset = TRUE; return(ERR); } /*===========================================================================* * win_results * *===========================================================================*/ PRIVATE int win_results(wn) register struct wini *wn; /* pointer to the drive struct */ { /* Extract results from the controller after an operation. */ register int i; int status; port_in(WIN_DATA, &status); port_out(WIN_DMA, 0); if (!(status & 2)) /* Test "error" bit */ return(OK); command[0] = WIN_SENSE; command[1] = wn->wn_drive; if (com_out(NO_DMA_INT) != OK) return(ERR); /* Loop, extracting bytes from WIN */ for (i = 0; i < MAX_RESULTS; i++) { if (hd_wait(WST_REQ) != OK) return(ERR); port_in(WIN_DATA, &status); wn->wn_results[i] = status & BYTE; } if(hd_wait(WST_REQ) != OK) /* Missing from */ return (ERR); /* Original. 11-Apr-87 G.O. */ port_in(WIN_DATA, &status); /* Read "error" flag */ if(((status & 2) != 0) || (wn->wn_results[0] & 0x3F)) { return(ERR); } else return(OK); } /*===========================================================================* * win_out * *===========================================================================*/ PRIVATE win_out(val) int val; /* write this byte to winchester disk controller */ { /* Output a byte to the controller. This is not entirely trivial, since you * can only write to it when it is listening, and it decides when to listen. * If the controller refuses to listen, the WIN chip is given a hard reset. */ int r; if (w_need_reset) return; /* if controller is not listening, return */ do { port_in(WIN_STATUS, &r); } while((r & (WST_REQ | WST_BUSY)) == WST_BUSY); port_out(WIN_DATA, val); } /*===========================================================================* * w_reset * *===========================================================================*/ PRIVATE w_reset() { /* Issue a reset to the controller. This is done after any catastrophe, * like the controller refusing to respond. */ int r = 0, i; /* Strobe reset bit low. */ port_out(WIN_STATUS, 0); for(i = MAX_WIN_RETRY/10; i; --i) ; /* Spin loop for a while */ port_out(WIN_SELECT, 0); /* Issue select pulse */ for (i = 0; i < MAX_WIN_RETRY; i++) { port_in(WIN_STATUS, &r); if(r & 0x30) /* What is 10? 20 = INTERRUPT */ return (ERR); if((r & (WST_BUSY | WST_BUS | WST_REQ)) == (WST_BUSY | WST_BUS | WST_REQ)) break; } if (i == MAX_WIN_RETRY) { printf("Hard disk won't reset, status = %x\n", r); return(ERR); } /* Reset succeeded. Tell WIN drive parameters. */ w_need_reset = FALSE; if(win_specify(0, ¶m0) != OK) return (ERR); if ((nr_drives > 1) && (win_specify(1, ¶m1) != OK)) return(ERR); for (i=0; i 10) { /* Some arbitrary limit below which we don't really care */ if(i == MAX_WIN_RETRY) printf("wini: timeout waiting for INTERRUPT status\n"); else printf("wini: %d loops waiting for INTERRUPT status\n", i); } #endif /* MONITOR */ } /*============================================================================* * win_specify * *============================================================================*/ PRIVATE win_specify(drive, paramp) int drive; struct param *paramp; { command[0] = WIN_SPECIFY; /* Specify some parameters */ command[1] = drive << 5; /* Drive number */ if (com_out(NO_DMA_INT) != OK) /* Output command block */ return(ERR); lock(); /* No. of cylinders (high byte) */ win_out(paramp->nr_cyl >> 8); /* No. of cylinders (low byte) */ win_out(paramp->nr_cyl); /* No. of heads */ win_out(paramp->nr_heads); /* Start reduced write (high byte) */ win_out(paramp->reduced_wr >> 8); /* Start reduced write (low byte) */ win_out(paramp->reduced_wr); /* Start write precompensation (high byte) */ win_out(paramp->wr_precomp >> 8); /* Start write precompensation (low byte) */ win_out(paramp->wr_precomp); /* Ecc burst length */ win_out(paramp->max_ecc); unlock(); if (check_init() != OK) { /* See if controller accepted parameters */ w_need_reset = TRUE; return(ERR); } else return(OK); } /*============================================================================* * check_init * *============================================================================*/ PRIVATE check_init() { /* Routine to check if controller accepted the parameters */ int r, s; if (hd_wait(WST_REQ | WST_INPUT) == OK) { port_in(WIN_DATA, &r); do { port_in(WIN_STATUS, &s); } while(s & WST_BUSY); /* Loop while still busy */ if (r & 2) /* Test error bit */ { return(ERR); } else return(OK); } else { return (ERR); /* Missing from original: 11-Apr-87 G.O. */ } } /*============================================================================* * read_ecc * *============================================================================*/ PRIVATE read_ecc() { /* Read the ecc burst-length and let the controller correct the data */ int r; command[0] = WIN_ECC_READ; if (com_out(NO_DMA_INT) == OK && hd_wait(WST_REQ) == OK) { port_in(WIN_DATA, &r); if (hd_wait(WST_REQ) == OK) { port_in(WIN_DATA, &r); if (r & 1) w_need_reset = TRUE; } } return(ERR); } /*============================================================================* * hd_wait * *============================================================================*/ PRIVATE hd_wait(bits) register int bits; { /* Wait until the controller is ready to receive a command or send status */ register int i = 0; int r; do { port_in(WIN_STATUS, &r); r &= bits; } while ((i++ < MAX_WIN_RETRY) && r != bits); /* Wait for ALL bits */ if (i >= MAX_WIN_RETRY) { w_need_reset = TRUE; return(ERR); } else return(OK); } /*============================================================================* * com_out * *============================================================================*/ PRIVATE com_out(mode) int mode; { /* Output the command block to the winchester controller and return status */ register int i; int r; port_out(WIN_DMA, mode); port_out(WIN_SELECT, mode); for (i=0; i MAX_DRIVES ? MAX_DRIVES : (int) *buf; /* Read the switches from the controller */ port_in(WIN_SELECT, &i); #if AUTO_BIOS /* Get the drive parameters from sector zero of the drive if the */ /* autoconfig mode of the controller has been selected */ if(i & AUTO_ENABLE) { /* set up some phoney parameters so that we can read the first sector */ /* from the winchester. all drives will have one cylinder and one head */ /* but set up initially to the mini scribe drives from ibm */ param1.nr_cyl = param0.nr_cyl = AUTO_CYLS; param1.nr_heads = param0.nr_heads = AUTO_HEADS; param1.reduced_wr = param0.reduced_wr = AUTO_RWC; param1.wr_precomp = param0.wr_precomp = AUTO_WPC; param1.max_ecc = param0.max_ecc = AUTO_ECC; param1.ctrl_byte = param0.ctrl_byte = AUTO_CTRL; wini[DEV_PER_DRIVE].wn_heads = wini[0].wn_heads = param0.nr_heads; wini[DEV_PER_DRIVE].wn_low = wini[0].wn_low = 0L; wini[DEV_PER_DRIVE].wn_size = wini[0].wn_size = (long)AUTO_CYLS * (long)AUTO_HEADS * (long)NR_SECTORS; if(w_reset() != OK) panic("cannot setup for reading winchester parameter tables",0); if (nr_drives > 1) { /* generate the request to read the first sector from the winchester */ w_mess.DEVICE = DEV_PER_DRIVE; w_mess.POSITION = 0L; w_mess.COUNT = BLOCK_SIZE; w_mess.ADDRESS = (char *) buf; w_mess.PROC_NR = WINCHESTER; w_mess.m_type = DISK_READ; if(w_do_rdwt(&w_mess) != BLOCK_SIZE) panic("cannot read drive parameters from winchester",DEV_PER_DRIVE); /* copy the parameter tables into the structures for later use */ copy_param(&buf[AUTO_PARAM], ¶m1); } /* generate the request to read the first sector from the winchester */ w_mess.DEVICE = 0; w_mess.POSITION = 0L; w_mess.COUNT = BLOCK_SIZE; w_mess.ADDRESS = (char *) buf; w_mess.PROC_NR = WINCHESTER; w_mess.m_type = DISK_READ; if(w_do_rdwt(&w_mess) != BLOCK_SIZE) panic("cannot read drive parameters from winchester", 0); /* copy the parameter tables into the structures for later use */ copy_param(&buf[AUTO_PARAM], ¶m0); /* whoever compiled the kernel wanted the auto bios code included. if it * turns out that the tables should be read from the rom, then handle * this case the regular way */ } else { #endif /* Calculate the drive types */ type_0 = i & 3; type_1 = (i >> 2) & 3; /* Copy the parameter vector from the saved vector table */ offset = vec_table[2 * 0x41]; segment = vec_table[2 * 0x41 + 1]; /* Calculate the address off the parameters and copy them to buf */ address = ((phys_bytes)segment << 4) + offset; phys_copy(address, umap(proc_addr(WINCHESTER), D, buf, 64), 64L); /* Copy the parameters to the structures */ copy_param(&buf[type_0 * 16], ¶m0); copy_param(&buf[type_1 * 16], ¶m1); #if AUTO_BIOS /* close up the code to be executed when the controller has not been * set up to for auto configuration */ } #endif /* Set the parameters in the drive structure */ for (i = 0; i < DEV_PER_DRIVE; i++) { wini[i].wn_heads = param0.nr_heads; wini[i].wn_ctrl_byte = param0.ctrl_byte; wini[i].wn_drive = 0 << 5; /* Set drive number */ } wini[0].wn_low = wini[DEV_PER_DRIVE].wn_low = 0L; wini[0].wn_size = (long)((long)param0.nr_cyl * (long)param0.nr_heads * (long)NR_SECTORS); for (i = DEV_PER_DRIVE; i < (2*DEV_PER_DRIVE); i++) { wini[i].wn_heads = param1.nr_heads; wini[i].wn_ctrl_byte = param1.ctrl_byte; wini[i].wn_drive = 1 << 5; /* Set drive number */ } wini[DEV_PER_DRIVE].wn_size = (long)((long)param1.nr_cyl * (long)param1.nr_heads * (long)NR_SECTORS); /* Initialize the controller */ if ((nr_drives > 0) && (w_reset() != OK)) nr_drives = 0; /* Read the partition table for each drive and save them */ for (i = 0; i < nr_drives; i++) { w_mess.DEVICE = i * DEV_PER_DRIVE; w_mess.POSITION = 0L; w_mess.COUNT = BLOCK_SIZE; w_mess.ADDRESS = (char *) buf; w_mess.PROC_NR = WINCHESTER; w_mess.m_type = DISK_READ; if (w_do_rdwt(&w_mess) != BLOCK_SIZE) panic("Can't read partition table of winchester ", i); copy_prt(i * DEV_PER_DRIVE); } } /*==========================================================================* * copy_params * *==========================================================================*/ PRIVATE copy_params(src, dest) register unsigned char *src; register struct param *dest; { /* This routine copies the parameters from src to dest * and sets the parameters for partition 0 and DEV_PER_DRIVE */ dest->nr_cyl = *(int *)src; dest->nr_heads = (int)src[2]; dest->reduced_wr = *(int *)&src[3]; dest->wr_precomp = *(int *)&src[5]; dest->max_ecc = (int)src[7]; dest->ctrl_byte = (int)src[8]; } /*==========================================================================* * copy_prt * *==========================================================================*/ PRIVATE copy_prt(drive) int drive; { /* This routine copies the partition table for the selected drive to * the variables wn_low and wn_size */ register int i, offset; struct wini *wn; long adjust; for (i=0; i<4; i++) { adjust = 0; wn = &wini[i + drive + 1]; offset = PART_TABLE + i * 0x10; wn->wn_low = *(long *)&buf[offset]; if ((wn->wn_low % (BLOCK_SIZE/SECTOR_SIZE)) != 0) { adjust = wn->wn_low; wn->wn_low = (wn->wn_low/(BLOCK_SIZE/SECTOR_SIZE)+1)*(BLOCK_SIZE/SECTOR_SIZE); adjust = wn->wn_low - adjust; } wn->wn_size = *(long *)&buf[offset + sizeof(long)] - adjust; } sort(&wini[drive + 1]); } sort(wn) register struct wini *wn; { register int i,j; for (i=0; i<4; i++) for (j=0; j<3; j++) if ((wn[j].wn_low == 0) && (wn[j+1].wn_low != 0)) swap(&wn[j], &wn[j+1]); else if (wn[j].wn_low > wn[j+1].wn_low && wn[j+1].wn_low != 0) swap(&wn[j], &wn[j+1]); } swap(first, second) register struct wini *first, *second; { register struct wini tmp; tmp = *first; *first = *second; *second = tmp; } /* This file contains a driver for the IBM-AT winchester controller. * It was written by Adri Koppes. * * The driver supports two operations: read a block and * write a block. It accepts two messages, one for reading and one for * writing, both using message format m2 and with the same parameters: * * m_type DEVICE PROC_NR COUNT POSITION ADRRESS * ---------------------------------------------------------------- * | DISK_READ | device | proc nr | bytes | offset | buf ptr | * |------------+---------+---------+---------+---------+---------| * | DISK_WRITE | device | proc nr | bytes | offset | buf ptr | * ---------------------------------------------------------------- * * The file contains one entry point: * * winchester_task: main entry when system is brought up * */ #include "../h/const.h" #include "../h/type.h" #include "../h/callnr.h" #include "../h/com.h" #include "../h/error.h" #include "const.h" #include "type.h" #include "proc.h" /* I/O Ports used by winchester disk controller. */ #define WIN_REG1 0x1f0 #define WIN_REG2 0x1f1 #define WIN_REG3 0x1f2 #define WIN_REG4 0x1f3 #define WIN_REG5 0x1f4 #define WIN_REG6 0x1f5 #define WIN_REG7 0x1f6 #define WIN_REG8 0x1f7 #define WIN_REG9 0x3f6 /* Winchester disk controller command bytes. */ #define WIN_RECALIBRATE 0x10 /* command for the drive to recalibrate */ #define WIN_READ 0x20 /* command for the drive to read */ #define WIN_WRITE 0x30 /* command for the drive to write */ #define WIN_SPECIFY 0x91 /* command for the controller to accept params */ /* Parameters for the disk drive. */ #define SECTOR_SIZE 512 /* physical sector size in bytes */ /* Error codes */ #define ERR -1 /* general error */ /* Miscellaneous. */ #define MAX_ERRORS 4 /* how often to try rd/wt before quitting */ #define NR_DEVICES 10 /* maximum number of drives */ #define MAX_WIN_RETRY 10000 /* max # times to try to output to WIN */ #define PART_TABLE 0x1C6 /* IBM partition table starts here in sect 0 */ #define DEV_PER_DRIVE 5 /* hd0 + hd1 + hd2 + hd3 + hd4 = 5 */ /* Variables. */ PRIVATE struct wini { /* main drive struct, one entry per drive */ int wn_opcode; /* DISK_READ or DISK_WRITE */ int wn_procnr; /* which proc wanted this operation? */ int wn_drive; /* drive number addressed */ int wn_cylinder; /* cylinder number addressed */ int wn_sector; /* sector addressed */ int wn_head; /* head number addressed */ int wn_heads; /* maximum number of heads */ int wn_maxsec; /* maximum number of sectors per track */ int wn_ctlbyte; /* control byte (steprate) */ int wn_precomp; /* write precompensation cylinder / 4 */ long wn_low; /* lowest cylinder of partition */ long wn_size; /* size of partition in blocks */ int wn_count; /* byte count */ vir_bytes wn_address; /* user virtual address */ } wini[NR_DEVICES]; PRIVATE int w_need_reset = FALSE; /* set to 1 when controller must be reset */ PRIVATE int nr_drives; /* Number of drives */ PRIVATE message w_mess; /* message buffer for in and out */ PRIVATE int command[8]; /* Common command block */ PRIVATE unsigned char buf[BLOCK_SIZE]; /* Buffer used by the startup routine */ /*===========================================================================* * winchester_task * *===========================================================================*/ PUBLIC winchester_task() { /* Main program of the winchester disk driver task. */ int r, caller, proc_nr; /* First initialize the controller */ init_param(); /* Here is the main loop of the disk task. It waits for a message, carries * it out, and sends a reply. */ while (TRUE) { /* First wait for a request to read or write a disk block. */ receive(ANY, &w_mess); /* get a request to do some work */ if (w_mess.m_source < 0) { printf("winchester task got message from %d ", w_mess.m_source); continue; } caller = w_mess.m_source; proc_nr = w_mess.PROC_NR; /* Now carry out the work. */ switch(w_mess.m_type) { case DISK_READ: case DISK_WRITE: r = w_do_rdwt(&w_mess); break; default: r = EINVAL; break; } /* Finally, prepare and send the reply message. */ w_mess.m_type = TASK_REPLY; w_mess.REP_PROC_NR = proc_nr; w_mess.REP_STATUS = r; /* # of bytes transferred or error code */ send(caller, &w_mess); /* send reply to caller */ } } /*===========================================================================* * w_do_rdwt * *===========================================================================*/ PRIVATE int w_do_rdwt(m_ptr) message *m_ptr; /* pointer to read or write w_message */ { /* Carry out a read or write request from the disk. */ register struct wini *wn; int r, device, errors = 0; long sector; /* Decode the w_message parameters. */ device = m_ptr->DEVICE; if (device < 0 || device >= NR_DEVICES) return(EIO); if (m_ptr->COUNT != BLOCK_SIZE) return(EINVAL); wn = &wini[device]; /* 'wn' points to entry for this drive */ wn->wn_drive = device/DEV_PER_DRIVE; /* save drive number */ if (wn->wn_drive >= nr_drives) return(EIO); wn->wn_opcode = m_ptr->m_type; /* DISK_READ or DISK_WRITE */ if (m_ptr->POSITION % BLOCK_SIZE != 0) return(EINVAL); sector = m_ptr->POSITION/SECTOR_SIZE; if ((sector+BLOCK_SIZE/SECTOR_SIZE) > wn->wn_size) return(EOF); sector += wn->wn_low; wn->wn_cylinder = sector / (wn->wn_heads * wn->wn_maxsec); wn->wn_sector = (sector % wn->wn_maxsec) + 1; wn->wn_head = (sector % (wn->wn_heads * wn->wn_maxsec) )/wn->wn_maxsec; wn->wn_count = m_ptr->COUNT; wn->wn_address = (vir_bytes) m_ptr->ADDRESS; wn->wn_procnr = m_ptr->PROC_NR; /* This loop allows a failed operation to be repeated. */ while (errors <= MAX_ERRORS) { errors++; /* increment count once per loop cycle */ if (errors > MAX_ERRORS) return(EIO); /* First check to see if a reset is needed. */ if (w_need_reset) w_reset(); /* Perform the transfer. */ r = w_transfer(wn); if (r == OK) break; /* if successful, exit loop */ } return(r == OK ? BLOCK_SIZE : EIO); } /*===========================================================================* * w_transfer * *===========================================================================*/ PRIVATE int w_transfer(wn) register struct wini *wn; /* pointer to the drive struct */ { extern phys_bytes umap(); phys_bytes usr_buf = umap(proc_addr(wn->wn_procnr), D, wn->wn_address, BLOCK_SIZE); register int i,j; int r = 0; /* The command is issued by outputing 7 bytes to the controller chip. */ if (usr_buf == (phys_bytes)0) return(ERR); command[0] = wn->wn_ctlbyte; command[1] = wn->wn_precomp; command[2] = BLOCK_SIZE/SECTOR_SIZE; command[3] = wn->wn_sector; command[4] = wn->wn_cylinder & 0xFF; command[5] = ((wn->wn_cylinder & 0x0300) >> 8); command[6] = (wn->wn_drive << 4) | wn->wn_head | 0xA0; command[7] = (wn->wn_opcode == DISK_READ ? WIN_READ : WIN_WRITE); if (com_out() != OK) return(ERR); /* Block, waiting for disk interrupt. */ if (wn->wn_opcode == DISK_READ) { for (i=0; i> 4), (unsigned)(usr_buf & 0x0F)); unlock(); usr_buf += 0x200; if (win_results() != OK) { w_need_reset = TRUE; return(ERR); } } r = OK; } else { for (i=0; i> 4), (unsigned)(usr_buf & 0x0F)); unlock(); usr_buf += 0x200; receive(HARDWARE, &w_mess); if (win_results() != OK) { w_need_reset = TRUE; return(ERR); } } r = OK; } if (r == ERR) w_need_reset = TRUE; return(r); } /*===========================================================================* * w_reset * *===========================================================================*/ PRIVATE w_reset() { /* Issue a reset to the ĘĖÍÎĪĐŅŌĶcontroller. This is done after any catastrophe, * like the controller refusing to respond. */ int i, r; /* Strobe reset bit low. */ lock(); port_out(WIN_REG9, 4); for (i = 0; i < 10; i++) ; port_out(WIN_REG9, wini[0].wn_ctlbyte & 0x0F); unlock(); for (i = 0; i < MAX_WIN_RETRY && drive_busy(); i++) ; if (drive_busy()) { printf("Winchester wouldn't reset, drive busy\n"); return(ERR); } port_in(WIN_REG2, &r); if (r != 1) { printf("Winchester wouldn't reset, drive error\n"); return(ERR); } /* Reset succeeded. Tell WIN drive parameters. */ w_need_reset = FALSE; return(win_init()); } /*===========================================================================* * win_init * *===========================================================================*/ PRIVATE win_init() { /* Routine to initialize the drive parameters after boot or reset */ register int i; command[0] = wini[0].wn_ctlbyte; command[1] = wini[0].wn_precomp; command[2] = wini[0].wn_maxsec; command[4] = 0; command[6] = (wini[0].wn_heads - 1) | 0xA0; command[7] = WIN_SPECIFY; /* Specify some parameters */ if (com_out() != OK) /* Output command block */ return(ERR); receive(HARDWARE, &w_mess); if (win_results() != OK) { /* See if controller accepted parameters */ w_need_reset = TRUE; return(ERR); } if (nr_drives > 1) { command[0] = wini[5].wn_ctlbyte; command[1] = wini[5].wn_precomp; command[2] = wini[5].wn_maxsec; command[4] = 0; command[6] = (wini[5].wn_heads - 1) | 0xB0; command[7] = WIN_SPECIFY; /* Specify some parameters */ if (com_out() != OK) /* Output command block */ return(ERR); receive(HARDWARE, &w_mess); if (win_results() != OK) { /* See if controller accepted parameters */ w_need_reset = TRUE; return(ERR); } } for (i=0; i 0) && (win_init() != OK)) nr_drives = 0; /* Read the partition table for each drive and save them */ for (i = 0; i < nr_drives; i++) { w_mess.DEVICE = i * 5; w_mess.POSITION = 0L; w_mess.COUNT = BLOCK_SIZE; w_mess.ADDRESS = (char *) buf; w_mess.PROC_NR = WINCHESTER; w_mess.m_type = DISK_READ; if (w_do_rdwt(&w_mess) != BLOCK_SIZE) panic("Can't read partition table of winchester ", i); if (buf[510] != 0x55 || buf[511] != 0xAA) { printf("Invalid partition table\n"); continue; } copy_prt(i*5); } } /*============================================================================* * copy_params * *============================================================================*/ PRIVATE copy_params(src, dest) register unsigned char *src; register struct wini *dest; { /* This routine copies the parameters from src to dest * and sets the parameters for partition 0 and 5 */ register int i; long cyl, heads, sectors; for (i=0; i<5; i++) { dest[i].wn_heads = (int)src[2]; dest[i].wn_precomp = *(int *)&src[5] >> 2; dest[i].wn_ctlbyte = (int)src[8]; dest[i].wn_maxsec = (int)src[14]; } cyl = (long)(*(int *)src); heads = (long)dest[0].wn_heads; sectors = (long)dest[0].wn_maxsec; dest[0].wn_size = cyl * heads * sectors; } /*============================================================================* * copy_prt * *============================================================================*/ PRIVATE copy_prt(drive) int drive; { /* This routine copies the partition table for the selected drive to * the variables wn_low and wn_size */ register int i, offset; struct wini *wn; long adjust; for (i=0; i<4; i++) { adjust = 0; wn = &wini[i + drive + 1]; offset = PART_TABLE + i * 0x10; wn->wn_low = *(long *)&buf[offset]; if ((wn->wn_low % (BLOCK_SIZE/SECTOR_SIZE)) != 0) { adjust = wn->wn_low; wn->wn_low = (wn->wn_low/(BLOCK_SIZE/SECTOR_SIZE)+1)*(BLOCK_SIZE/SECTOR_SIZE); adjust = wn->wn_low - adjust; } wn->wn_size = *(long *)&buf[offset + sizeof(long)] - adjust; } sort(&wini[drive + 1]); } sort(wn) register struct wini *wn; { register int i,j; for (i=0; i<4; i++) for (j=0; j<3; j++) if ((wn[j].wn_low == 0) && (wn[j+1].wn_low != 0)) swap(&wn[j], &wn[j+1]); else if (wn[j].wn_low > wn[j+1].wn_low && wn[j+1].wn_low != 0) swap(&wn[j], &wn[j+1]); } swap(first, second) register struct wini *first, *second; { register struct wini tmp; tmp = *first; *first = *second; *second = tmp; } ...makefileklib88.smpx88.s# The kernel dir contains wd_wini.c, xt_wini.c and at_wini.c. Before running # make you must copy one of these to wini.c, depending on which controller you # have. If you do not have a hard disk, you MUST choose one of them at random. # On a PC, cpp and cem are in /lib and will be removed to make space while # linking the kernel. On an AT, they are in /usr/lib are are not removed. # This is because they have to be in /lib on a PC; the diskette is too small # for them to be in /usr/lib. CFLAGS= -Di8088 -w -F -T. h=../h l=/usr/lib obj = mpx88.s main.s tty.s floppy.s wini.s system.s proc.s clock.s memory.s \ printer.s table.s klib88.s dmp.s kernel: makefile $(obj) $l/libc.a @echo "Start linking Kernel." @echo "On a PC, /lib/* will be removed to make space on RAM disk" @echo "If linking fails due to lack of space on 2/1, rm xx_wini.c" @rm -f /lib/cem /lib/cpp /tmp/* @asld -o kernel $(obj) $l/libc.a $l/end.s @echo "Kernel done. On a PC, restore /lib/cem and /lib/cpp manually" clock.s: const.h type.h $h/const.h $h/type.h clock.s: $h/callnr.h clock.s: $h/com.h clock.s: $h/error.h clock.s: $h/signal.h clock.s: glo.h clock.s: proc.h floppy.s: const.h type.h $h/const.h $h/type.h floppy.s: $h/callnr.h floppy.s: $h/com.h floppy.s: $h/error.h floppy.s: glo.h floppy.s: proc.h dmp.s: const.h type.h $h/const.h $h/type.h dmp.s: $h/callnr.h dmp.s: $h/com.h dmp.s: $h/error.h dmp.s: glo.h dmp.s: proc.h main.s: const.h type.h $h/const.h $h/type.h main.s: $h/callnr.h main.s: $h/com.h main.s: $h/error.h main.s: glo.h main.s: proc.h memory.s: const.h type.h $h/const.h $h/type.h memory.s: $h/callnr.h memory.s: $h/com.h memory.s: $h/error.h memory.s: proc.h printer.s: const.h type.h $h/const.h $h/type.h printer.s: $h/callnr.h printer.s: $h/com.h printer.s: $h/error.h proc.s: const.h type.h $h/const.h $h/type.h proc.s: $h/callnr.h proc.s: $h/com.h proc.s: $h/error.h proc.s: glo.h proc.s: proc.h system.s: const.h type.h $h/const.h $h/type.h system.s: $h/callnr.h system.s: $h/com.h system.s: $h/error.h system.s: $h/signal.h system.s: glo.h system.s: proc.h table.s: const.h type.h $h/const.h $h/type.h table.s: glo.h table.s: proc.h tty.s: const.h type.h $h/const.h $h/type.h tty.s: $h/callnr.h tty.s: $h/com.h tty.s: $h/error.h tty.s: $h/sgtty.h tty.s: $h/signal.h tty.s: glo.h tty.s: proc.h wini.s: const.h type.h $h/const.h $h/type.h wini.s: $h/callnr.h wini.s: $h/com.h wini.s: $h/error.h wini.s: proc.h | This file contains a number of assembly code utility routines needed by the | kernel. They are: | | phys_copy: copies data from anywhere to anywhere in memory | cp_mess: copies messages from source to destination | port_out: outputs data on an I/O port | port_in: inputs data from an I/O port | lock: disable interrupts | unlock: enable interrupts | restore: restore interrupts (enable/disabled) as they were before lock() | build_sig: build 4 word structure pushed onto stack for signals | csv: procedure prolog to save the registers | cret: procedure epilog to restore the registers | get_chrome: returns 0 is display is monochrome, 1 if it is color | vid_copy: copy data to video ram (on color display during retrace only) | get_byte: reads a byte from a user program and returns it as value | reboot: reboot for CTRL-ALT-DEL | wreboot: wait for character then reboot | dma_read: transfer data between HD controller and memory | dma_write: transfer data between memory and HD controller | The following procedures are defined in this file and called from outside it. .globl _phys_copy, _cp_mess, _port_out, _port_in, _lock, _unlock, _restore .globl _build_sig, csv, cret, _get_chrome, _vid_copy, _get_byte, _reboot .globl _wreboot, _dma_read, _dma_write | The following external procedure is called in this file. .globl _panic | Variables and data structures .globl _color, _cur_proc, _proc_ptr, splimit, _vec_table, _vid_mask |*===========================================================================* |* phys_copy * |*===========================================================================* | This routine copies a block of physical memory. It is called by: | phys_copy( (long) source, (long) destination, (long) bytecount) _phys_copy: pushf | save flags cli | disable interrupts push bp | save the registers push ax | save ax push bx | save bx push cx | save cx push dx | save dx push si | save si push di | save di push ds | save ds push es | save es mov bp,sp | set bp to point to saved es L0: mov ax,28(bp) | ax = high-order word of 32-bit destination mov di,26(bp) | di = low-order word of 32-bit destination mov cx,*4 | start extracting click number from dest L1: rcr ax,*1 | click number is destination address / 16 rcr di,*1 | it is used in segment register for copy loop L1 | 4 bits of high-order word are used mov es,di | es = destination click mov ax,24(bp) | ax = high-order word of 32-bit source mov si,22(bp) | si = low-order word of 32-bit source mov cx,*4 | start extracting click number from source L2: rcr ax,*1 | click number is source address / 16 rcr si,*1 | it is used in segment register for copy loop L2 | 4 bits of high-order word are used mov ds,si | ds = source click mov di,26(bp) | di = low-order word of dest address and di,*0x000F | di = offset from paragraph # in es mov si,22(bp) | si = low-order word of source address and si,*0x000F | si = offset from paragraph # in ds mov dx,32(bp) | dx = high-order word of byte count mov cx,30(bp) | cx = low-order word of byte count test cx,#0x8000 | if bytes >= 32768, only do 32768 jnz L3 | per iteration test dx,#0xFFFF | check high-order 17 bits to see if bytes jnz L3 | if bytes >= 32768 then go to L3 jmp L4 | if bytes < 32768 then go to L4 L3: mov cx,#0x8000 | 0x8000 is unsigned 32768 L4: mov ax,cx | save actual count used in ax; needed later test cx,*0x0001 | should we copy a byte or a word at a time? jz L5 | jump if even rep | copy 1 byte at a time movb | byte copy jmp L6 | check for more bytes L5: shr cx,*1 | word copy rep | copy 1 word at a time movw | word copy L6: mov dx,32(bp) | decr count, incr src & dst, iterate if needed mov cx,30(bp) | dx || cx is 32-bit byte count xor bx,bx | bx || ax is 32-bit actual count used sub cx,ax | compute bytes - actual count sbb dx,bx | dx || cx is # bytes not yet processed or cx,cx | see if it is 0 jnz L7 | if more bytes then go to L7 or dx,dx | keep testing jnz L7 | if loop done, fall through pop es | restore all the saved registers pop ds | restore ds pop di | restore di pop si | restore si pop dx | restore dx pop cx | restore cx pop bx | restore bx pop ax | restore ax pop bp | restore bp popf | restore flags ret | return to caller L7: mov 32(bp),dx | store decremented byte count back in mem mov 30(bp),cx | as a long add 26(bp),ax | increment destination adc 28(bp),bx | carry from low-order word add 22(bp),ax | increment source adc 24(bp),bx | carry from low-order word jmp L0 | start next iteration |*===========================================================================* |* cp_mess * |*===========================================================================* | This routine is makes a fast copy of a message from anywhere in the address | space to anywhere else. It also copies the source address provided as a | parameter to the call into the first word of the destination message. | It is called by: | cp_mess(src, src_clicks, src_offset, dst_clicks, dst_offset) | where all 5 parameters are shorts (16-bits). | | Note that the message size, 'Msize' is in WORDS (not bytes) and must be set | correctly. Changing the definition of message the type file and not changing | it here will lead to total disaster. | This routine destroys ax. It preserves the other registers. Msize = 12 | size of a message in 16-bit words _cp_mess: push bp | save bp push es | save es push ds | save ds mov bp,sp | index off bp because machine can't use sp pushf | save flags cli | disable interrupts push cx | save cx push si | save si push di | save di mov ax,8(bp) | ax = process number of sender mov di,16(bp) | di = offset of destination buffer mov es,14(bp) | es = clicks of destination mov si,12(bp) | si = offset of source message mov ds,10(bp) | ds = clicks of source message seg es | segment override prefix mov (di),ax | copy sender's process number to dest message add si,*2 | don't copy first word add di,*2 | don't copy first word mov cx,*Msize-1 | remember, first word doesn't count rep | iterate cx times to copy 11 words movw | copy the message pop di | restore di pop si | restore si pop cx | restore cs popf | restore flags pop ds | restore ds pop es | restore es pop bp | restore bp ret | that's all folks! |*===========================================================================* |* port_out * |*===========================================================================* | port_out(port, value) writes 'value' on the I/O port 'port'. _port_out: push bx | save bx mov bx,sp | index off bx push ax | save ax push dx | save dx mov dx,4(bx) | dx = port mov ax,6(bx) | ax = value out | output 1 byte pop dx | restore dx pop ax | restore ax pop bx | restore bx ret | return to caller |*===========================================================================* |* port_in * |*===========================================================================* | port_in(port, &value) reads from port 'port' and puts the result in 'value'. _port_in: push bx | save bx mov bx,sp | index off bx push ax | save ax push dx | save dx mov dx,4(bx) | dx = port in | input 1 byte xorb ah,ah | clear ah mov bx,6(bx) | fetch address where byte is to go mov (bx),ax | return byte to caller in param pop dx | restore dx pop ax | restore ax pop bx | restore bx ret | return to caller |*===========================================================================* |* lock * |*===========================================================================* | Disable CPU interrupts. _lock: pushf | save flags on stack cli | disable interrupts pop lockvar | save flags for possible restoration later ret | return to caller |*===========================================================================* |* unlock * |*=================================ßáâãäåæįčéęë==========================================* | Enable CPU interrupts. _unlock: sti | enable interrupts ret | return to caller |*===========================================================================* |* restore * |*===========================================================================* | Restore enable/disable bit to the value it had before last lock. _restore: push lockvar | push flags as they were before previous lock popf | restore flags ret | return to caller |*===========================================================================* |* build_sig * |*===========================================================================* |* Build a structure that is pushed onto the stack for signals. It contains |* pc, psw, etc., and is machine dependent. The format is the same as generated |* by hardware interrupts, except that after the "interrupt", the signal number |* is also pushed. The signal processing routine within the user space first |* pops the signal number, to see which function to call. Then it calls the |* function. Finally, when the function returns to the low-level signal |* handling routine, control is passed back to where it was prior to the signal |* by executing a return-from-interrupt instruction, hence the need for using |* the hardware generated interrupt format on the stack. The call is: |* build_sig(sig_stuff, rp, sig) | Offsets within proc table PC = 24 csreg = 18 PSW = 28 _build_sig: push bp | save bp mov bp,sp | set bp to sp for accessing params push bx | save bx push si | save si mov bx,4(bp) | bx points to sig_stuff mov si,6(bp) | si points to proc table entry mov ax,8(bp) | ax = signal number mov (bx),ax | put signal number in sig_stuff mov ax,PC(si) | ax = signalled process' PC mov 2(bx),ax | put pc in sig_stuff mov ax,csreg(si) | ax = signalled process' cs mov 4(bx),ax | put cs in sig_stuff mov ax,PSW(si) | ax = signalled process' PSW mov 6(bx),ax | put psw in sig_stuff pop si | restore si pop bx | restore bx pop bp | restore bp ret | return to caller |*===========================================================================* |* csv & cret * |*===========================================================================* | This version of csv replaces the standard one. It checks for stack overflow | within the kernel in a simpler way than is usually done. cret is standard. csv: pop bx | bx = return address push bp | stack old frame pointer mov bp,sp | set new frame pointer to sp push di | save di push si | save si sub sp,ax | ax = # bytes of local variables cmp sp,splimit | has kernel stack grown too large jbe csv.1 | if sp is too low, panic jmp (bx) | normal return: copy bx to program counter csv.1: mov splimit,#0 | prevent call to panic from aborting in csv mov bx,_proc_ptr | update rp->p_splimit mov 50(bx),#0 | rp->sp_limit = 0 push _cur_proc | task number mov ax,#stkoverrun | stack overran the kernel stack area push ax | push first parameter call _panic | call is: panic(stkoverrun, cur_proc) jmp csv.1 | this should not be necessary cret: lea sp,*-4(bp) | set sp to point to saved si pop si | restore saved si pop di | restore saved di pop bp | restore bp ret | end of procedure |*===========================================================================* |* get_chrome * |*===========================================================================* | This routine calls the BIOS to find out if the display is monochrome or | color. The drivers are different, as are the video ram addresses, so we | need to know. _get_chrome: int 0x11 | call the BIOS to get equipment type andb al,#0x30 | isolate color/mono field cmpb al,*0x30 | 0x30 is monochrome je getchr1 | if monochrome then go to getchr1 mov ax,#1 | color = 1 ret | color return getchr1: xor ax,ax | mono = 0 ret | monochrome return |*===========================================================================* |* dma_read * |*===========================================================================* _dma_read: push bp mov bp,sp push cx push dx push di push es mov cx,#256 | transfer 256 words mov dx,#0x1F0 | from/to port 1f0 cld mov es,4(bp) | segment in es mov di,6(bp) | offset in di .byte 0xF3, 0x6D | opcode for 'rep insw' pop es pop di pop dx pop dx mov sp,bp pop bp ret |*===========================================================================* |* dma_write * |*===========================================================================* _dma_write: push bp mov bp,sp push cx push dx push si push ds mov cx,#256 | transfer 256 words mov dx,#0x1F0 | from/to port 1f0 cld mov ds,4(bp) | segment in ds mov si,6(bp) | offset in si .byte 0xF3, 0x6F | opcode for 'rep outsw' pop ds pop si pop dx pop dx mov sp,bp pop bp ret |*===========================================================================* |* vid_copy * |*===========================================================================* | This routine takes a string of (character, attribute) pairs and writes them | onto the screen. For a color display, the writing only takes places during | the vertical retrace interval, to avoid displaying garbage on the screen. | The call is: | vid_copy(buffer, videobase, offset, words) | where | 'buffer' is a pointer to the (character, attribute) pairs | 'videobase' is 0xB800 for color and 0xB000 for monochrome displays | 'offset' tells where within video ram to copy the data | 'words' tells how many words to copy | if buffer is zero, the fill char (BLANK) is used BLANK = 0x0700 | controls color of cursor on blank screen _vid_copy: push bp | we need bp to access the parameters mov bp,sp | set bp to sp for indexing push si | save the registers push di | save di push bx | save bx push cx | save cx push dx | save dx push es | save es vid.0: mov si,4(bp) | si = pointer to data to be copied mov di,8(bp) | di = offset within video ram and di,_vid_mask | only 4K or 16K counts mov cx,10(bp) | cx = word count for copy loop mov dx,#0x3DA | prepare to see if color display is retracing mov bx,di | see if copy will run off end of video ram add bx,cx | compute where copy ends add bx,cx | bx = last character copied + 1 sub bx,_vid_mask | bx = # characters beyond end of video ram sub bx,#1 | note: dec bx doesn't set flags properly jle vid.1 | jump if no overrun sar bx,#1 | bx = # words that don't fit in video ram sub cx,bx | reduce count by overrun mov tmp,cx | save actual count used for later vid.1: test _color,*1 | skip vertical retrace test if display is mono jz vid.4 | if monochrome then go to vid.2 |vid.2: in | with a color display, you can only copy to | test al,*010 | the video ram during vertical retrace, so | jnz vid.2 | wait for start of retrace period. Bit 3 of vid.3: in | 0x3DA is set during retrace. First wait testb al,*010 | until it is off (no retrace), then wait jz vid.3 | until it comes on (start of retrace) vid.4: pushf | copying may now start; save flags cli | interrupts just get in the way: disable them mov es,6(bp) | load es now: int routines may ruin it cmp si,#0 | si = 0 means blank the screen je vid.7 | jump for blanking lock | this is a trick for the IBM PC simulator only inc vidlock | 'lock' indicates a video ram access rep | this is the copy loop movw | ditto vid.5: popf | restore flags cmp bx,#0 | if bx < 0, then no overrun and we are done jle vid.6 | jump if everything fit mov 10(bp),bx | set up residual count mov 8(bp),#0 | start copying at base of video ram cmp 4(bp),#0 | NIL_PTR means store blanks je vid.0 | go do it mov si,tmp | si = count of words copied add si,si | si = count of bytes copied add 4(bp),si | increment buffer pointer jmp vid.0 | go copy some more vid.6: pop es | restore registers pop dx | restore dx pop cx | restore cx pop bx | restore bx pop di | restore di pop si | restore si pop bp | restore bp ret | return to caller vid.7: mov ax,#BLANK | ax = blanking character rep | copy loop stow | blank screen jmp vid.5 | done |*===========================================================================* |* get_byte * |*===========================================================================* | This routine is used to fetch a byte from anywhere in memory. | The call is: | c = get_byte(seg, off) | where | 'seg' is the value to put in es | 'off' is the offset from the es value _get_byte: push bp | save bp mov bp,sp | we need to access parameters push es | save es mov es,4(bp) | load es with segment value mov bx,6(bp) | load bx with offset from segment seg es | go get the byte movb al,(bx) | al = byte xorb ah,ah | ax = byte pop es | restore es pop bp | restore bp ret | return to caller |*===========================================================================* |* reboot & wreboot * |*===========================================================================* | This code reboots the PC _reboot: cli | disable interrupts mov ax,#0x20 | re-enable interrupt controller out 0x20 call resvec | restore the vectors in low core mov ax,#0x40 mov ds,ax mov ax,#0x1234 mov 0x72,ax mov ax,#0xFFFF mov ds,ax mov ax,3 push ax mov ax,1 push ax reti _wreboot: cli | disable interrupts mov ax,#0x20 | re-enable interrupt controller out 0x20 call resvec | restore the vectors in low core xor ax,ax | wait for character before continuing int 0x16 | get char mov ax,#0x40 mov ds,ax mov ax,#0x1234 mov 0x72,ax mov ax,#0xFFFF mov ds,ax mov ax,3 push ax mov ax,1 push ax reti | Restore the interrupt vectors in low core. resvec: cld mov cx,#2*71 mov si,#_vec_table xor di,di mov es,di rep movw ret | Some library routines use exit, so this label is needed. | Actual calls to exit cannot occur in the kernel. .globl _exit _exit: sti jmp _exit .data lockvar: .word 0 | place to store flags for lock()/restore() vidlock: .word 0 | dummy variable for use with lock prefix splimit: .word 0 | stack limit for current task (kernel only) tmp: .word 0 | count of bytes already copied stkoverrun: .asciz "Kernel stack overrun, task = " _vec_table: .zerow 142 | storage for interrupt vectors | This file is part of the lowest layer of the MINIX kernel. All processing | switching and message handling is done here and in file "proc.c". This file | is entered on every transition to the kernel, both for sending/receiving | messages and for all interrupts. In all cases, the trap or interrupt | routine first calls save() to store the machine state in the proc table. | Then the stack is switched to k_stack. Finally, the real trap or interrupt | handler (in C) is called. When it returns, the interrupt routine jumps to | restart, to run the process or task whose number is in 'cur_proc'. | | The external entry points into this file are: | s_call: process or task wants to send or receive a message | tty_int: interrupt routine for each key depression and release | lpr_int: interrupt routine for each line printer interrupt | disk_int: disk interrupt routine | wini_int: winchester interrupt routine | clock_int: clock interrupt routine (HZ times per second) | surprise: all other interrupts < 16 are vectored here | trp: all traps with vector >= 16 are vectored here | divide: divide overflow traps are vectored here | restart: start running a task or process | These symbols MUST agree with the values in ../h/com.h to avoid disaster. K_STACK_BYTES = 256 WINI = -6 FLOPPY = -5 CLOCK = -3 IDLE = -999 DISKINT = 1 CLOCK_TICK = 2 | The following procedures are defined in this file and called from outside it. .globl _tty_int, _lpr_int, _clock_int, _disk_int, _wini_int .globl _s_call, _surprise, _trp, _divide, _restart | The following external procedures are called in this file. .globl _main, _sys_call, _interrupt, _keyboard, _panic, _unexpected_int, _trap .globl _pr_char, _div_trap | Variables, data structures and miscellaneous. .globl _cur_proc, _proc_ptr, _scan_code, _int_mess, _k_stack, splimit .globl _sizes, begtext, begdata, begbss | The following constants are offsets into the proc table. esreg = 14 dsreg = 16 csreg = 18 ssreg = 20 SP = 22 PC = 24 PSW = 28 SPLIM = 50 OFF = 18 ROFF = 12 .text begtext: |*===========================================================================* |* MINIX * |*===========================================================================* MINIX: | this is the entry point for the MINIX kernel. jmp M.0 | skip over the next few bytes .word 0,0 | build puts DS at kernel text address 4 M.0: cli | disable interrupts mov ax,cs | set up segment registers mov ds,ax | set up ds mov ax,4 | build has loaded this word with ds value mov ds,ax | ds now contains proper value mov ss,ax | ss now contains proper value mov _scan_code,bx | save scan code for '=' key from bootstrap mov sp,#_k_stack | set sp to point to the top of the add sp,#K_STACK_BYTES | kernel stack call _main | start the main program of MINIX M.1: jmp M.1 | this should never be executed |*===========================================================================* |* s_call * |*===========================================================================* _s_call: | System calls are vectored here. call save | save the machine state mov bp,_proc_ptr | use bp to access sys call parameters push 2(bp) | push(pointer to user message) (was bx) push (bp) | push(src/dest) (was ax) push _cur_proc | push caller push 4(bp) | push(SEND/RECEIVE/BOTH) (was cx) call _sys_call | sys_call(function, caller, src_dest, m_ptr) jmp _restart | jump to code to restart proc/task running |*===========================================================================* |* tty_int * |*===========================================================================* _tty_int: | Interrupt routine for terminal input. call save | save the machine state call _keyboard | process a keyboard interrupt jmp _restart | continue execution |*===========================================================================* |* lpr_int * |*===========================================================================* _lpr_int: | Interrupt routine for terminal input. call save | save the machine state call _pr_char | process a line printer interrupt jmp _restart | continue execution |*===========================================================================* |* disk_int * |*===========================================================================* _disk_int: | Interrupt routine for the floppy disk. call save | save the machine state mov _int_mess+2,*DISKINT| build message for disk task mov ax,#_int_mess | prepare to call interrupt(FLOPPY, &intmess) push ax | push second parameter mov ax,*FLOPPY | prepare to push first parameter push ax | push first parameter call _interrupt | this is the call jmp _restart | continue execution |*===========================================================================* |* wini_int * |*===========================================================================* _wini_int: | Interrupt routine for the winchester disk. call save | save the machine state mov _int_mess+2,*DISKINT| build message for winchester task mov ax,#_int_mess | prepare to call interrupt(WINI, &intmess) push ax | push second parameter mov ax,*WINI | prepare to push first parameter push ax | push first parameter call _interrupt | this is the call jmp _restart | continue execution |*===========================================================================* |* clock_int * |*===========================================================================* _clock_int: | Interrupt routine for the clock. call save | save the machine state mov _int_mess+2,*CLOCK_TICK | build message for clock task mov ax,#_int_mess | prepare to call interrupt(CLOCK, &intmess) push ax | push second parameter mov ax,*CLOCK | prepare to push first parameter push ax | push first parameter call _interrupt | this is the call jmp _restart | continue execution |*===========================================================================* |* surprise * |*===========================================================================* _surprise: | This is where unexpected interrupts come. call save | save the machine state call _unexpected_int | go panic jmp _restart | never executed |*===========================================================================* |* trp * |*===========================================================================* _trp: | This is where unexpected traps come. call save | save the machine state call _trap | print a message jmp _restart | this error is not fatal |*===========================================================================* |* divide * |*===========================================================================* _divide: | This is where divide overflow traps come. call save | save the machine state call _div_trap | print a message jmp _restart | this error is not fatal |*===========================================================================* |* save * |*===========================================================================* save: | save the machine state in the proc table. push ds | stack: psw/cs/pc/ret addr/ds push cs | prepare to restore ds pop ds | ds has now been set to cs mov ds,4 | word 4 in kernel text space contains ds value pop ds_save | stack: psw/cs/pc/ret addr pop ret_save | stack: psw/cs/pc mov bx_save,bx | save bx for later ; we need a free register mov bx,_proc_ptr | start save set up; make bx point to save area add bx,*OFF | bx points to place to store cs pop PC-OFF(bx) | store pc in proc table pop csreg-OFF(bx) | store cs in proc table pop PSW-OFF(bx) | store psw mov ssreg-OFF(bx),ss | store ss mov SP-OFF(bx),sp | sp as it was prior to interrupt mov sp,bx | now use sp to point into proc table/task save mov bx,ds | about to set ss mov ss,bx | set ss push ds_save | start saving all the registers, sp first push es | save es between sp and bp mov es,bx | es now references kernel memory toķõö÷o push bp | save bp push di | save di push si | save si push dx | save dx push cx | save cx push bx_save | save original bx push ax | all registers now saved mov sp,#_k_stack | temporary stack for interrupts add sp,#K_STACK_BYTES | set sp to top of temporary stack mov splimit,#_k_stack | limit for temporary stack add splimit,#8 | splimit checks for stack overflow mov ax,ret_save | ax = address to return to jmp (ax) | return to caller; Note: sp points to saved ax |*===========================================================================* |* restart * |*===========================================================================* _restart: | This routine sets up and runs a proc or task. cmp _cur_proc,#IDLE | restart user; if cur_proc = IDLE, go idle je idle | no user is runnable, jump to idle routine cli | disable interrupts mov sp,_proc_ptr | return to user, fetch regs from proc table pop ax | start restoring registers pop bx | restore bx pop cx | restore cx pop dx | restore dx pop si | restore si pop di | restore di mov lds_low,bx | lds_low contains bx mov bx,sp | bx points to saved bp register mov bp,SPLIM-ROFF(bx) | splimit = p_splimit mov splimit,bp | ditto mov bp,dsreg-ROFF(bx) | bp = ds mov lds_low+2,bp | lds_low+2 contains ds pop bp | restore bp pop es | restore es mov sp,SP-ROFF(bx) | restore sp mov ss,ssreg-ROFF(bx) | restore ss using the value of ds push PSW-ROFF(bx) | push psw push csreg-ROFF(bx) | push cs push PC-ROFF(bx) | push pc lds bx,lds_low | restore ds and bx in one fell swoop iret | return to user or task |*===========================================================================* |* idle * |*===========================================================================* idle: | executed when there is no work sti | enable interrupts L3: wait | just idle while waiting for interrupt jmp L3 | loop until interrupt |*===========================================================================* |* data * |*===========================================================================* .data begdata: _sizes: .word 0x526F | this must be the first data entry (magic #) .zerow 7 | build table uses prev word and this space bx_save: .word 0 | storage for bx ds_save: .word 0 | storage for ds ret_save:.word 0 | storage for return address lds_low: .word 0,0 | storage used for restoring bx ttyomess: .asciz "RS232 interrupt" .bss begbss: ...makefileklib88.smpx88.s# The kernel directory contains files xt_wini.c and at_wini.c. Before running # make you must copy one of these to wini.c, depending on whether you have a # PC or an AT. You must do this even if you do not have a hard disk.. CFLAGS = -Di8088 -O h=../h l=../lib obj = mpx88.o main.o tty.o floppy.o wini.o system.o proc.o clock.o memory.o \ printer.o table.o klib88.o dmp.o kernel: makefile $(obj) $l/libc.a @echo "Start linking Kernel" @ld -o kernel $(obj) $l/libc.a $l/end.o @echo "Kernel done" klib88.o: klib88.s as -o klib88.o klib88.s mpx88.o: mpx88.s as -o mpx88.o mpx88.s clock.o: const.h type.h $h/const.h $h/type.h clock.o: $h/callnr.h clock.o: $h/com.h clock.o: $h/error.h clock.o: $h/signal.h clock.o: glo.h clock.o: proc.h floppy.o: const.h type.h $h/const.h $h/type.h floppy.o: $h/callnr.h floppy.o: $h/com.h floppy.o: $h/error.h floppy.o: glo.h floppy.o: proc.h dmp.o: const.h type.h $h/const.h $h/type.h dmp.o: $h/callnr.h dmp.o: $h/com.h dmp.o: $h/error.h dmp.o: glo.h dmp.o: proc.h main.o: const.h type.h $h/const.h $h/type.h main.o: $h/callnr.h main.o: $h/com.h main.o: $h/error.h main.o: glo.h main.o: proc.h memory.o: const.h type.h $h/const.h $h/type.h memory.o: $h/callnr.h memory.o: $h/com.h memory.o: $h/error.h memory.o: proc.h printer.o: const.h type.h $h/const.h $h/type.h printer.o: $h/callnr.h printer.o: $h/com.h printer.o: $h/error.h proc.o: const.h type.h $h/const.h $h/type.h proc.o: $h/callnr.h proc.o: $h/com.h proc.o: $h/error.h proc.o: glo.h proc.o: proc.h system.o: const.h type.h $h/const.h $h/type.h system.o: $h/callnr.h system.o: $h/com.h system.o: $h/error.h system.o: $h/signal.h system.o: glo.h system.o: proc.h table.o: const.h type.h $h/const.h $h/type.h table.o: glo.h table.o: proc.h tty.o: const.h type.h $h/const.h $h/type.h tty.o: $h/callnr.h tty.o: $h/com.h tty.o: $h/error.h tty.o: $h/sgtty.h tty.o: $h/signal.h tty.o: glo.h tty.o: proc.h wini.o: const.h type.h $h/const.h $h/type.h wini.o: $h/callnr.h wini.o: $h/com.h wini.o: $h/error.h wini.o: proc.h ...make.bat_link.batlinklistklib88.asmmpx88.asmecho off echo/ if "%1"=="-l" _link.bat echo Making Kernel echo/ echo To re-make the kernel after you changed a module, echo just delete the .OBJ file for that module. echo Specifying a '-l' switch will only link the kernel echo (e.g. after a manual compilation of a module). echo NOTE: do not forget the "-di8088" on a manual compilation. echo/ if exist MPX88.OBJ goto a1 echo assembling: mpx88 masm mpx88,,nul,nul >mpx88.lst :a1 if exist KLIB88.OBJ goto cc echo assembling: klib88 masm klib88,,nul,nul >klib88.lst :cc if exist CLOCK.OBJ goto fout echo compiling: clock cc1 -di8088 clock >clock.lst if errorlevel 1 goto fout cc2 clock >>clock.lst if errorlevel 1 goto fout cc3 clock>>clock.lst if errorlevel 1 goto fout cc4 clock>>clock.lst :fout if exist DMP.OBJ goto fout2 echo compiling: dmp cc1 -di8088 dmp>dmp.lst if errorlevel 1 goto fout2 cc2 dmp>>dmp.lst if errorlevel 1 goto fout2 cc3 dmp>>dmp.lst if errorlevel 1 goto fout2 cc4 dmp>>dmp.lst :fout2 if exist FLOPPY.OBJ goto fout3 echo compiling: floppy cc1 -di8088 floppy>floppy.lst if errorlevel 1 goto fout3 cc2 floppy >>floppy.lst if errorlevel 1 goto fout3 cc3 floppy >>floppy.lst if errorlevel 1 goto fout3 cc4 floppy >>floppy.lst :fout3 if exist MAIN.OBJ goto fout4 echo compiling: main cc1 -di8088 main >main.lst if errorlevel 1 goto fout4 cc2 main >>main.lst if errorlevel 1 goto fout4 cc3 main >>main.lst if errorlevel 1 goto fout4 cc4 main >>main.lst :fout4 if exist MEMORY.OBJ goto fout5 echo compiling: memory cc1 -di8088 memory >memory.lst if errorlevel 1 goto fout5 cc2 memory >>memory.lst if errorlevel 1 goto fout5 cc3 memory >>memory.lst if errorlevel 1 goto fout5 cc4 memory >>memory.lst :fout5 if exist PRINTER.OBJ goto fout6 echo compiling: printer cc1 -di8088 printer >printer.lst if errorlevel 1 goto fout6 cc2 printer >>printer.lst if errorlevel 1 goto fout6 cc3 printer >>printer.lst if errorlevel 1 goto fout6 cc4 printer >>printer.lst :fout6 if exist PROC.OBJ goto fout7 echo compiling: proc cc1 -di8088 proc >proc.lst if errorlevel 1 goto fout7 cc2 proc >>proc.lst if errorlevel 1 goto fout7 cc3 proc >>proc.lst if errorlevel 1 goto fout7 cc4 proc >>proc.lst :fout7 if exist SYSTEM.OBJ goto fout8 echo compiling: system cc1 -di8088 system >system.lst if errorlevel 1 goto fout8 cc2 system >>system.lst if errorlevel 1 goto fout8 cc3 system >>system.lst if errorlevel 1 goto fout8 cc4 system >>system.lst :fout8 if exist TABLE.OBJ goto fout9 echo compiling: table cc1 -di8088 table >table.lst if errorlevel 1 goto fout9 cc2 table >>table.lst if errorlevel 1 goto fout9 cc3 table >>table.lst if errorlevel 1 goto fout9 cc4 table >>table.lst :fout9 if exist TTY.OBJ goto fout10 echo compiling: tty cc1 -di8088 tty >tty.lst if errorlevel 1 goto fout10 cc2 tty >>tty.lst if errorlevel 1 goto fout10 cc3 tty >>tty.lst if errorlevel 1 goto fout10 cc4 tty >>tty.lst :fout10 if exist WINI.OBJ goto fout11 echo compiling: wini cc1 -di8088 wini >wini.lst if errorlevel 1 goto fout11 cc2 wini >>wini.lst if errorlevel 1 goto fout11 cc3 wini >>wini.lst if errorlevel 1 goto fout11 cc4 wini >>wini.lst :fout11 _link.bat echo/ echo Linking kernel link/m mpx88+ < linklist >kernel.lst dos2out -d kernel >> kernel.lst echo/ echo Kernel done. Check the .LST-files for errors pause echo on for %%f in (*.lst) do type %%f main+proc+system+clock+memory+floppy+ wini+tty+printer+table+klib88+dmp kernel kernel ..\lib\mxc86 title klib88 page,132 ; This file contains a number of assembly code utility routines needed by the ; kernel. They are: ; ; phys_copy: copies data from anywhere to anywhere in memory ; cp_mess: copies messages from source to destination ; port_out: outputs data on an I/O port ; port_in: inputs data from an I/O port ; lock: disable interrupts ; unlock: enable interrupts ; restore: restore interrupts [enable/disabled] as they were before lock[] ; build_sig: build 4 word structure pushed onto stack for signals ; csv: procedure prolog to save the registers ; cret: procedure epilog to restore the registers ; get_chrome: returns 0 is display is monochrome, 1 if it is color ; vid_copy: copy data to video ram [on color display during retrace only] ; get_byte: reads a byte from a user program and returns it as value ; The following procedures are defined in this file and called from outside it. PUBLIC phys_cop, cp_mess, port_out, port_in, lock, unlock, restore PUBLIC build_si, csv, cret, get_chro, vid_copy, get_byte PUBLIC reboot, wreboot ; The following external procedure is called in this file. EXTRN panic:NEAR ; Variables and data structures EXTRN color:WORD, cur_proc:WORD, proc_ptr:WORD, splimit:WORD PUBLIC vec_tabl INCLUDE ..\lib\prologue.h @CODE SEGMENT assume cs:@code,ds:dgroup ;=========================================================================== ; phys_copy ;=========================================================================== ; This routine copies a block of physical memory. It is called by: ; phys_copy( (long) source, (long) destination, (long) bytecount) phys_cop: pushf ; save flags cli ; disable interrupts push bp ; save the registers push ax ; save ax push bx ; save bx push cx ; save cx push dx ; save dx push si ; save si push di ; save di push ds ; save ds push es ; save es mov bp,sp ; set bp to point to saved es L0: mov ax,28[bp] ; ax = high-order word of 32-bit destination mov di,26[bp] ; di = low-order word of 32-bit destination mov cx,4 ; start extracting click number from dest L1: rcr ax,1 ; click number is destination address / 16 rcr di,1 ; it is used in segment register for copy loop L1 ; 4 bits of high-order word are used mov es,di ; es = destination click mov ax,24[bp] ; ax = high-order word of 32-bit source mov si,22[bp] ; si = low-order word of 32-bit source mov cx,4 ; start extracting click number from source L2: rcr ax,1 ; click number is source address / 16 rcr si,1 ; it is used in segment register for copy loop L2 ; 4 bits of high-order word are used mov ds,si ; ds = source click mov di,26[bp] ; di = low-order word of dest address and di,000Fh ; di = offset from paragraph in es mov si,22[bp] ; si = low-order word of source address and si,000Fh ; si = offset from paragraph in ds mov dx,32[bp] ; dx = high-order word of byte count mov cx,30[bp] ; cx = low-order word of byte count test cx,8000h ; if bytes >= 32768, only do 32768 jnz L3 ; per iteration test dx,0FFFFh ; check high-order 17 bits to see if bytes jnz L3 ; if bytes >= 32768 then go to L3 jmp short L4 ; if bytes < 32768 then go to L4 L3: mov cx,8000h ; 0x8000 is unsigned 32768 L4: mov ax,cx ; save actual count used in ax; needed later test cx,0001h ; should we copy a byte or a word at a time? jz L5 ; jump if even rep movsb ; copy 1 byte at a time jmp short L6 ; check for more bytes L5: shr cx,1 ; word copy rep movsw ; copy 1 word at a time L6: mov dx,32[bp] ; decr count, incr src & dst, iterate if needed mov cx,30[bp] ; dx || cx is 32-bit byte count xor bx,bx ; bx || ax is 32-bit actual count used sub cx,ax ; compute bytes - actual count sbb dx,bx ; dx ;; cx is bytes not yet processed or cx,cx ; see if it is 0 jnz L7 ; if more bytes then go to L7 or dx,dx ; keep testing jnz L7 ; if loop done, fall through pop es ; restore all the saved registers pop ds ; restore ds pop di ; restore di pop si ; restore si pop dx ; restore dx pop cx ; restore cx pop bx ; restore bx pop ax ; restore ax pop bp ; restore bp popf ; restore flags ret ; return to caller L7: mov 32[bp],dx ; store decremented byte count back in mem mov 30[bp],cx ; as a long add 26[bp],ax ; increment destination adc 28[bp],bx ; carry from low-order word add 22[bp],ax ; increment source adc 24[bp],bx ; carry from low-order word jmp L0 ; start next iteration ;=========================================================================== ; cp_mess ;=========================================================================== ; This routine is makes a fast copy of a message from anywhere in the address ; space to anywhere else. It also copies the source address provided as a ; parameter to the call into the first word of the destination message. ; It is called by: ; cp_mess[src, src_clicks, src_offset, dst_clicks, dst_offset] ; where all 5 parameters are shorts [16-bits]. ; ; Note that the message size, 'Msize' is in WORDS [not bytes] and must be set ; correctly. Changing the definition of message the type file and not changing ; it here will lead to total disaster. ; This routine destroys ax. It preserves the other registers. Msize = 12 ; size of a message in 16-bit words cp_mess: push bp ; save bp push es ; save es push ds ; save ds mov bp,sp ; index off bp because machine can't use sp pushf ; save flags cli ; disable interrupts push cx ; save cx push si ; save si push di ; save di mov ax,8[bp] ; ax = process number of sender mov di,16[bp] ; di = offset of destination buffer mov es,14[bp] ; es = clicks of destination mov si,12[bp] ; si = offset of source message mov ds,10[bp] ; ds = clicks of source message mov es:[di],ax ; copy sender's process number to dest message add si,2 ; don't copy first word add di,2 ; don't copy first word mov cx,Msize-1 ; remember, first word doesn't count rep movsw ; iterate cx times to copy the message pop di ; restore di pop si ; restore si pop cx ; restore cs popf ; restore flags pop ds ; restore ds pop es ; restore es pop bp ; restore bp ret ; that's all folks! ;=========================================================================== ; port_out ;=========================================================================== ; port_out(port, value) writes 'value' on the I/O port 'port'. port_out: push bx ; save bx mov bx,sp ; index off bx push ax ; save ax push dx ; save dx mov dx,4[bx] ; dx = port mov ax,6[bx] ; ax = value out dx,al ; output 1 byte pop dx ; restore dx pop ax ; restore ax pop bx ; restore bx ret ; return to caller ;=========================================================================== ; port_in ;=========================================================================== ; port_in(port, &value) reads from port 'port' and puts the result in 'value'. port_in: push bx ; save bx mov bx,sp ; index off bx push ax ; save ax push dx ; save dx mov dx,4[bx] ; dx = port in al,dx ; input 1 byte xor ah,ah ; clear ah mov bx,6[bx] ; fetch address where byte is to go mov [bx],ax ; return byte to caller in param pop dx ; restore dx pop ax ; restore ax pop bx ; restore bx ret ; return to caller ;=========================================================================== ; lock ;=========================================================================== ; Disable CPU interrupts. lock: pushf ; save flags on stack cli ; disable interrupts pop lockvar ; save flags for possible restoration later ret ; return to caller ;=========================================================================== ; unlock ;=========================================================================== ; Enable CPU interrupts. unlock: sti ; enable interrupts ret ; return to caller ;=========================================================================== ; restore ;=========================================================================== ; Restore enable/disable bit to   the value it had before last lock. restore: push lockvar ; push flags as they were before previous lock popf ; restore flags ret ; return to caller ;=========================================================================== ; build_sig ;=========================================================================== ; Build a structure that is pushed onto the stack for signals. It contains ; pc, psw, etc., and is machine dependent. The format is the same as generated ; by hardware interrupts, except that after the "interrupt", the signal number ; is also pushed. The signal processing routine within the user space first ; pops the signal number, to see which function to call. Then it calls the ; function. Finally, when the function returns to the low-level signal ; handling routine, control is passed back to where it was prior to the signal ; by executing a return-from-interrupt instruction, hence the need for using ; the hardware generated interrupt format on the stack. The call is: ; build_sig(sig_stuff, rp, sig) ; Offsets within proc table PC = 24 csreg = 18 PSW = 28 build_si: push bp ; save bp mov bp,sp ; set bp to sp for accessing params push bx ; save bx push si ; save si mov bx,4[bp] ; bx points to sig_stuff mov si,6[bp] ; si points to proc table entry mov ax,8[bp] ; ax = signal number mov [bx],ax ; put signal number in sig_stuff mov ax,PC[si] ; ax = signalled process' PC mov 2[bx],ax ; put pc in sig_stuff mov ax,csreg[si] ; ax = signalled process' cs mov 4[bx],ax ; put cs in sig_stuff mov ax,PSW[si] ; ax = signalled process' PSW mov 6[bx],ax ; put psw in sig_stuff pop si ; restore si pop bx ; restore bx pop bp ; restore bp ret ; return to caller ;=========================================================================== ; csv & cret (compiler generated symbols) ;=========================================================================== ; This version of csv replaces the standard one. It checks for stack overflow ; within the kernel in a simpler way than is usually done. cret is standard. csv: pop bx ; bx = return address push bp ; stack old frame pointer mov bp,sp ; set new frame pointer to sp push di ; save di push si ; save si sub sp,ax ; ax = bytes of local variables cmp sp,dgroup:splimit ; has kernel stack grown too large jbe csv1 ; if sp is too low, panic jmp [bx] ; normal return: copy bx to program counter csv1: mov dgroup:splimit,0 ; prevent call to panic from aborting in csv mov bx,dgroup:proc_ptr ; update rp->p_splimit mov WORD PTR 50[bx],0 ; rp->sp_limit = 0 push dgroup:cur_proc ; task number mov ax,offset dgroup:stkoverrun ; stack overran the kernel stack area push ax ; push first parameter call panic ; call is: panic(stkoverrun, cur_proc) jmp csv1 ; this should not be necessary cret: lea sp,-4[bp] ; set sp to point to saved si pop si ; restore saved si pop di ; restore saved di pop bp ; restore bp ret ; end of procedure ;=========================================================================== ; get_chrome ;=========================================================================== ; This routine calls the BIOS to find out if the display is monochrome or ; color. The drivers are different, as are the video ram addresses, so we ; need to know. get_chro: int 11h ; call the BIOS to get equipment type and al,30h ; isolate color/mono field cmp al,30h ; 0x30 is monochrome je getchr1 ; if monochrome then go to getchr1 mov ax,1 ; color = 1 ret ; color return getchr1:xor ax,ax ; mono = 0 ret ; monochrome return ;=========================================================================== ; vid_copy ;=========================================================================== ; This routine takes a string of [character, attribute] pairs and writes them ; onto the screen. For a color display, the writing only takes places during ; the vertical retrace interval, to avoid displaying garbage on the screen. ; The call is: ; vid_copy(buffer, videobase, offset, words) ; where ; 'buffer' is a pointer to the (character, attribute) pairs ; 'videobase' is 0xB800 for color and 0xB000 for monochrome displays ; 'offset' tells where within video ram to copy the data ; 'words' tells how many words to copy ; if buffer is zero, the fill character (BLANK) is used BLANK = 0700h vid_copy: push bp ; we need bp to access the parameters mov bp,sp ; set bp to sp for indexing push si ; save the registers push di ; save di push bx ; save bx push cx ; save cx push dx ; save dx push es ; save es vid0: mov si,4[bp] ; si = pointer to data to be copied mov di,8[bp] ; di = offset within video ram and di,dgroup:vid_mask ; only 4K or 16K counts mov cx,10[bp] ; cx = word count for copy loop mov dx,03DAh ; prepare to see if color display is retracing mov bx,di ; see if copy will run off end of video ram add bx,cx ; compute where copy ends add bx,cx ; bx = last character copied + 1 sub bx,dgroup:vid_mask ; bx = # characters beyond end of video ram sub bx,1 ; note: dec bx doesn't set flags properly jle vid.1 ; jump if no overrun sar bx,1 ; bx = # words that don't fit in video ram sub cx,bx ; reduce count by overrun mov tmp,cx ; save actual count used for later vid1: test dgroup:color,1 ; skip vertical retrace test if display is mono jz vid4 ; if monochrome then go to vid.2 ; vid2:in ; with a color display, you can only copy to ; test al,010q ; the video ram during vertical retrace, so ; jnz vid2 ; wait for start of retrace period. Bit 3 of vid3: in al,dx ; 0x3DA is set during retrace. First wait test al,010q ; until it is off (no retrace), then wait jz vid3 ; until it comes on (start of retrace) vid4: pushf ; copying may now start; save the flags cli ; interrupts just get in the way: disable them mov es,6[bp] ; load es now: int routines may ruin it cmp si,0 ; si = 0 means blank the screen je vid7 ; jmp for blanking lock nop ; this is a trick for the IBM PC-simulator only ; 'lock' indicates a video ram access rep movsw ; this is the copy loop vid5: popf ; restore flags cmp bx,0 ; if bx < 0, then no overrun and we are done jle vid6 ; jump if everything fit mov 10[bp],bx ; set up residual count mov 8[bp],0 ; start copying at base of video ram cmp 4[bp],0 ; NIL_PTR means store blanks je vid0 ; go do it mov si,tmp ; si = count of words copied add si,si ; si = count of bytes copied add 4[bp],si ; increment buffer pointer jmp vid0 ; go copy some more vid6: pop es ; restore registers pop dx ; restore dx pop cx ; restore cx pop bx ; restore bx pop di ; restore di pop si ; restore si pop bp ; restore bp ret ; return to caller vid7: mov ax,BLANK ; ax = blanking character rep stosw ; blank the screen jmp vid5 ; done ;=========================================================================== ; get_byte ;=========================================================================== ; This routine is used to fetch a byte from anywhere in memory. ; The call is: ; c = get_byte(seg, off) ; where ; 'seg' is the value to put in es ; 'off' is the offset from the es value get_byte: push bp ; save bp mov bp,sp ; we need to access parameters push es ; save es mov es,4[bp] ; load es with segment value mov bx,6[bp] ; load bx with offset from segment mov al,es:[bx] ; al = byte xor ah,ah ; ax = byte pop es ; restore es pop bp ; restore bp ret ; return to caller ;=========================================================================== ; reboot & dump ;=========================================================================== ; This code reboots the PC reboot: cli ; disable interrupts mov al,20h out 20h,al ; re-enable interrupt controller call resvec ; restore the vectors in low core int 19h ; reboot the PC wreboot: cli ; disable interrupts mov al,20h ; re-enable interrupt controller out 20h,al call resvec ; restore the vectors in low core xor ax,ax ; wait for character before continuing int 16h ; get char int 19h ; reboot the PC ; Restore the interrupt vectors in low core. resvec: cld mov cx,2*65 mov si,offset dgroup:vec_tabl xor di,di mov es,di rep movsw ret @CODE ENDS @DATAI SEGMENT lockvar DW 0 ; place to store flags for lock()/restore() tmp DW 0 ; count of bytes already copied vec_tabl DW 130 dup(0) ; storage for interrupt vectors stkoverrun DB "Kernel stack overrun, task = ",0 @DATAI ENDS END ; end of assembly Title mpx88 page,132 ; This file is part of the lowest layer of the Minix kernel. All process ; switching and message handling is done here and in file "proc.c". This file ; is entered on every transition to the kernel, both for sending/receiving ; messages and for all interrupts. In all cases, the trap or interrupt ; routine first calls save() to store the machine state in the proc table. ; Then the stack is switched to k_stack. Finally, the real trap or interrupt ; handler (in C) is called. When it returns, the interrupt routine jumps to ; restart, to run the process or task whose number is in 'cur_proc'. ; ; The external entry points into this file are: ; s_call: process or task wants to send or receive a message ; tty_int: interrupt routine for each key depression and release ; lpr_int: interrupt routine for each line printer interrupt ; disk_int: disk interrupt routine ; clock_int: clock interrupt routine [HZ times per second] ; surprise: all other interrupts < 16 are vectored here ; trp: all traps with vector >= 16 are vectored here ; divide: divide overflow traps are vectored here ; restart: start running a task or process ; include the following from const.h: K_STACK_BYTES EQU 256 IDLE EQU -999 ; include the following from ../h/com.h DISKINT EQU 1 CLOCK EQU -3 CLOCK_TICK EQU 2 FLOPPY EQU -5 ; The following procedures are defined in this file and called from outside it. PUBLIC $main, tty_int, lpr_int, clock_in, disk_int PUBLIC s_call, surprise, trp, divide, restart ; The following external procedures are called in this file. EXTRN main:NEAR, sys_call:NEAR, interrup:NEAR, keyboard:NEAR EXTRN panic:NEAR, unexpect:NEAR, trap:NEAR, div_trap:NEAR, pr_char:NEAR ; Variables and data structures. PUBLIC sizes, brksize, splimit, @end EXTRN cur_proc:WORD, proc_ptr:WORD, int_mess:WORD EXTRN scan_cod:WORD, k_stack:WORD ; The following constants are offsets into the proc table. esreg = 14 dsreg = 16 csreg = 18 ssreg = 20 spreg = 22 PC = 24 PSW = 28 SPLIM = 50 OFF = 18 ROFF = 12 INCLUDE ..\lib\prologue.h @CODE SEGMENT assume cs:@code,ds:dgroup ;=========================================================================== ; Minix ;=========================================================================== $main: Minix: ; this is the entry point for the Minix kernel. jmp short M0 ; skip over the next word(s) ORG 4 ; build writes the ds value at text address 4 ker_ds DW dgroup ; this word will contain kernel's ds value ; and it forces relocation for dos2out M0:cli ; disable interrupts mov ax,cs ; set up segment registers mov ds,ax ; set up ds mov ax,cs:ker_ds ; build has loaded this word with ds value mov ds,ax ; ds now contains proper value mov ss,ax ; ss now contains proper value mov dgroup:scan_cod,bx ; save scan code for '=' key from bootstrap mov sp,offset dgroup:k_stack ; set sp to point to the top of the add sp,K_STACK_BYTES ; kernel stack call main ; start the main program of Minix M1:jmp M1 ; this should never be executed ;=========================================================================== ; s_call ;=========================================================================== s_call: ; System calls are vectored here. call save ; save the machine state mov bp,dgroup:proc_ptr ; use bp to access sys call parameters push 2[bp] ; push(pointer to user message) (was bx) push [bp] ; push(src/dest) (was ax) push dgroup:cur_proc ; push caller push 4[bp] ; push(SEND/RECEIVE/BOTH) (was cx) call sys_call ; sys_call(function, caller, src_dest, m_ptr) jmp restart ; jump to code to restart proc/task running ;=========================================================================== ; tty_int ;=========================================================================== tty_int: ; Interrupt routine for terminal input. call save ; save the machine state call keyboard ; process a keyboard interrupt jmp restart ; continue execution ;=========================================================================== ; lpr_int ;=========================================================================== lpr_int: ; Interrupt routine for terminal input. call save ; save the machine state call pr_char ; process a line printer interrupt jmp restart ; continue execution ;=========================================================================== ; disk_int ;=========================================================================== disk_int: ; Interrupt routine for the floppy disk. call save ; save the machine state mov dgroup:int_mess+2,DISKINT ; build message for disk task mov ax,offset dgroup:int_mess ; prepare to call interrupt[FLOPPY, &intmess] push ax ; push second parameter mov ax,FLOPPY ; prepare to push first parameter push ax ; push first parameter call interrup ; this is the call jmp restart ; continue execution ;=========================================================================== ; clock_int ;=========================================================================== clock_in: ; Interrupt routine for the clock. call save ; save the machine state mov dgroup:int_mess+2,CLOCK_TICK; build message for clock task mov ax,offset dgroup:int_mess ; prepare to call interrupt(CLOCK,&intmess) push ax ; push second parameter mov ax,CLOCK ; prepare to push first parameter push ax ; push first parameter call interrup ; this is the call jmp restart ; continue execution ;=========================================================================== ; surprise ;=========================================================================== surprise: ; This is where unexpected interrupts come. call save ; save the machine state call unexpect ; go panic jmp restart ; never executed ;=========================================================================== ; trp ;=========================================================================== trp: ; This is where unexpected traps come. call save ; save the machine state call trap ; print a message jmp restart ; this error is not fatal ;=========================================================================== ; divide ;=========================================================================== divide: ; This is where divide overflow traps come. call save ; save the machine state call div_trap ; print a message jmp restart ; this error is not fatal ;=========================================================================== ; save ;=========================================================================== save: ; save the machine state in the proc table. push ds ; stack: psw/cs/pc/ret addr/ds push cs ; prepare to restore ds pop ds ; ds has now been set to cs mov ds,ker_ds ; word 4 in kernel text space contains ds value pop ds_save ; stack: psw/cs/pc/ret addr pop ret_save ; stack: psw/cs/pc mov bx_save,bx ; save bx for later ; we need a free register mov bx,dgroup:proc_ptr ; start save set up; make bx point to save area add bx,OFF ; bx points to place to store cs pop PC-OFF[bx] ; store pc in proc table pop csreg-OFF[bx] ; store cs in proc table pop PSW-OFF[bx] ; store psw mov ssreg-OFF[bx],ss ; store ss mov spreg-OFF[bx],sp ; sp as it was prior to interrupt mov sp,bx ; now use sp to point into proc table/task save mov bx,ds ; about to set ss mov ss,bx ; set ss push ds_save ; start saving all the registers, sp first push es ; save es between sp and bp mov es,bx ; es now references kernel memory too push bp ; save bp push di ; save di push si ; save si push dx ; save dx push cx ; save cx push bx_save ; save original bx push ax ; all registers now saved mov sp,offset dgroup:k_stack ; temporary stack for interrupts add sp,K_STACK_BYTES ; set sp to top of temporary stack mov splimit,offset dgroup:k_stack ; limit for temporary stack add splimit,8 ; splimit checks for stack overflow mov ax,ret_save ; ax = address to return to jmp ax ; return to caller; Note: sp points to saved ax  ;=========================================================================== ; restart ;=========================================================================== restart: ; This routine sets up and runs a proc or task. cmp dgroup:cur_proc,IDLE; restart user; if cur_proc = IDLE, go idle je _idle ; no user is runnable, jump to idle routine cli ; disable interrupts mov sp,dgroup:proc_ptr ; return to user, fetch regs from proc table pop ax ; start restoring registers pop bx ; restore bx pop cx ; restore cx pop dx ; restore dx pop si ; restore si pop di ; restore di mov lds_low,bx ; lds_low contains bx mov bx,sp ; bx points to saved bp register mov bp,SPLIM-ROFF[bx] ; splimit = p_splimit mov splimit,bp ; ditto mov bp,dsreg-ROFF[bx] ; bp = ds mov lds_low+2,bp ; lds_low+2 contains ds pop bp ; restore bp pop es ; restore es mov sp,spreg-ROFF[bx] ; restore sp mov ss,ssreg-ROFF[bx] ; restore ss using the value of ds push PSW-ROFF[bx] ; push psw (flags) push csreg-ROFF[bx] ; push cs push PC-ROFF[bx] ; push pc lds bx,DWORD PTR lds_low ; restore ds and bx in one fell swoop iret ; return to user or task ;=========================================================================== ; idle ;=========================================================================== _idle: ; executed when there is no work sti ; enable interrupts L3: wait ; just idle while waiting for interrupt jmp L3 ; loop until interrupt @CODE ENDS ;=========================================================================== ; data ;=========================================================================== @DATAB SEGMENT ; datab ensures it is the beginning of all data sizes DW 526Fh ; this must be the first data entry (magic nr) DW 7 dup(0) ; space for build table - total 16b splimit DW 0 ; stack limit for current task (kernel only) bx_save DW 0 ; storage for bx ds_save DW 0 ; storage for ds ret_save DW 0 ; storage for return address lds_low DW 0,0 ; storage used for restoring ds:bx brksize DW offset dgroup:@END+2 ; first free memory in kernel ttyomess DB "RS232 interrupt",0 @DATAB ENDS @DATAV SEGMENT ; DATAV holds nothing. The label in @end label byte ; the segment just tells us where @DATAV ENDS ; the data+bss ends. @STACK SEGMENT BYTE STACK 'STACK' ; Satisfy DOS-linker (dummy stack) @STACK ENDS END ; end of assembly-file ... signal.h!stat.h"sgtty.h#error.h$callnr.h%com.h&const.h'type.h#define NR_SIGS 16 /* number of signals used */ #define NSIG 16 /* number of signals used */ #define SIGHUP 1 /* hangup */ #define SIGINT 2 /* interrupt (DEL) */ #define SIGQUIT 3 /* quit (ASCII FS) */ #define SIGILL 4 /* illegal instruction (not reset when caught)*/ #define SIGTRAP 5 /* trace trap (not reset when caught) */ #define SIGIOT 6 /* IOT instruction */ #define SIGEMT 7 /* EMT instruction */ #define SIGFPE 8 /* floating point exception */ #define SIGKILL 9 /* kill (cannot be caught or ignored) */ #define SIGBUS 10 /* bus error */ #define SIGSEGV 11 /* segmentation violation */ #define SIGSYS 12 /* bad argument to system call */ #define SIGPIPE 13 /* write on a pipe with no one to read it */ #define SIGALRM 14 /* alarm clock */ #define SIGTERM 15 /* software termination signal from kill */ #define STACK_FAULT 16 /* used by kernel to signal stack fault */ int (*signal())(); #define SIG_DFL (int (*)())0 #define SIG_IGN (int (*)())1 struct stat { short int st_dev; unsigned short st_ino; unsigned short st_mode; short int st_nlink; short int st_uid; short int st_gid; short int st_rdev; long st_size; long st_atime; long st_mtime; long st_ctime; }; /* Some common definitions. */ #define S_IFMT 0170000 /* type of file */ #define S_IFDIR 0040000 /* directory */ #define S_IFCHR 0020000 /* character special */ #define S_IFBLK 0060000 /* block special */ #define S_IFREG 0100000 /* regular */ #define S_ISUID 04000 /* set user id on execution */ #define S_ISGID 02000 /* set group id on execution */ #define S_ISVTX 01000 /* save swapped text even after use */ #define S_IREAD 00400 /* read permission, owner */ #define S_IWRITE 00200 /* write permission, owner */ #define S_IEXEC 00100 /* execute/search permission, owner */ /* Data structures for IOCTL. */ struct sgttyb { char sg_ispeed; /* input speed (not used) */ char sg_ospeed; /* output speed (not used) */ char sg_erase; /* erase character */ char sg_kill; /* kill character */ int sg_flags; /* mode flags */ }; struct tchars { char t_intrc; /* SIGINT char */ char t_quitc; /* SIGQUIT char */ char t_startc; /* start output (initially CTRL-Q) */ char t_stopc; /* stop output (initially CTRL-S) */ char t_eofc; /* EOF (initially CTRL-D) */ char t_brkc; /* input delimiter (like nl) */ }; /* Fields in t_flags. */ #define XTABS 0006000 /* do tab expansion */ #define RAW 0000040 /* enable raw mode */ #define CRMOD 0000020 /* map lf to cr + lf */ #define ECHO 0000010 /* echo input */ #define CBREAK 0000002 /* enable cbreak mode */ #define COOKED 0000000 /* neither CBREAK nor RAW */ #define TIOCGETP (('t'<<8) | 8) #define TIOCSETP (('t'<<8) | 9) #define TIOCGETC (('t'<<8) | 18) #define TIOCSETC (('t'<<8) | 17) /* Error codes. They are negative since a few system calls, such as READ, can * either return a positive number indicating success, or an error code. */ #define NERROR 34 #define OK 0 #define ERROR -1 #define EPERM -1 #define ENOENT -2 #define ESRCH -3 #define EINTR -4 #define EIO -5 #define ENXIO -6 #define E2BIG -7 #define ENOEXEC -8 #define EBADF -9 #define ECHILD -10 #define EAGAIN -11 #define ENOMEM -12 #define EACCES -13 #define EFAULT -14 #define ENOTBLK -15 #define EBUSY -16 #define EEXIST -17 #define EXDEV -18 #define ENODEV -19 #define ENOTDIR -20 #define EISDIR -21 #define EINVAL -22 #define ENFILE -23 #define EMFILE -24 #define ENOTTY -25 #define ETXTBSY -26 #define EFBIG -27 #define ENOSPC -28 #define ESPIPE -29 #define EROFS -30 #define EMLINK -31 #define EPIPE -32 #define EDOM -33 #define ERANGE -34 #define E_LOCKED -101 #define E_BAD_CALL -102 #define E_LONG_STRING -103 #define EOF -104 /* End Of File - used by drivers */ /* The following error codes are generated by the kernel itself. */ #define E_BAD_DEST -1 /* destination address illegal */ #define E_BAD_SRC -2 /* source address illegal */ #define E_TRY_AGAIN -3 /* can't send-- tables full */ #define E_OVERRUN -4 /* interrupt for task that is not waiting */ #define E_BAD_BUF -5 /* message buf outside caller's addr space */ #define E_TASK -6 /* can't send to task */ #define E_NO_MESSAGE -7 /* RECEIVE failed: no message present */ #define E_NO_PERM -8 /* ordinary users can't send to tasks */ #define E_BAD_FCN -9 /* only valid fcns are SEND, RECEIVE, BOTH */ #define E_BAD_ADDR -10 /* bad address given to utility routine */ #define E_BAD_PROC -11 /* bad proc number given to utility */ #define NCALLS 69 /* number of system calls allowed */ #define EXIT 1 #define FORK 2 #define READ 3 #define WRITE 4 #define OPEN 5 #define CLOSE 6 #define WAIT 7 #define CREAT 8 #define LINK 9 #define UNLINK 10 #define CHDIR 12 #define TIME 13 #define MKNOD 14 #define CHMOD 15 #define CHOWN 16 #define BRK 17 #define STAT 18 #define LSEEK 19 #define GETPID 20 #define MOUNT 21 #define UMOUNT 22 #define SETUID 23 #define GETUID 24 #define STIME 25 #define ALARM 27 #define FSTAT 28 #define PAUSE 29 #define UTIME 30 #define ACCESS 33 #define SYNC 36 #define KILL 37 #define DUP 41 #define PIPE 42 #define TIMES 43 #define SETGID 46 #define GETGID 47 #define SIGNAL 48 #define IOCTL 54 #define EXEC 59 #define UMASK 60 #define CHROOT 61 /* The following are not system calls, but are processed like them. */ #define KSIG 64 /* kernel detected a signal */ #define UNPAUSE 65 /* to MM or FS: check for EINTR */ #define BRK2 66 /* to MM: used to say how big FS & INIT are */ #define REVIVE 67 /* to FS: revive a sleeping process */ #define TASK_REPLY 68 /* to FS: reply code from tty task */ /* System calls. */ #define SEND 1 /* function code for sending messages */ #define RECEIVE 2 /* function code for receiving messages */ #define BOTH 3 /* function code for SEND + RECEIVE */ #define ANY (NR_PROCS+100) /* receive(ANY, buf) accepts from any source */ /* Task numbers, function codes and reply codes. */ #define HARDWARE -1 /* used as source on interrupt generated msgs */ #define SYSTASK -2 /* internal functions */ # define SYS_XIT 1 /* fcn code for sys_xit(parent, proc) */ # define SYS_GETSP 2 /* fcn code for sys_sp(proc, &new_sp) */ # define SYS_SIG 3 /* fcn code for sys_sig(proc, sig) */ # define SYS_FORK 4 /* fcn code for sys_fork(parent, child) */ # define SYS_NEWMAP 5 /* fcn code for sys_newmap(procno, map_ptr) */ # define SYS_COPY 6 /* fcn code for sys_copy(ptr) */ # define SYS_EXEC 7 /* fcn code for sys_exec(procno, new_sp) */ # define SYS_TIMES 8 /* fcn code for sys_times(procno, bufptr) */ # define SYS_ABORT 9 /* fcn code for sys_abort() */ #define CLOCK -3 /* clock class */ # define SET_ALARM 1 /* fcn code to CLOCK, set up alarm */ # define CLOCK_TICK 2 /* fcn code for clock tick */ # define GET_TIME 3 /* fcn code to CLOCK, get real time */ # define SET_TIME 4 /* fcn code to CLOCK, set real time */ # define REAL_TIME 1 /* reply from CLOCK: here is real time */ #define MEM -4 /* /dev/ram, /dev/(k)mem and /dev/null class */ # define RAM_DEV 0 /* minor device for /dev/ram */ # define MEM_DEV 1 /* minor device for /dev/mem */ # define KMEM_DEV 2 /* minor device for /dev/kmem */ # define NULL_DEV 3 /* minor device for /dev/null */ #define FLOPPY -5 /* floppy disk class */ #define WINCHESTER -6 /* winchester (hard) disk class */ # define DISKINT 1 /* fcn code for disk interupt */ # define DISK_READ 3 /* fcn code to DISK (must equal TTY_READ) */ # define DISK_WRITE 4 /* fcn code to DISK (must equal TTY_WRITE) */ # define DISK_IOCTL 5 /* fcn code for setting up RAM disk */ #define TTY -7 /* terminal I/O class */ #define PRINTER -8 /* printer I/O class */ # define TTY_CHAR_INT 1 /* fcn code for tty input interrupt */ # define TTY_O_DONE 2 /* fcn code for tty output done */ # define TTY_READ 3 /* fcn code for reading from tty */ # define TTY_WRITE 4 /* fcn code for writing to tty */ # define TTY_IOCTL 5 /* fcn code for ioctl */ # define SUSPEND -998 /* used in interrupts when tty has no data */ /* Names of message fields for messages to CLOCK task. */ #define DELTA_TICKS m6_l1 /* alarm interval in clock ticks */ #define FUNC_TO_CALL m6_f1 /* pointer to function to call */ #define NEW_TIME m6_l1 /* value to set clock to (SET_TIME) */ #define CLOCK_PROC_NR m6_i1 /* which proc (or task) wants the alarm? */ #define SECONDS_LEFT m6_l1 /* how many seconds were remaining */ /* Names of message fields used for messages to block and character tasks. */ #define DEVICE m2_i1 /* major-minor device */ #define PROC_NR m2_i2 /* which (proc) wants I/O? */ #define COUNT m2_i3 /* how many bytes to transfer */ #define POSITION m2_l1 /* file offset */ #define ADDRESS m2_p1 /* core buffer address */ /* Names of message fields for messages to TTY task. */ #define TTY_LINE m2_i1 /* message parameter: terminal line */ #define TTY_REQUEST m2_i3 /* message parameter: ioctl request code */ #define TTY_SPEK m2_l1 /* message parameter: ioctl speed, erasing */ #define TTY_FLAGS m2_l2 /* message parameter: ioctl tty mode */ /* Names of messages fields used in reply messages from tasks. */ #define REP_PROC_NR m2_i1 /* # of proc on whose behalf I/O was done */ #define REP_STATUS m2_i2 /* bytes transferred or error number */ /* Names of fields for copy message to SYSTASK. */ #define SRC_SPACE m5_c1 /* T or D space (stack is also D) */ #define SRC_PROC_NR m5_i1 /* process to copy from */ #define SRC_BUFFER m5_l1 /* virtual address where data come from */ #define DST_SPACE m5_c2 /* T or D space (stack is also D) */ #define DST_PROC_NR m5_i2 /* process to copy to */ #define DST_BUFFER m5_l2 /* virtual address where data go to */ #define COPY_BYTES m5_l3 /* number of bytes to copy */ /* Field names for accounting, SYSTASK and miscellaneous. */ #define USER_TIME m4_l1 /* user time consumed by process */ #define SYSTEM_TIME m4_l2 /* system time consumed by process */ #define CHILD_UTIME m4_l3 /* user time consumed by process' children */ #define CHILD_STIME m4_l4 /* system time consumed by proces children */ #define PROC1 m1_i1 /* indicates a process */ #define PROC2 m1_i2 /* indicates a process */ #define PID m1_i3 /* process id passed from MM to kernel */ #define STACK_PTR m1_p1 /* used for stack ptr in sys_exec, sys_getsp */ #define PR m6_i1 /* process number for sys_sig */ #define SIGNUM m6_i2 /* signal number for sys_sig */ #define FUNC m6_f1 /* function pointer for sys_sig */ #define MEM_PTR m1_p1 /* tells where memory map is for sys_newmap */ #define CANCEL 0 /* general request to force a task to cancel */ #define SIG_MAP m1_i2 /* used by kernel for passing signal bit map */ /* Copyright (C) 1987 by Prentice-Hall, Inc. Permission is hereby granted to * private individuals and educational institutions to modify and * redistribute the binary and source programs of this system to other * private individuals and educational institutions for educational and * research purposes. For corporate or commercial use, permission from * Prentice-Hall is required. In general, such permission will be granted, * subject to a few conditions. */ #define EXTERN extern /* used in *.h files */ #define PRIVATE static /* PRIVATE x limits the scope of x */ #define PUBLIC /* PUBLIC is the opposite of PRIVATE */ #define FORWARD /* some compilers require this to be 'static' */ #define TRUE 1 /* used for turning integers into Booleans */ #define FALSE 0 /* used for turning integers into Booleans */ #define HZ 60 /* clock freq (software settable on IBM-PC) */ #define BLOCK_SIZE 1024 /* # bytes in a disk block */ #define SUPER_USER (uid) 0 /* uid of superuser */ #define MAJOR 8 /* major device = (dev>>MAJOR) & 0377 */ #define MINOR 0 /* minor device = (dev>>MINOR) & 0377 */ #define NR_TASKS 8 /* number of tasks in the transfer vector */ #define NR_PROCS 16 /* number of slots in proc table */ #define NR_SEGS 3 /* # segments per process */ #define T 0 /* proc[i].mem_map[T] is for text */ #define D 1 /* proc[i].mem_map[D] is for data */ #define S 2 /* proc[i].mem_map[S] is for stack */ #define MAX_P_LONG 2147483647 /* maximum positive long, i.e. 2**31 - 1 */ /* Memory is allocated in clicks. */ #define CLICK_SIZE 0020 /* unit in which memory is allocated */ #define CLICK_SHIFT 4 /* log2 of CLICK_SIZE */ /* Process numbers of some important processes */ #define MM_PROC_NR 0 /* process number of memory manager */ #define FS_PROC_NR 1 /* process number of file system */ #define INIT_PROC_NR 2 /* init -- the process that goes multiuser */ #define LOW_USER 2 /* first user not part of operating system */ /* Miscellaneous */ #define BYTE 0377 /* mask for 8 bits */ #define TO_USER 0 /* flag telling to copy from fs to user */ #define FROM_USER 1 /* flag telling to copy from user to fs */ #define READING 0 /* copy data to user */ #define WRITING 1 /* copy data from user */ #define ABS -999 /* this process means absolute memory */ #define WORD_SIZE 2 /* number of bytes per word */ #define NIL_PTR (char *) 0 /* generally useful expression */ #define NO_NUM 0x8000 /* used as numerical argument to panic() */ #define MAX_PATH 128 /* max length of path names */ #define SIG_PUSH_BYTES 8 /* how many bytes pushed by signal */ #define MAX_ISTACK_BYTES 1024 /* maximum initial stack size for EXEC */ /* Device numbers of root (RAM) and boot (fd0) devices. */ #define ROOT_DEV (dev_nr) 256 /* major-minor device number of root dev */ #define BOOT_DEV (dev_nr) 512 /* major-minor device number of boot diskette */ /* Flag bits for i_mode in the inode. */ #define I_TYPE 0170000 /* this field gives inode type */ #define I_REGULAR 0100000 /* regular file, not dir or special */ #define I_BLOCK_SPECIAL 0060000 /* block special file */ #define I_DIRECTORY 0040000 /* file is a directory */ #define I_CHAR_SPECIAL 0020000 /* character special file */ #define I_SET_UID_BIT 0004000 /* set effective uid on exec */ #define I_SET_GID_BIT 0002000 /* set effective gid on exec */ #define ALL_MODES 0006777 /* all bits for user, group and others */ #define RWX_MODES 0000777 /* mode bits for RWX only */ #define R_BIT 0000004 /* Rwx protection bit */ #define W_BIT 0000002 /* rWx protection bit */ #define X_BIT 0000001 /* rwX protection bit */ #define I_NOT_ALLOC 0000000 /* this inode is free */ /* Macros */ #define MAX(a,b) (a > b ? a : b) #define MIN(a,b) (a < b ? a : b) /* Type definitions */ typedef unsigned short unshort; /* must be 16-bit unsigned */ typedef unshort block_nr; /* block number */ #define NO_BLOCK (block_nr) 0 /* indicates the absence of a block number */ #define MAX_BLOCK_NR (block_nr) 0177777 typedef unshort inode_nr; /* inode number */ #define NO_ENTRY (inode_nr) 0 /* indicates the absence of a dir entry */ #define MAX_INODE_NR (inode_nr) 0177777 typedef unshort zone_nr; /* zone number */ #define NO_ZONE (zone_nr) 0 /* indicates the absence of a zone number */ #define HIGHEST_ZONE (zone_nr) 0177777 typedef unshort bit_nr; /* if inode_nr & zone_nr both unshort, then also unshort, else long */ typedef long zone_type; /* zone size */ typedef unshort mask_bits; /* mode bits */ typedef unshort dev_nr; /* major | minor device number */ #define NO_DEV (dev_nr) ~0 /* indicates absence of a device number */ typedef char links; /* number of links to an inode */ #define MAX_LINKS 0177 typedef long real_time; /* real time in seconds since Jan 1, 1980 */ typedef long file_pos; /* position in, or length of, a file */ #define MAX_FILE_POS 017777777777L typedef short int uid; /* user id */ typedef char gid; /* group id */ typedef unsigned vir_bytes; /* virtual addresses and lengths in bytes */ typedef unsigned vir_clicks; /* virtual addresses and lengths in clicks */ typedef long phys_bytes; /* physical addresses and lengths in bytes */ typedef unsigned phys_clicks; /* physical addresses and lengths in clicks */ typedef int signed_clicks; /* same length as phys_clicks, but signed */ /* Types relating to messages. */ #define M1 1 #define M3 3 #define M4 4 #define M3_STRING 14 typedef struct {int m1i1, m1i2, m1i3; char *m1p1, *m1p2, *m1p3;} mess_1; typedef struct {int m2i1, m2i2, m2i3; long m2l1, m2l2; char *m2p1;} mess_2; typedef struct {int m3i1, m3i2; char *m3p1; char m3ca1[M3_STRING];} mess_3; typedef struct {long m4l1, m4l2, m4l3, m4l4;} mess_4; typedef struct {char m5c1, m5c2; int m5i1, m5i2; long m5l1, m5l2, m5l3;} mess_5; typedef struct {int m6i1, m6i2, m6i3; long m6l1; int (*m6f1)();} mess_6; typedef struct { int m_source; /* who sent the message */ int m_type; /* what kind of message is it */ union { mess_1 m_m1; mess_2 m_m2; mess_3 m_m3; mess_4 m_m4; mess_5 m_m5; mess_6 m_m6; } m_u; } message; #define MESS_SIZE (sizeof(message)) #define NIL_MESS (message *) 0 /* The following defines provide names for useful members. */ #define m1_i1 m_u.m_m1.m1i1 #define m1_i2 m_u.m_m1.m1i2 #define m1_i3 m_u.m_m1.m1i3 #define m1_p1 m_u.m_m1.m1p1 #define m1_p2 m_u.m_m1.m1p2 #define m1_p3 m_u.m_m1.m1p3 #define m2_i1 m_u.m_m2.m2i1 #define m2_i2 m_u.m_m2.m2i2 #define m2_i3 m_u.m_m2.m2i3 #define m2_l1 m_u.m_m2.m2l1 #define m2_l2 m_u.m_m2.m2l2 #define m2_p1 m_u.m_m2.m2p1 #define m3_i1 m_u.m_m3.m3i1 #define m3_i2 m_u.m_m3.m3i2 #define m3_p1 m_u.m_m3.m3p1 #define m3_ca1 m_u.m_m3.m3ca1 #define m4_l1 m_u.m_m4.m4l1 #define m4_l2 m_u.m_m4.m4l2 #define m4_l3 m_u.m_m4.m4l3 #define m4_l4 m_u.m_m4.m4l4 #define m5_c1 m_u.m_m5.m5c1 #define m5_c2 m_u.m_m5.m5c2 #define m5_i1 m_u.m_m5.m5i1 #define m5_i2 m_u.m_m5.m5i2 #define m5_l1 m_u.m_m5.m5l1 #define m5_l2 m_u.m_m5.m5l2 #define m5_l3 m_u.m_m5.m5l3 #define m6_i1 m_u.m_m6.m6i1 #define m6_i2 m_u.m_m6.m6i2 #define m6_i3 m_u.m_m6.m6i3 #define m6_l1 m_u.m_m6.m6l1 #define m6_f1 m_u.m_m6.m6f1 struct mem_map { vir_clicks mem_vir; /* virtual address */ phys_clicks mem_phys; /* physical address */ vir_clicks mem_len; /* length */ }; struct copy_info { /* used by sys_copy(src, dst, bytes) */ int cp_src_proc; int cp_src_space; vir_bytes cp_src_vir; int cp_dst_proc; int cp_dst_space; vir_bytes cp_dst_vir; vir_bytes cp_bytes; }; ========================================================================*/ PRIVATE func_key(ch) char ch; /* scan code for a function key */ { /* This procedure traps function keys for debugging purposes. When MINIX is * fully debugged, it should be removed. */ if (ch == F1) p_dmp(); /* print process table */ if (ch == F2) map_dmp(); /* print memory map */ if (ch == F9) sigchar(&tty_struct[0], 0, SIGKILL); /* issue SIGKILL */ } #endif 2253 Sep 1 09:00 kernel/proc.h -rw-r--r-- 1 bin 18953 Sep 1 09:00 kernel/system.c -rw-r--r-- 1 bin 1594 Sep 1 09:00 kernel/table.c -rw-r--r-- 1 bin 48579 Sep 1 09:00 kernel/tty.c -rw-r--r-- 1 bin 664 Sep 1 09:00 kernel/type.h -rw-r--r-- 1 bin 26289 Sep 1 09:00 kernel/xt_wini.c kernel/C86: total 36 -rw-r--r-- 1 bin 201 Sep 1 09:00 _link.bat -rw-r--r-- 1 bin 16854 Sep 1 09:00 klib88.asm -rw-r--r-- 1 bin 99 Sep 1 09:00 linklist -rw-r--r-- 1 bin 3158 Sep 1 09:00 make.bat -rw-r--r-- 1 bin 10616 Sep 1 09:00 mpx88.asm kernel/MINIX: total 35 -rw-r--r-- 2 bin 18697 Sep 1 09:00 klib88.s -rw-r--r-- 1 bin 2467 Sep 1 09:00 makefile -rw-r--r-- 2 bin 10719 Sep 1 09:00 mpx88.s kernel/PCIX: total 35 -rw-r--r-- 2 bin 18697 Sep 1 09:00 klib88.s -rw-r--r-- 1 bin 2057 Sep 1 09:00 makefile -rw-r--r-- 2 bin 10719 Sep 1 09:00 mpx88.s E2˙d--77711