€h’’’’ž’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’?ž’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’A0ē #ķA„‘ # ¤§"ņ # ¤× ö #¤J ÷ #¤’ ł #¤Jū # !¤Le‘ #"#$%&'(*¤. ‘ #=>?@ABCEķ@ ‘ #F¤@‘ #Gķ]p‘ #HIJKLMNP¤ I"‘ #fghijkln¤§"&‘ #z{|}~€‚¤>*‘ #„…†‡ˆ¤£ -‘ #‰Š‹¤b/‘ #ŒŽ¤Ģ1‘ #‘’¤ß)4‘ #“”•–—˜™›¤Æ&8‘ #Ÿ ”¢£¤„§¤Ą =‘ #Ŗ«¬¤ 3A‘ #­®Æ°±²³µ¤ĶE‘ #»¼½¤įG‘ #¾æĄĮ¤ JO‘ #ĀĆÄÅĘĒČŹ¤ÖR‘ #Ö×ŲŁŚŪ¤:U‘ #ÜŻ¤ŖW‘ #ޤýk‘ #ßąįāćäå礁,?s‘ #¤˜w‘ #!¤¢Y‘ #"#$%&'(*¤]%†‘ #:;<=>?@B...kernel...clock.cclock.sconst.hdmp.cdmp.sfloppy.c floppy.s fs glo.h kernel klib88.smain.cmain.smakefilememory.cmemory.smpx88.sprinter.cprinter.sproc.cproc.hproc.ssystem.csystem.stable.ctable.stty.ctty.stype.h wini.c!wini.s/* 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 ® _sched_ticks: É_clock_task Ā6 † _clock_task: ƒ ‚ € ® _2: ĀI001B Ā1 Ā3 ĀI0017 ĀI001A ĀI0018 ĀI0019 † Ó_init_clock • Ąį_mc € 16 € Ó_receive Š Š Ąå_mc+2 ĄĘ,ģ ” ĢI0015 I0017: Ąį_mc € Ó_do_setalarm Š ĢI0016 I0018: Ó_do_get_time ĢI0016 I0019: Ąį_mc € Ó_do_set_time Š ĢI0016 I001A: Ó_do_clocktick ĢI0016 I001B: Į_mc+2 Ąį_1 € Ó_panic Š Š ĢI0016 I0015: Ąč#_2 ‰ Ģ.csa2 ½ Ą_mc+2ö ĶĘ,#2 je I0013 Ąį_mc € Į_mc Ó_send Š Š ­ _do_setalarm: ƒ ‚ ×ņ,#10 ’ Ąä4Ē ¬ Ąä10Ē Į12Ē ĄŲ,ė ĆŠ Ąä14Ē Ą-10(ń),ė Ąį86 mul Ļ Äį688 » Äā_proc ĄĘ,ģ § Ąä70Ē Ąę72Ē ×ėö sbb ć0 Ł1f or ęė 1: or ęķ ŁI0023 ‡ € € ĢI0024 I0023: § Ąä70Ē Ąę72Ē ×ä_realtime sbb ę_realtime+2 Įķ € ‡ € Ąį60 € Ó.dvi4 Įķ € I0024: Ć_mc+10 Ć_mc+10+2 ĄäŲ ĄåŠ ×į0 sbb ā0 Ł1f or åė 1: or åģ ŁI0026 ‡ € € ĢI0027 I0026: ĄäŲ ĄåŠ Ää_realtime adc å_realtime+2 ¦ € I0027: §  Ćķ Ą70Ē,ė Ą72Ē,ķ ĶĻö jge I0029 £ ōė sal į1 » Ąä-10(ń) Ą_watch_dogĒ,ė I0029: Ą_next_alarm,#65535 Ą_next_alarm+2,#32767 ĄĘ,#_proc I002E: ĶĘ,#_proc+2064 jae I002B § Ąä70Ē Ąę72Ē ×į0 sbb ć0 Ł1f or ęė 1: or ęķ je I002C § Ąä70Ē Ąę72Ē ×ä_next_alarm sbb ę_next_alarm+2 Ł1f Öäė je 1f Üķ 1: or ęķ jge I002C § mov ę70Ē Į72Ē Ą_next_alarm,ķ Ć_next_alarm+2 I002C: ÄĘ,#86 ĢI002E I002B: … „  _do_get_time: ƒ ‚ Ą_mc+2,#1 Į_realtime+2 Į_realtime ‡ € Ąį60 € Ó.dvi4 Ää_boot_time adc ę_boot_time+2 Ą_mc+10,ė Ą_mc+10+2,ķ … „  _do_set_time: ƒ ‚ ’ Į12Ē Į10Ē Į_realtime+2 Į_realtime ‡ € Ąį60 € Ó.dvi4 ‰ Ćī ×åė sbb ēķ Ą_boot_time,ģ Ą_boot_time+2,ī … „  _do_clocktick: ƒ ‚ ×ņ,#10 Ąå_lost_ticks ĄĻ,ģ £ Üė cwd Ää_realtime adc ē_realtime+2 Ą_realtime,ė Ą_realtime+2,ī Ąå_lost_ticks ×åĻ Ą_lost_ticks,ģ Ąå_next_alarm Ąę_next_alarm+2 ×å_realtime sbb ę_realtime+2 Ł1f Öåģ je 1f Üķ 1: or ęķ jg I0053 Ą_next_alarm,#65535 Ą_next_alarm+2,#32767 ĄĘ,#_proc I0058: ĶĘ,#_proc+2064 jae I0053 § Ąä70Ē Ąę72Ē ×į0 sbb ć0 Ł1f or ęė 1: or ęķ je I0056 § Ąä70Ē Ąę72Ē ×ä_realtime sbb ę_realtime+2 Ł1f Öäė je 1f Üķ 1: or ęķ jg I005D — ×į_proc Ąā86 cwd išv ģ ×į8 ĄŠ,ė ĶŠö jl I00510 4 € ĮŠ Ó_cause_ļg Š Š ĢI00511 I00510: ĄäŠ ōė sal į1 » Ąä_watch_dogĒ Ó(ė) I00511: § Ą70Ēö Ą72Ēö I005D: § Ąä70Ē Ąę72Ē ×į0 sbb ć0 Ł1f or ęė 1: or ęķ je I0056 § Ąä70Ē Ąę72Ē ×ä_next_alarm sbb ę_next_alarm+2 Ł1f Öäė je 1f Üķ 1: or ęķ jge I0056 § Ąę70Ē Į72Ē Ą_next_alarm,ķ Ć_next_alarm+2 I0056: ÄĘ,#86 ĢI0058 I0053: Ó_accounting ó_sched_ticks ŁI00517 Ąå_prev_ptr Ķ_bill_ptr,ģ ŁI0051A Ó_sched I0051A: Ą_sched_ticks,#6 Ąå_bill_ptr Ą_prev_ptr,ģ Ķ_pr_busyö je I0051D Ķ_pcountö jle I0051D Ąå_prev_ct Ķ_cum_count,ģ ŁI0051D Ó_pr_char I0051D: Ąå_cum_count Ą_prev_ct,ģ I00517: … „  _accounting: ƒ ‚ € Ķ_prev_proc,#2 jl I0063 Ąå_bill_ptr Äā54 ĄĘ,ģ § « Ąę2Ē Äį1 adc ć0 ° Ą2Ē,ķ ĢI0064 I0063: Ąå_bill_ptr Äā58 ĄĘ,ģ § « Ąę2Ē Äį1 adc ć0 ° Ą2Ē,ķ I0064: … „  _init_clock: ƒ ‚ ×ņ,#6 ĄĘ,#19886 — „ ¬ Ąć8 — shr äcl „ ĄŠ,ė Ąį54 € Ąį67 € Ó_port_out Š Š ¹ Ąį64 € Ó_port_out Š Š ĮŠ Ąį64 € Ó_port_out Š Š … „  ³ _watch_dog: .zerow 18/2 _mc: .zerow 24/2 _prev_ptr: .zerow 2/2 _next_alarm: .zerow 4/2 _boot_time: .zerow 4/2 ® _1: Ā27747 Ā25455 Ā8299 Ā24948 Ā27507 Ā26400 Ā29807 Ā25120 Ā25697 Ā27936 Ā29541 Ā24947 Ā25959 ø † /* 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 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); } É_p_dmp † _p_dmp: ƒ ‚ ×ņ,#22 Ąį_1 € Ó_printk Š “0 € Ąį_nbuff €  € Ąį_proc+516 € Ó_umap Äņ,#8 Ą-20(ń),ė Ą-1Ō,ī ĄĘ,#_proc I0015: ĶĘ,#_proc+2064 jae I0012 § Į30Ē  ž  – testb al,#1 je I0017 ­ I0017: § Ąä34Ē Ą-10(ń),ė ĄčĘ Ąä48Ē Ää46(ļ) Ą-12(ń),ė ‡ Ąć4 Ąå-10(ń) © 2: sal ā1 rcl į1 Ø 1: Äā512 adc į0 Ą-1Ī,ģ Ą-1Å,ė Į-1Å Į-1Ī ‡ € 024 € Ó.dvi4 ĄŠ,ė ‡ Ąć4 Ąå-12(ń) © 2: sal ā1 rcl į1 Ø 1: Äā512 adc į0 Ą-1Ī,ģ Ą-1Å,ė Į-1Å Į-1Ī ‡ € 024 € Ó.dvi4 ĄŲ,ė — ×į_proc Ąā86 cwd išv ģ € Ó_prname Š Ąå-2(ń) ĄčĘ ĄéĘ ĮŲ ĮŠ Į60Ē Į58Ē Į56(ļ) Į54(ļ) Į30(š) Į22Ē Į24(ļ) Į52(š) Ąį_2 € Ó_printk Äņ,#22 § Ķ30Ēö ŁI001A Ąį_3 € Ó_printk Š ĢI001B I001A: § Ąä80Ē Äį8 € Ó_prname Š I001B: — ×į_proc Ąā86 cwd išv ģ ×į8 Ą-22(ń),ė Ķ-22(ń),#2 jle I001D  Ąä-22(ń) sal äcl » Ąä_aoutĒ Ąę_aout+2Ē ×į0 sbb ć0 Ł1f or ęė 1: or ęķ je I001D  Ąä-22(ń) sal äcl » ‡ € “0 € Į-1Ō Į-20(ń) Į_aout+2Ē Į_aoutĒ Ó_phys_copy Äņ,#12 Ą_aout+80ö Ą_aout+80+2ö ĄĻ,#_nbuff I00113: ĶĻ,#_nbuff+20 jae I00110 ĄåĻ Œ Ž ‹ Ķį32 jle I00114 ĄåĻ Œ Ž ‹  ž – Ķį127 jb I00111 I00114: ĄåĻ ŹĒö I00111: ÄĻ,#1 š3 I00110: Ķ-22(ń),#3 ŁI00119 Ąį_4 € Ó_printk Š ĢI001D I00119: Ąį_nbuff € Ąį_5 € Ó_printk Š Š I001D: Ąį_6 € Ó_printk Š • ÄĘ,#86 ĢI0015   Ąį_7 € Ó_printk Š … „  É_map_dmp _map_dmp: ƒ ‚ ×ņ,#14 Ąį_8 € Ó_printk Š ĄĘ,#_proc+688 I0025: ĶĘ,#_proc+2064 jae I0022 § Į30Ē  ž  – testb al,#1 je I0027 ĢI0023 I0027: § Ąä34Ē ĄŲ,ė ĄčĘ Ąä48Ē Ää46(ļ) Ą-10(ń),ė ‡ Ąć4 ĄåŲ © 2: sal ā1 rcl į1 Ø 1: Äā512 adc į0 Ą-1Å,ģ Ą-12(ń),ė Į-12(ń) Į-1Å ‡ € 024 € Ó.dvi4 ¬ Ąä-10(ń) ×äŲ Ėåģ Ąć4 © 2: sal į1 rcl ā1 Ø 1: Äį512 adc ā0 Ą-1Å,ė Ą-12(ń),ģ Į-12(ń) Į-1Å ‡ € 024 € Ó.dvi4 ĄŠ,ė — ×į_proc Ąā86 cwd išv ģ € Ó_prname Š § ĄčĘ ĄéĘ ĮŠ ¹ Į48Ē Į46(ļ) Į44(š) Į42Ē Į40(ļ) Į38(š) Į36Ē Į34(ļ) Į32(š) Ąį_9 € Ó_printk Äņ,#24 I0023: ÄĘ,#86 ĢI0025 I0022: … „  É_nayme ® _nayme: Ā_10 Ā_11 Ā_12 Ā_13 Ā_14 Ā_15 Ā_16 Ā_17 Ā_18 Ā_19 Ā_20 É_prname † _prname: ƒ ‚ ĶÅ,#124 ŁI0033 Ąį_21 € Ó_printk Š ĢI0034 I0033: ĶÅö jl I0036 ĶÅ,#10 jg I0036 ¢ sal į1 » Į_naymeĒ Ąį_22 € Ó_printk Š Š ĢI0034 I0036: ¢ ×į8 € Ąį_23 € Ó_printk Š Š I0034: mov ņ,ń „  É_set_name _set_name: ƒ ‚ ×ņ,#12 ĶĪö ŁI0043  ¢ sal äcl » Ą_aoutĒö Ą_aout+2Ēö … „  I0043: Æ Ąį86 mul Å Äį688 Ąčė “ € Äā2 ¦  € Äč#_proc Įļ Ó_umap Äņ,#8 ¬ ĄĘ,ī £ § ×į0 sbb ā0 Ł1f or åė 1: or åģ ŁI0046 … „  I0046: “ € Ąį_vargv €  € Ąį_proc+516 € Ó_umap Äņ,#8 ĄŲ,ė ĄŠ,ī ‡ € “ € ĮŠ ĮŲ ” ¹ Ó_phys_copy Äņ,#12 Ąį86 mul Å Äį688 » “0 € Į_vargv  € Äā_proc ¦ Ó_umap Äņ,#8  ’ sal åcl Ą_aoutĒ,ė Ą_aout+2Ē,ī … „  É_vargv ³ _vargv: .zerow 2/2 É_nbuff _nbuff: .zerow 22/2 É_aout _aout: .zerow 64/2 ® _1: Ā28682 Ā28530 Ā8291 Ā11552 Ā26992 Ā11620 Ā11552 Ā25456 Ā8237 Ā11552 Ā28787 Ā8237 Ā26144 Ā24940 Ā8295 Ā29984 Ā25971 Ā8306 Ā11552 Ā31091 Ā11635 ² Ā24930 Ā25971 Ā27680 Ā28009 Ā29801 Ā29216 Ā25445 Ā8310 ² Ā28515 Ā28013 Ā28257 Ā2660 ø _2: Ā9504 Ā25652 Ā9504 Ā30772 Ā9504 Ā30772 Ā9504 Ā30772 Ā9504 Ā17462 Ā9504 Ā17463 ² Ā13093 Ā19300 Ā9504 Ā25651 Ā8267 Ā32 _3: ² ² ² ø _4: Ā25135 Ā28265 Ā29487 Ā104 _5: Ā29477 ø _6: Ā10 _7: Ā10 _8: Ā20490 Ā20306 Ā8259 ² Ā11565 Ā11565 Ā21549 Ā22597 Ā11604 Ā11565 Ā11565 ² Ā11565 Ā11565 .word 17453 Ā21569 Ā11585 Ā11565 Ā11565 ² Ā11565 Ā11565 Ā21587 Ā17217 Ā11595 Ā11565 Ā11565 ² Ā16706 Ā17747 Ā21280 Ā23113 Ā2629 ø _9: Ā9504 Ā30772 Ā9504 Ā30772 Ā9504 Ā30772 ² Ā13349 Ā8312 Ā13349 Ā8312 Ā13349 Ā8312 Ā9504 Ā30772 Ā9504 Ā30772 Ā9504 Ā30772 ² Ā13093 Ā19300 Ā9504 Ā25651 Ā2635 ø _10: Ā21072 Ā20041 Ā21076 ø _11: Ā21588 Ā8281 ² ø _12: Ā18775 Ā17230 Ā17736 ø _13: Ā19526 Ā20559 Ā22864 ø _14: Ā16722 Ā17485 Ā19283 ø _15: Ā19523 Ā17231 Ā8267 ø _16: Ā22867 Ā8275 ² ø _17: Ā16712 Ā17490 Ā21079 ø _18: Ā19789 ² ² ø _19: Ā21318 ² ² ø _20: Ā20041 Ā21577 ² ø _21: Ā20033 Ā8281 ² ø _22: Ā29477 ø _23: Ā13349 Ā8292 Ā32 † /* 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); } ® _len: Ā255 Ā-255 Ā-254 Ā1023 Ā-1 Ā-1 Ā-1 Ā1279 _interleave: Ā513 Ā1027 Ā1541 Ā2055 Ā2569 Ā3083 Ā3597 Ā15 _gap: Ā42 Ā27 Ā42 Ā42 Ā35 Ā35 _rate: Ā2 ø Ā2 Ā2 Ā1 Ā1 _nr_sectors: Ā9 Ā15 Ā9 Ā9 Ā9 Ā9 _nr_blocks: Ā720 Ā2400 Ā720 Ā1440 Ā720 Ā1440 _steps_per_cyl: Ā1 Ā1 Ā2 Ā1 Ā2 Ā1 _mtr_setup: Ā15 Ā45 Ā15 Ā15 Ā45 É_floppy_task Ā45 † _floppy_task: ƒ ‚ ×ņ,#6 ® _2: ĀI001C Ā3 Ā1 ĀI001A ĀI001B † • Ąį_mess € 16 € Ó_receive Š Š Ķ_messö jge I0016 Į_mess Ąį_1 € Ó_panic Š Š ½ Ąå_mess ĄĻ,ģ Ąå_mess+6 ĄŠ,ģ Į_mess+2 ĢI0018 I001A: Ąį_mess € Ó_do_rdwt Š ” ĢI0019 I001B: Ąį_mess € Ó_do_rdwt Š ” ĢI0019 I001C: ĄĘ,#-22 ĢI0019 I0018: Ąč#_2 ‰ Ģ.csa2 I0019: Ą_mess+2,#68 ĄåŠ Ą_mess+4,ģ § Ą_mess+6,ģ Ąį_mess € ¹ Ó_send Š Š ­ _do_rdwt: ƒ ‚ ×ņ,#12 ’ Ąä4Ē ĄŠ,ė ĶŠö jl I0022 ĶŠ,#2 jl I0023 I0022: Ąį-5 € ĢI0021 I0023: “8 mul Š » Äā_floppy ĄĘ,ģ § ĄäŠ Ą6Ē,ė ’ Ąä2Ē § ° ’ Į12Ē Į10Ē ‡ € 024 € Ó.rmi4 ×ē#0 sbb ā0 Ł1f or åī 1: or åģ je I0027 Ąį-22 € ĢI0021 I0027: ’ Į12Ē Į10Ē ‡ € Ąį512 € Ó.dvi4 Ą-12(ń),ė Ą-10(ń),ķ Ąä-12(ń) Ąå-10(ń) ×į2400 sbb ā0 Ł1f Öäė je 1f Üģ 1: or åģ jl I002A Ąį-104 € ĢI0021 I002A: § Źal,27Ē Ž ‹ Ą_d,ė Ąć4 Ąā4 Ąä-12(ń) Į-10(ń) – Ąå_d sal ā1 € Į_nr_sectorsĒ  ž  – Ąć1 sal äcl Ėåģ ¦ € Ó.dvu4 § Ą8Ē,ė Ąå_d sal ā1 Ąä_nr_sectorsĒ cwd Ąā_interleave ¦ Į-10(ń) Į-12(ń) œ € Ó.rmi4 ‰ Äåī Œ Ž ‹ § Ą10Ē,ė Ąć4 Ąā4 Ąä-12(ń) Į-10(ń) – Ąå_d sal ā1 € Į_nr_sectorsĒ  ž  – Ąć1 sal äcl Ėåģ ¦ € Ó.rmu4 Ąå_d sal ā1 Ąäī cwd išv _nr_sectorsĒ § Ą12Ē,ė ’ ĄčĘ Ąä8Ē Ą14(ļ),ė ’ ĄčĘ Ąä18Ē Ą16(ļ),ė ’ ĄčĘ Ąä6Ē Ą4(ļ),ė § Ķ14Ē,#1024 je I002D Ąį-22 push ė ĢI0021 I002D: ĄŲö I00210: ĶŲ,#20 jg I002F ÜŲ Ąā3 ĄäŲ cwd išv ģ or ēī ŁI00213 Ąå_d Üģ Ąć6 Ąäģ cwd išv ķ Ą_d,ī § Ź27Ē,dl Ą_need_reset,#1 I00213: Ąå_d sal ā1 Ąä_nr_blocksĒ cwd Ąå-12(ń) Ąę-10(ń) ×åė sbb ęī Ł1f Öåģ je 1f Üķ 1: or ęķ jl I00216 ĢI00210 I00216: Ķ_need_resetö je I00219 Ó_reset I00219: ” Ó_dma_setup Š ” Ó_start_motor Š ” Ó_seek Š ¬ ĶĻö je I0021C ĢI00210 I0021C: ” Ó_transfer Š ¬ ĶĻö ŁI0021F ĢI002F I0021F: ĶĻ,#-5 ŁI00210 I002F: Ą_motor_goal,#12 Ąį_stop_motor € 80 € Ó_clock_mess Š Š ĶĻö ŁI00225 § Ķ8Ēö jle I00225 Ą_initialized,#1 I00225: ĶĻö ŁI00229 024 € ĢI0021 I00229: Ąį-5 € I0021:  … „  _dma_setup: ƒ ‚ ×ņ,#22 ’ ĶĒ,#3 ŁI0033 Ąį70 € ĢI0034 I0033: Ąį74 € I0034:  ž  – ” ’ Ąä16Ē Ą-1Ī,ė Į14Ē  ž  – Ą-1Ō,ė ’ Ąį86 mul 4Ē Äį688 » Į-1Ō Į-1Ī  € Äā_proc ¦ Ó_umap Äņ,#8 Ą-22(ń),ė Ą-20(ń),ī Ėęķ Ąä-22(ń) Ąå-20(ń) © 2: sar ā1 rcr į1 Ø 1:  ž – „  ž – ¬ Ąć8 Ąä-22(ń) Ąå-20(ń) © 2: sar ā1 rcr į1 Ø 1:  ž – „  ž – ĄŠ,ė Ąć16 Ąä-22(ń) Ąå-20(ń) © 2: sar ā1 rcr į1 Ø 1:  ž – „  ž – ĄŲ,ė Ąä-1Ō ×į1 Ėęķ shr äcl  ž –  ž – „  ž – Ą-10(ń),ė Ąä-1Ō ×į1 Ąć8 shr äcl  ž –  ž – „  ž – Ą-12(ń),ė Ąä-22(ń) Ąå-20(ń) ×į0 sbb ā0 Ł1f or åė 1: or åģ ŁI0036  ž Ąä-1Ī – € Ąį_3 € Ó_panic Š Š I0036: ‡ € Ąć4 Ąā4 Ąä-22(ń) Į-1Ō Į-20(ń) – ‰ Ćķ Ćī Äęė adc ēģ ×ć1 sbb ē#0 œ Įķ 6 € Ćķ  ‰ © 2: shr ā1 rcr į1 Ø 1: „ Öā0 Ą-1Å,ė ĄäŲ Ķ-1Å,ė je I0039 ĮŲ Ąį_4 € Ó_panic Š Š I0039: Ó_lock ” 2 € Ó_port_out Š Š ” 1 € Ó_port_out Š Š push Ļ Ąį4 € Ó_port_out Š Š ĮŠ Ąį4 € Ó_port_out Š Š ĮŲ 29 € Ó_port_out Š Š Į-10(ń) Ąį5 € Ó_port_out Š Š Į-12(ń) Ąį5 € Ó_port_out Š Š Ó_unlock “ € 0 € Ó_port_out Š Š … „  _start_motor: ƒ ‚ ×ņ,#6 Ó_lock ’ Ąä6Ē Äį4 Ąęė  sal äcl ” Į6Ē  ž  – €  ž — – or į12 ‰ or äģ  ž – Ą_motor_goal,ė Ąå_motor_status test _prev_motor,ģ je I0043 Ąå_prev_motor or _motor_goal,ģ I0043: — Öä_motor_status ¬ Į_motor_goal 010 € Ó_port_out Š Š Ąå_motor_goal Ą_motor_status,ģ § Ą_prev_motor,ģ Ó_unlock ĶĻö je I0046 … „  I0046: Ąå_d sal ā1 Ąį_send_mess € Į_mtr_setupĒ Ó_clock_mess Š Š Ąį_mess € Ąį-3 € Ó_receive Š Š … „  _stop_motor: ƒ ‚  ž Ąä_motor_goal – Öį240 €  ž Ąä_motor_status – Öį240 ‰ Ķåė je I0053 Į_motor_goal 010 € Ó_port_out Š Š Ąå_motor_goal Ą_motor_status,ģ I0053: … „  _seek: ƒ ‚ € ’ cmpb 26Ēö ŁI0063 ˆ Ó_recalibrate Š ™ je I0063 Ąį-1 € ĢI0061 I0063: ’ ĄčÅ Ąä8(ļ) Ķ2Ē,ė ŁI0069 ‡ € ĢI0061 I0069: 5 € Ó_fdc_out Š ’  Ąä12Ē sal äcl or ä6Ē € Ó_fdc_out Š Ąå_d sal ā1 ĄčÅ Ąä8(ļ) mul _steps_per_cylĒ € Ó_fdc_out Š Ķ_need_resetö je I006C Ąį-1 € ĢI0061 I006C: Ąį_mess € Ąį-1 € Ó_receive Š Š Ąį8 € Ó_fdc_out Š ˆ Ó_fdc_results Š ” ’ Źal,18Ē Ž ‹  ž – Öį248 Ķį32 je I006F ĄĘ,#-1 I006F: ’ Źal,19Ē Ž ‹ Ąå_d sal ā1 ĄčÅ € Ąä8(ļ) mul _steps_per_cylĒ ‰ Ķåė je I00612 ĄĘ,#-1 I00612: ĶĘö je I00615 ˆ Ó_recalibrate Š ™ je I00615 Ąį-1 € ĢI0061 I00615: ” I0061:  … „  _transfer: ƒ ‚ ×ņ,#10 ’ cmpb 26Ēö ŁI0073 Ąį-2 € ĢI0071 I0073: ’ Ąä6Ē Äį4 Ąęė Ąå_motor_status sar åcl testb bl,#1 ŁI0076 Ąį-2 € ĢI0071 I0076: Ķ_pc_atö je I0079 Ąå_d sal ā1 Į_rateĒ 015 € call _port_out Š Š I0079: ’ ĶĒ,#3 ŁI007C “30 € ĢI007D I007C: 97 € I007D:  ž  – ĄŠ,ė ĮŠ Ó_fdc_out Š ’  Ąä12Ē sal äcl or ä6Ē € Ó_fdc_out Š ’ Į8Ē Ó_fdc_out Š ’ Į12Ē Ó_fdc_out Š ’ Į10Ē Ó_fdc_out Š Źal,_len+4 Ž ‹ € Ó_fdc_out Š Ąå_d sal ā1 Į_nr_sectorsĒ Ó_fdc_out Š Ąå_d sal ā1 Į_gapĒ Ó_fdc_out Š “55 € Ó_fdc_out Š Ķ_need_resetö je I007F Ąį-2 € ĢI0071 I007F: Ąį_mess € Ąį-1 € Ó_receive Š Š ˆ Ó_fdc_results Š ” ĶĘö je I00712 ” ĢI0071 I00712: ’ Źal,19Ē Ž ‹  ž – testb al,#5 ŁI00714 ’ Źal,20Ē Õah,ah ‹  ž – testb al,#31 je I00715 I00714: ’ Ź26Ēö I00715: ’ Źal,19Ē Ž ‹  ž – testb al,#2 je I00719 ’ Į6Ē Ąį_5 € Ó_printk Š Š Ąį-5 € ĢI0071 I00719: ’ Źal,18Ē Ž ‹  ž – testb al,#248 je I0071C Ąį-2 € ĢI0071 I0071C: ’ Źal,20Ē Ž ‹ € Źal,19Ē Ž ‹ ‰ or äģ ™ je I0071F Ąį-2 € ĢI0071 I0071F: ’ Źal,21Ē Ž ‹ ×ä8Ē  ž – Ąć1 sal äcl Ąå_d sal ā1 € Į_nr_sectorsĒ  ž  – ‰ mul ģ  ž – ¬ ’ Źal,22Ē Ž ‹ ×ä12Ē Ąå_d sal ā1 mul _nr_sectorsĒ ÄäĻ ¬ ’ Źal,23Ē Ž ‹ ×ä10(bx) ÄäĻ ¬ Ąį512 mul Ļ Ķ14Ē,ė je I00722 Ąį-2 € ĢI0071 I00722: ‡ € I0071:  … „  _fdc_results: ƒ ‚ ×ņ,#8 ¼ I0085: ĶĘ,#8 jge I0082 ĄŲö ĄĻö I0089: ĶĻ,#100 jge I0086 ŪäŠ € 012 € Ó_port_in Š Š  ž ĄäŠ – testb al,#128 je I0087 ĄŲ,#1 ĢI0086 I0087: ÜĻ ĢI0089 I0086: ĶŲö ŁI008E Ąį-3 € ĢI0081 I008E:  ž ĄäŠ – testb al,Ń ŁI00811 ‡ € ĢI0081 I00811:  ž ĄäŠ – testb al,#64 ŁI00814 Ąį-3 € ĢI0081 I00814: ŪäŠ € 013 € Ó_port_in Š Š  ž ĄäŠ – „  ž – ’ ÄåĘ Ź18Ē,al ÜĘ ĢI0085 I0082: Ą_need_reset,#1 Ąį-3 € I0081:  … „  _fdc_out: ƒ ‚ ×ņ,#6 Ķ_need_resetö je I0093 … „  I0093: ĄĘ,#100 I0096: ” óĘ  ™ jle I0095 ŪäĻ € 012 € Ó_port_in Š Š  ž £ – Öį192  ž – ¬  ž £ – Ķį128 je I0099 ĢI0096 I0099: ˆ 013 € Ó_port_out Š Š … „  I0095: Ą_need_reset,#1 … „  _recalibrate: ƒ ‚ € ˆ Ó_start_motor Š Ąį7 € Ó_fdc_out Š ’ Į6Ē Ó_fdc_out Š Ķ_need_resetö je I00A3 Ąį-1 € ĢI00A1 I00A3: Ąį_mess € Ąį-1 € Ó_receive Š Š Ąį8 € Ó_fdc_out Š ˆ Ó_fdc_results Š ” ’ Ą2Ē,#-1 ĶĘö ŁI00A5 ’ Źal,18Ē Ž ‹  ž – Öį248 Ķį32 ŁI00A5 ’ cmpb 19Ēö je I00A6 I00A5: Ą_need_reset,#1 ’ Ź26Ēö Ąį-4 € ĢI00A1 I00A6: ’ Ź26Ē,#1 ‡ € I00A1:  … „  _reset: ƒ ‚ ×ņ,#8 Ą_need_resetö Ó_lock Ą_motor_statusö Ą_motor_goalö ‡ € 010 € Ó_port_out Š Š 2 € 010 € Ó_port_out Š Š Ó_unlock Ąį_mess € Ąį-1 € Ó_receive Š Š ĄŲ,#_floppy ĄåŲ Ź18Ēö Ąį8 € Ó_fdc_out Š ĮŲ Ó_fdc_results Š ¬ ĄåŲ ‡ Źal,18Ē €  ž  – ĄŠ,ė Ąį3 € Ó_fdc_out Š “23 € Ó_fdc_out Š “ € Ó_fdc_out Š mov Ęö I00B5: ĶĘ,#2 jge I00B2 “8 mul Ę » Ź_floppy+26Ēö ÜĘ ĢI00B5 I00B2: … „  _clock_mess: ƒ ‚ Ą_mess+2,#1 Ą_mess+4,#-5 ¢ cwd Ą_mess+10,ė Ą_mess+10+2,ī Æ Ą_mess+14,ģ Ąį_mess € Ąį-3 € Ó_sendrec Š Š … „  _send_mess: ƒ ‚ Ą_mess+2,Ž Ąį_mess € Ąį-5 € Ó_send Š Š … „  ³ _mess: .zerow 24/2 _d: .zerow 2/2 _initialized: .zerow 2/2 _need_reset: .zerow 2/2 _prev_motor: .zerow 2/2 _motor_goal: .zerow 2/2 _motor_status: .zerow 2/2 _floppy: .zerow 56/2 ® _1: Ā26980 Ā27507 Ā29728 Ā29537 Ā8299 Ā28519 Ā8308 Ā25965 Ā29555 Ā26465 Ā8293 Ā29286 Ā28015 Ā32 _3: Ā21318 Ā26400 Ā30305 Ā8293 Ā27750 Ā28783 Ā31088 Ā25632 Ā29545 Ā8299 Ā29284 Ā30313 Ā29285 Ā25120 Ā25697 Ā24864 æ00 Ā114 _4: Ā29268 Ā27001 Ā26478 Ā29728 Ā8303 Ā19780 Ā8257 Ā25441 Ā28530 Ā29555 Ā13856 Ā19252 Ā25120 Ā30063 æ10 Ā29281 Ā121 _5: Ā26948 Ā27507 Ā29797 Ā25972 Ā26912 Ā8302 Ā29284 Ā30313 Ā8293 Ā25637 Ā26912 Ā8307 Ā29303 Ā29801 Ā8293 Ā29296 Ā29807 Ā25445 Ā25972 Ā11876 Ā10 † D...type.hsuper.hmkfsparam.hinode.hglo.h fproc.h file.h dev.h const.hdtestbuf.hdtest.cklib88.swini.clink.cmain.cmisc.cmount.copen.cpipe.cprotect.cputc.cread.cstadir.csuper.ctable.ctime.cutility.cwrite.c!MINIX#PCIX%C86Dmkfs.c/* 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. */  Pfķ %ėśŒČŽŲ”ŽŲŽŠ‰`p¼ŹƒÄč!ėžč…‹.ø{’v’v’6ź‹’včDGéÄčkčŸé»čbčÉVé²čYĒŠ‹øĪ‹Pøū’PčāEé›čBĒŠ‹øĪ‹Pøś’PčĖEé„č+ĒŠ‹øĪ‹Pøż’Pč“Eėnččėfč č±ė^ččāėVŽbfdf‰`f‹ø{ƒĆGG ŒW‰g‰ÜŒŪŽÓ’6bfŽĆUWVRQ’6`fP¼ŹƒÄĒÄlʃƒÄl”df’ą>ź‹üt5ś‹&ø{X[YZ^_‰ff‰ć‹o&‰.Äl‹o‰.hf]‹g ŽW’w’w’w ÅffĻū›ėżU‰åƒģč·WĒFų`‹RfPf‰^ś‹FśFų‰FöĒFžŗ{~žvéĒFüƒ~ü })¹»‹Füč)b¹Óą¹»čb‹^üŃć^ž‰’FüėыFž-ŗ{»V™÷ū-‰Fü~žj~s¹‹FüÓą ‰ĆĆŹ„SėøP‹^žX‰G‹^ž‹vž‹G‰D2‹^žƒ2t‹^žƒĆ2‰^ī‹^‹FüŃą‰Ć‹vž‹‡Ŗl‰D‹^žƒuƒ~ü|’vžčrI^‹^žĒG‹^žĒGƒ~ü}P‹^žĒG$ ™‹^ž‹Fų‰G"‹^žĒG* ™‹Pf^ų‹vž‰\(‹^žĒG0 ™‹Pf^ųRf‹vž‰\.‹RfPf‹vž‰\,雸V÷füZ‰Ć‹‡č{‰Fō‹Füѹѹ‰Ć‹vž‹‡Pf‰D$ƒ~üu’vöė’vō‹^žX‰G"‹Füѹѹ‰Ć‹vž‹‡Pf‰D*‹Füѹѹ‰Ć‹vž‹‡PfD"‰D(‹Füѹѹ‰Ć‹vž‹‡Pf‰D,‹Füѹѹ‰Ć‹vž‹‡PfD(‰D.‹^ž’w"¹»Xčt`‹^ž‰G‹^ž’w(¹»Xč^`‹^ž‰G‹^ž’w(¹»XčH`‹^ž‰G‹^ž’w(¹»Xč2`‹^ž‰GƒFžVéŪżĒ*~ʃ*~ĒF~ʃƒF~ĒFžl~žŹƒs‹^žĒGƒFžVėėčØU£bpøPø’’PčaV^^‰Fü¹»‹FüčŅ_=üuĒŹ‹øPøęlPøPø~PčDAƒÄ‰Fš‰Vņ1ĄPøP’vņ’vš1ĄPPčūSƒÄ ĒFüƒ~ü}’vųø’P’vüčČƒÄ’FüėåĒFü~ü}’vųøšP’vüč§ƒÄ’Füėä’vųø¢P1ĄPč’ƒÄ’vųø"Pø PčƒÄ’vųø|PøPčpƒÄ’vųø<Pø Pč_ƒÄ’vųøNPøPčNƒÄ’vųøEPøPč=ƒÄƒ>Ź‹t’vųøePøvPč%ƒÄė’vųøePø PčƒÄ1ĄPøŗ{PøPčƒÄǶ{~čF1ĄPø!PčŹS^^1ĄPø”Pč¾S^^čÜū‰ģ]ĆU‰åøzfPč§Y^‹ø{¹‹G*ÓąP’wø˜fPčYƒÄ‰ģ]ĆU‰åø¼fPč~Y^øÜfPčvY^øgPčnY^‹ø{¹‹G*ÓąP’wøLgPčVYƒÄ‰ģ]ĆU‰åøpgPčEY^‹ø{¹‹G*ÓąP’wø–gPč-YƒÄ‰ģ]ĆU‰å‹^€?t1’vøŗgPčY^^¹»‹FčÜ]=€t ’vøĢgPčōX^^øŠgPčėX^øŅgPčćX^čjT‰ģ]ĆU‰åƒģ ‹F‰Fü‹F‰Fž^ü‰^ś1Ą1Ū‹PfNŲPQøPYX[ćŃąŃÓāś‰Fö‰^ų¹»‹Fö’vųči]1ŪYFśŁQ¹»čW]‰FöFų‹F™¾1’‰Ć‰Š1ÉQ¹Qč^RP’vų’vöč“QƒÄ ‰ģ]ĆU‰åƒģ辍FčPøtPč$X^^ø‚÷fģ‰ĆĆ²q‰^ę’vźė^FčPč^^ėՍFčP’vęčv^^ėĒFčP’vęčš ^^ė¹FčP’vęčf ^^ė«FčP’vęčŁ^^ė1ĄPPPPøź’P’vī’včøDPčDƒÄ끾fi[éd\U‰åƒģčßQ‹^‹G‰FōĒFņpq‹^ōŠ0䘉Fü‹Fü‰Fś‹FüFüƒFō’vü’NüX Ą~’vōƒFō[‹vņŠˆƒFņėą‹^‹G‰Fō‹^ōĘč‹QĒFņpq’vś’NśX ĄéĀ’vņƒFņ[ŠˆFń’vņƒFņ[Š0䘉FüŠFń0ä˜P’vüčœ^^ø‚÷fü‰ĆĆ²q‰^ī‹^r~­‹^ī’·\¹»XčĢ[%"¹»čĄ[‰Fž‹^Īƒ~žuéz’‹^Ģém’’vīč¾^‰Fž‹^īŠ‡n0䘉FųЇo0䘉Fö1ĄPPPP’vž’vö’vųøCPčƒÄé0’‰ģ]ĆU‰åƒģ ø‚÷f‰ĆĆ²q‰^žŠF0ä˜=;|ŠF0ä˜=DŠF0ä˜Pč/^‰ģ]Ƌ^žæĢČ|‰ģ]Ƌ^ž’·\¹»Xč[%"¹»čöZ‰Fü‹^ž€æetŠF0ä˜Pč"^ˆFė.¹»‹FüčĶZ= t¹»ŠF0äčŗZ%¹»č®ZˆF€~u‰ģ]ƃ~üt¹»‹FüčZ=téWƒ~üté|ŠF0䘋^žPЇg0ä˜[9Ću>‹^ž€æcu4’vžčv^=’’t$øP’vžč*^^ø P’vžč^^øP’vžč^^‰ģ]ÊF0䘋^žPЇh0ä˜[9Ću:‹^ž€æcu0’vžč"^ Ąuėó‹^žŠ‡h0ä˜P’vžčĪ^^ø P’vžčĀ^^‰ģ]Ƌ^ž€æcuA€~\u‹^žĘ‡cŠF0ä˜P’vžč—^^‰ģ]ÊF0䘋^žPЇm0ä˜[9Ćté”ĘF鍋^žĘ‡cŠF0䘋^žPЇg0ä˜[9ĆtoŠF0䘋^žPЇh0ä˜[9ĆtYŠF0䘋^žPЇm0ä˜[9ĆtC‹^žĆȉ^ö‹vöø\P’7ƒ[Xˆ‹^žĆ̉^ö‹@‰‹^ž‹vžĘČ9·Ču ‹^ž‹Fž‰‡Č€~ u‹^ž’·\¹»XčėXØtĘF ŠF0䘋^žPЇi0ä˜[9ĆtŠF0䘋^žPЇj0ä˜[9Ću6ŠF0䘋^žPЇi0ä˜[9ĆuøPėøPFś’vś’v’vžčg ƒÄ‰ģ]ÊF0䘋^žPЇl0ä˜[9Ću ‹^žĘ‡d‰ģ]ÊF0䘋^žPЇk0ä˜[9Ću‹^žĘ‡d‹^ž‹‡Z’vž’Š^‰ģ]ƀ~ t€~u‹^žĆΉ^ö‹@‰‹^žĆȉ^ö‹vö1ĄŠFP’7ƒ[Xˆ‹^ž‹vžĘČ9·Ču ‹^ž‹Fž‰‡Č‹^žĆ̉^ö‹@‰ŠF0ä˜P’vžč„^^‰ģ]ĆU‰åƒģŠF0䘹»čØW%¹»čœW‰FžŠF0䘹»čŠW؀t1ĄPėøPFüƒ>Ģ‹t邃>npuƒ>lpt ‹^žŠ‡Bh0ä˜Pė ‹^žŠ‡īg0ä˜PFśƒ>fptƒ~ž} ‹^žŠ‡Bh0䘉Fśƒ~žF鯃>hpu鄃>npuƒ>lpt ‹^žŠ‡īg0ä˜Pė ‹^žŠ‡Bh0ä˜PFśéyƒ>npuƒ>lpt ‹^žŠ‡žh0ä˜Pė ‹^žŠ‡–h0ä˜PFśƒ>fptƒ~ž} ‹^žŠ‡Bh0䘉Fśƒ~žF~0ƒ>hpt)ƒ>npuƒ>lpt ‹^žŠ‡–h0ä˜Pė ‹^žŠ‡žh0ä˜PFś¹»‹FśčiV%’¹»č]V‰Fś¹»‹FśčNV=€r¹»‹Fśč=V=†s鎃>jpt'ƒ~śA| ƒ~śZƒFś ėƒ~śa|ƒ~śz ‹Fś- ‰Fśƒ>dpt¹»‹FśčōU €¹»ččU‰Fśƒ>fpt¹»‹FśčŅU%¹»čĘU‰Fśƒ~śuĒFśƒ~üuĒFś’vśé„¹»‹Fśč›U-€Pėh‹^ü‰npėf‹^ü‰lpė]‹^ü‰fpėT‹^ü‰dpėKƒ~ütƒ>źgt ø+jp£jpø+Fü£źgė)ƒ~ütƒ>ģgt ø+hp£hpø+Fü£ģgė¾xi[éńT1ĄPX‰ģ]ĆU‰å‹^’·\¹»Xč UØu‰ģ]ƀ~tŠF0ä˜P’vč– ^^’vč ^‰ģ]ĆU‰åPP‹^ƒæĢuø’’PėT‹^‹F9‡Čt ‹^‹‡ČHPė‹^ĆĒSFž‹^ž€? t‹^ž€? uø’’Pė‹^‹Fž‰‡Č‹^Ć̉^ü‹H‰1ĄPX‰ģ]ĆU‰åPP‹^ƒær~#‹^‹v1ĄPPPPøż’P’w’4øDPččƒÄ‰ģ]Ƌ^‹v‹ˆ„n‹^‹v‹Gˆ„o‹^‹v‹G‰„p‹^‹v‹G‰„r’vč0^‰Fž‹^Їo0䘉Fü‹^1ĄPPPP’vž’vü’7øDPčƒÄ‰ģ]ĆU‰åƒģ&‹^’·\¹»XčĀSØ"t1ĄPėøPFž‹^ƒæĢtƒ~žt‹^ƒæĪuøüPéé‹^Їo0䘻V÷㰉Áú{‰^ą‹^‹‡p‰Fš’·r¹»Xč^S‰Fī’vī’všøP’vąčŪ4ƒÄ‰Fź‰Vģ‹Fź‹^ģ-ƒŪu Ć Ūuøö’PéøPøppPøPø|Pč¤4ƒÄ‰Fę‰VčĒFöĒFōĒFņ‹^ƒæré;‹^‹v‹„Ģ9‡r} ‹^’·rė‹^’·ĢFų~ų}’vųėøPFųĒFüĒFāpp’vų’NųX Ąéw‹^Ćʉ^Ž‹vŽ’7ƒ[ŠˆFå‹^‹vĘČ9·Źu ‹^‹F‰‡Ź‹^āŠF刃Fā’Fü€~å t€~åu„‹^ĆΉ^Ž‹H‰ƒ~žt €~åu’Fņ’Fōƒ~žué{’ƒ~ņt‹FüHPė’vüFś‹Fś™RP’vģ’vź’vč’vęčfFƒÄ ‹Fś™FźVģ‰Fź‰Vģ‹FśFö‹^Ćr‰^Ś‹+Fü‰‹^Ć̉^Ś‹+Fü‰‹^ƒæĢt ƒ~ōuéøž‹^LJr’vöX‰ģ]ĆU‰åPP‹^LJP‹^LJ|‹^€æfu‰ģ]Ƌ^Їt0䘉FžŠ‡u0䘉Fü1ĄPPPP’v’vü’vžøDPčšƒÄ‹^ʇf‰ģ]ĆU‰åƒģ‹^‹v‹ˆ„t‹^‹v‹Gˆ„u‹^‹v‹G‰„v‹^‹v‹G‰„|‹^ʇf‹^LJ~‹^Їu0䘻V÷㰉Áú{‰^ś‹^‹‡v‰Fž’·|¹»XčÅP‰Fü’vü’vžøP’vśčB2ƒÄ‹^‰‡x‰—z‹^‹‡x‹z-ƒŁu Į Éu‹^LJ~ö’‹^LJ|‹^‹‡Z’v’Š^‰ģ]ĆU‰åƒģ&ĒFŚĒFüĒFžĒFųĒFś‹^’wé*‹^¹‹G ‹W ćŃśŃŲāś¹»Rč"P[%’ć‹^ˆ‡g‹^1ɋG ‹W ćŃśŃŲāś¹»RčöO[%’ć‹^ˆ‡h‹^‹v‹G‰„\éČ‹^¹‹G ‹W ćŃśŃŲāś¹»Rč¹O[%’ć‹^ˆ‡i‹^¹‹G ‹W ćŃśŃŲāś¹»RčŒO[%’ć‹^ˆ‡j‹^¹‹G ‹W ćŃśŃŲāś¹»Rč_O[%’ć‹^ˆ‡k‹^1ɋG ‹W ćŃśŃŲāś¹»Rč3O[%’ć‹^ˆ‡l‹^¹‹G‹WćŃśŃŲāś¹»RčO[%’ć‹^ˆ‡méå‹^Їg0䘙¹»RčßN[%’ćS¹»čĶN‰FōFö‹^Їh0䘙¹»Rč²N[%’ćS¹»č N‰FšFņ¹‹Fō‹^öćŃąŃÓāś Fš ^ņ‰Fų‰^ś‹^‹‡\™‰Fü‰Vžé]‹^Їi0䘙¹»RčWN[%’ćS¹»čEN‰FģFī‹^Їj0䘙¹»Rč*N[%’ćS¹»čN‰FčFź‹^Їk0䘙¹»RčżM[%’ćS¹»čėM‰FäFę‹^Їl0䘙¹»RčŠM[%’ćS¹»č¾M‰FąFā‹^Їm0䘙¹»Rč£M[%’ćS¹»č‘M‰F܏F޹‹Fč‹^źćŃąŃÓāś¹‹Vģ‹vīćŃāŃÖāś Ā Ž¹‹Fä‹^ęćŃąŃÓāś Š ó1ɋVą‹vāćŃāŃÖāś Ā Ž‰Vų‰vś¹‹F܋^ŽćŃąŃÓāś‰Fü‰^žėĒFŚź’ė¾ŠiZéųL‹^‹v’vś’vų’vž’vü’vŚ’w’4øDP荃ĉģ]ĆU‰å‹^ƒæru‹^ƒæ|u‰ģ]Ƌ^‹F‰‡Č‹^‹F‰‡Ź‹^LJ̋^LJ΋^LJr‹^LJ|‹^ʇf‹^ʇd‹^‹v1ĄPPPPøü’P’w’4øDPčƒÄ‰ģ]ĆU‰åƒģ‹F‰Fź‹F‰Fģ‹F ‰Fī‹F ‰Fö‹F‰Fų‹F‰Fņ‹F‰FōFčP’vč2G^^‰ģ]ĆU‰å‹^ʇdøü’P’včiś^^‹^‹F‰‡Č‹^‹F‰‡Ź‹^LJ̋^LJ΋F’vPču,^^‰ģ]ĆU‰åƒģ FüPø`Pčū@^^FžPøaPčī@^^¹»‹Fžč„K €PøaPčÅ@^^’vžøaPč¹@^^¹»‹Füč€K-€¹»čtK‰Fśƒ~ś~;ƒ~śu逃~ś*uéwƒ~ś6uénƒ~ś8thƒ~ś:tbƒ~śEt\ø PPča@^^‰ģ]ù»”uč$K% ¹»čK‰FųŠuˆN÷ƒ~ųu&ƒ>fptƒ~üu€~÷uĘuø PPč@^^‰ģ]ƃ>fptƒ>dpt ƒ~üSuč[A q0䘉Fś ‘q0ä˜9Fś}G‹FśFś‹Fś‰Ć‹Füˆ‡q‹Fś‰Ćʇq q0ä˜@¢qĒJpĒZpqøHpPøł’Pč-^^ė ø PPč“?^^‰ģ]ĆU‰åƒģ ‹^¹‹‡x‹—zćŃśŃŲāś¹»RčD= s’Füėź’vžøaPčU9^^č{9‰ģ]ĆU‰åƒģøPøÄPč;9^^øPøÅPč.9^^ĒFž²q~ž4use‹^ž‹Fž‰‡Č‹^ž‹Fž‰‡Ź‹^žĒ‡\ ‹^žĒ‡ZOQRSTUVWXYZ[\]^_`abcdeP‹^žĘ‡g‹^žĘ‡h@‹^žĘ‡i‹^žĘ‡j‹^žĘ‡k‹^žĘ‡l‹^žĘ‡mFž‚ė”Ęuƒ>bptĒDpøĒBp’?Ē@pŠĒFpėĒDp°ĒBp’Ē@p°ĒFppĒ uĘ‘qøPø Pčž^^1ĄPø Pčž^^1ĄPPø²qPč¾üƒÄƒ>`p uĒĢ‹‰ģ]ĆU‰åŠF0ä˜Pø²qP脳^^‰ģ]ĆU‰å€~;učÕ9€~4u} ’64uø8jPč¤ä^^‹4u‰^ü‹:u‰^ś’66uė!ø4uPčC^‰Fžėø4uPč6^‰FžėĒFžź’ė¾.j[é0BĒ6uD‹^ś‰8u‹^ž‰:uø4uP’vüčR=^^ėU‰åƒģ ‹^‹G‰Fśƒ~ś|ƒ~ś|øū’Pé?ø÷fś‰ĆĆXu‰^ž‹^ž‹Fś‰G‹^‹G‹^ž‰‹^’w ’w 1ĄPøPčQCƒźƒŪu Ó Ūtøź’Péō‹^’w ’w 1ĄPøPččA‰Fō‰Nö‹Fō‹^ö-` ƒŪu!ĄtC Ū|ø˜’P齋^žŠG0䘣Lu¹»‹Fō’vöč„A‹LuŃćP’·ži¹»XčoA¹Óą1ŪSPč[B‹^ž‰G‹LuŃ拇ži™»ÖiS’vö’vōRPč©B[ӊ0䘋^ž‰G ¹»‹Fō’vöč&A‹LuŃćP’·ži¹»XčA¹Óą1ŪSPčźB‹LuŃ扩™÷æži‹^ž‰G ‹^‹vž‹G‰D‹^‹vž‹G‰D‹^‹vž‹G‰D‹^žtøź’PéŪĒFųƒ~ų~é•’Fų»‹Fų™÷ū Ņu‹LuC¹‰Ų™÷ł‰Lu‹^žˆWĒPu‹LuŃ拇 j™‹^ō‹Nö)ĆŃu!ŪtA É|먃>Putčv’vžčs^’vžčŠ^’vžčt^‰Füƒ~üté}’’vžčS^‰Füƒ~üuė ƒ~üūtéb’ĒTu ø¤&Pø“PčŚ^^ƒ~üu‹^žƒ~ĒNuƒ~üuøPėøū’PX‰ģ]ĆU‰åƒģ‹^ƒ?uøFPėøJP¹»Xč»?‰Fž‹^‹G‰Fš’w¹»Xč¢?‰Fī‹^øV÷g°‰Ć’vī’všøPĆŗ{Sč!ƒÄ‰Fź‰Vģ1ɋFź‹^ģćŃūŃŲāś¹»č]?%’¹»čQ?‰Fü¹‹Fź‹^ģćŃūŃŲāś¹»č4?%’¹»č(?‰Fś¹‹Fź‹^ģćŃūŃŲāś¹»č ?%’¹»č’>‰Fų‹Fī-1ÉÓč¹»čé>¹»čą>%’¹»čŌ>‰Fö‹Fī-¹Óč¹»č½>¹»č“>%’¹»čØ>‰Fō‹Fź‹^ģ-ƒŪu Ć Ūu¹»‹Fšč…>PøTjPč~ą^^1ĄP¹»‹Fź’vī’vģčf>[YZĮڃéƒŚRQøPYX[ćŃėŃŲāś%’ć‰Fņ‹Fų9Fņt ’vųøxjPč-ą^^čv3’vžø PčH3^^’vžø Pč<3^^’vüøPč03^^’vśøPč$3^^’vųøPč3^^’vöøPč 3^^’vōøPč3^^č&3øPø Pčš2^^‰ģ]ĆU‰åƒģč3‹^‹G‰ĮøÓą‰Fž’w¹»Xč–=P¹»‹Fžč‰= [ Ų¹»čz=£Tu‹Vu…Rut‹Ru Tu‹Fž#Vu‰Fü’6TuøņPč{2^^‹Tu‰Vu‹^ž‰Ruč’2ƒ~üt‰ģ]ƋLuŃćøµ,P’·"jčņ^^ø4uPøż’Pč$8^^‰ģ]ĆU‰å¹»”Tuč’<%šP¹»”Vučļ<%š[9Ćt’6TuøņPč2^^‹Tu‰Vu‰ģ]ĆU‰åP‹^€u’vč?^ Ątø’’PéĪ‹^‹v‹D9Gu1ĄPéŗøPč¢^‹^¹‹G Óą GPč^‹LuŃć‹v‹D÷§jPčz^ƒ>Putø’’Pé|ø4uPø’’Pčb7^^øPčW^’vč‰^‰Fž‹^ŠG0䘹»č,<%ų= tĒFž’’‹^ŠG0䘋LuŃć‹vP‹D÷§j[9ĆtĒFž’’ƒ~žt’včs^ Ątø’’Pė’vžX‰ģ]ĆU‰åƒģ ‹^€uøž’Pé‹^‹G‰Į‹VuÓūöĆuøž’Péėƒ>Ź‹t‹LuŃć’·ņiø÷Pč¼0^^‹^ƒ?uøęPėøÅP¹»Xčs;‰Fś’vśčx^‹^¹‹G Óą GPče^‹^’wč[^‹^’w čQ^‹^’w čG^ Źi0ä˜Pč<^‹LuŃć’·žič.^‹LuŃć’·ęič ^ø’Pč^ƒ>Putøž’Pé=ø4uPø’’Pč6^^’vč/^‰Fžƒ~žt’vžé‹^ŠG0䘹»čĘ:Øu‹^ŠG0䘹»č°:Øt‹^ĘG‹^ŠG0䘹»č“:Øt‹^’wøšjPč©5^^øū’P黋^ŠG0䘹»čg:Øųtøž’P鞋^ŠG0ä˜PŠG0ä˜[ Ų Ątøž’P逋^ŠG0ä˜+G¹»č):¹Óą‹LuŃćP’·ži¹»Xč:[÷ć¹»č:‰Fü‹^ŠG0ä˜+G ‹LuŃć÷§žiFü‰Fü‹^ŠG0ä˜+G Fü‰Füø÷fü9Gtøž’Pė1ĄPX‰ģ]ĆU‰åƒģĒFžƒ~ž|é¤ĒFųĒFüƒ~üd})FśPøōPčÅ.^^¹»‹Fśč|9؀tĒFųė’Füėу~ųuøż’Pėi¹»‹FśčT9Øu1ĄPėT¹»‹Fśč?9Ø@uøż’Pė>FśPøõPče.^^¹»‹Fśč9%’¹»č9‹^^žˆG’FžéS’ĒPuøż’PX‰ģ]ĆU‰åƒģƒ>Put‰ģ]ĆĒFžd’vž’NžX Ą~KFüPøōPč.^^¹»‹Füčø8%Ą¹»č¬8‰Fü¹»‹Füč8=€tėŗ’vøõPč·-^^‰ģ]ĆĒPu‰ģ]ĆU‰åP’vč³ś^øPčx’^‹^’wčn’^ƒ>Putø’’Pėmø4uPø’’PčW3^^øPčL’^’vč~ž^‰Fž‹^ĒG’’ƒ~žu#‹^ŠG0䘹»č8%ų= u ‹^€tĒPu‹^ĘGøü’Pė ‹^ĘG1ĄPX‰ģ]ĆU‰åƒģĒPuč -ĒVuĒTu1ĄPøņPčę,^^ø PøņPčŁ,^^č’,ø4uPø’’Pč®2^^ĒFųXu‹^ųĘGøP藾^’vųčÉż^‰Fü‹^ų1ĄŠGP¹»Xčk7‰FśøPčož^øßPčgž^øPč_ž^ĒFžƒ~ž}ø÷fž‰Ćʇru’Fžėč‰ģ]ĆU‰åĒ6uĒ8uū’‹F™£>u‰@u‹^‰Buø4uPøż’Pč2^^‰ģ]ĆU‰åĒ6u’ø4uPøū’Pčō1^^‰ģ]ĆU‰åƒģč' ø°yPøtPčß1^^ƒ>°y}’6°yøŠjPčā1^^ėŻ‹°y‰^ü‹¶y‰^ś’6²yėø°yPč6^‰FžėĒFžź’ė¾Ęj[éS6DzyD‹^ś‰“y‹^ž‰¶yø°yP’vüču1^^ėŒU‰åƒģĒFų‹^‹G‰Fśƒ~ś|ƒ~ś |øū’PéÄ‹^tøź’Pé³ø÷fś‰ĆĆŹy‰^ž»‹Fś™÷ū‹^ž‰G‹^ž‹Čy9O|øū’Pé‚‹^‹G‹^ž‰‹^’w ’w 1ĄPøPčE7ƒźƒŪu Ó Ūtøź’PéO‹^’w ’w 1ĄPøPčÜ5‰Fō‰Nö‹Fō‹^öƒÓ‹vž+D\u!ĄtC Ū~ø˜’Pé‹^ž‹Fō‹NöGO‰Fō‰Nö¹»‹Fō’vöčf5‹^žP’w ¹»XčU5»÷ć1ŪSPčA6‹^ž‰G¹»‹Fō’vöč45P1ĄPøPč7‹^ž‰W¹»‹Fō’vöč5‹^žP’w ¹»Xč5»÷ć1ŪSPčÜ6SR1ĄPøPčć5‹^ž‰G ‹^‹vž‹G‰D‹^‹vž‹G‰D‹^‹vž‹G‰Dƒ~ų0’Fųƒ~ų|øū’Pė1ƒ>Äjtč’vžč&^’vžč^‰Füƒ~üuʃ~üuøPėøū’PX‰ģ]ĆU‰åƒģ‹^ƒ?uøGPėøKP¹»XčT4‰Fž‹^‹G‰Fš’w¹»Xč;4‰Fī‹^øV÷g°‰Ć’vī’všøPĆŗ{SčØƒÄ‰Fź‰Vģ¹»‹Fźč4%’¹»č÷3‰Fü¹‹Fź‹^ģćŃūŃŲāś¹»čŚ3%’¹»čĪ3‰Fś¹‹Fź‹^ģćŃūŃŲāś¹»č±3%’¹»č„3‰Fų‹Fī-¹»č“3¹»čŠ3%’¹»č~3‰Fö‹Fī-¹Óč¹»čg3¹»č^3%’¹»čR3‰Fō‹Fź‹^ģ-ƒŪu Ć Ūu¹»‹Fšč/3PøöjPč(Õ^^1ĄP¹»‹Fź’vī’vģč3[YZĮڃéƒŚRQøPYX[ćŃėŃŲāś%’ć‰Fņ‹Fų9Fņt ’vųøkPč×Ō^^č (’vžø Pčņ'^^’vžø Pčę'^^’vüøPčŚ'^^’vśøPčĪ'^^’vųø‚PčĀ'^^’vöøPč¶'^^’vōøPčŖ'^^čŠ'‰ģ]ĆU‰å‹^ƒ?uøPėø P¹»XčW2£¤y‹^¹‹GÓą G £¦y’w¹»Xč62%¹Óč‹^P’w»Xč 2[ Ų¹»č2£Øy‹^’w¹»Xč2%’¹»čõ1£ŖyǬyĒ®yøPčN^ Ątø’’PėJøPø Pčō&^^ø°yPø’’PčĢ,^^’vč/^ Ąu1ĄPė ‹^ŠG0ä˜%?=uč_ėĒÄjø’’PX‰ģ]ĆU‰åPPFüPø Pč°&^^1ĄPø#Pč”&^^÷Füu1ĄPé‹Ē¤y‹^¹‹GÓą£¦y1ĄPčÆ^ Ątø’’PėfĒFžƒ~ž}EøPčG^ Ątø’’PėIFüPø PčH&^^¹»‹Füč’0%’¹»čó0‹^^žˆG’Fžėµ‹^ŠG0䘨?tø’’Pė1ĄPX‰ģ]ĆU‰åƒ>Äjt‰ģ]ĆøPčŁ^ Ąu ’vø PčŃ%^^‰ģ]ĆU‰åPPĒFž’vžø!Pč·%^^ĒFü~ü'}$FžPø!Pč®%^^¹»‹Fžče0Øuė’FüėÕ÷Fžtø@kPčs+^ø’’Pė ĒÄjčPX‰ģ]ĆU‰åPǤy Ǧy1ĄPč^ Ątø’’Pé č`%¹‹šuÓūSč:’^¹»”šuč÷/%’Pč&’^’6œuč’^¹‹žuÓūSč’^¹»”žučĶ/%’Pčüž^¹‹ uÓūSčīž^¹»” uč«/%’P茞^’6¢učŅž^čī$č& Ąt ĒÄjø’’Péƒ>Čyé§Ē¦y 1ĄPčŻ^ Ątø’’Péīč®$¹‹uÓūS舞^¹»”učE/%’Pčtž^’6’učlž^¹‹”uÓūSč^ž^¹»””uč/%’PčJž^¹‹–uÓūSč<ž^¹»”–učł.%’Pč(ž^’6˜uč ž^č<$čt Ąt ĒÄjø’’Pė_ĒFž‹Čy9^ž}NǤy¹‹FžÓࣦyĒ®yøPč^ Ątø’’Pė(ø°yPø’’Pč”)^^čż Ąt ĒÄjø’’Pė’Fžė©1ĄPX‰ģ]ĆU‰åPøPčˆ^ ĄuFžPø Pč#^^÷Fžtø’’Pė 1ĄPė‰ģ]ĆX‰ģ]ĆU‰åPǤy 1ĄPč–^ Ąu?øPč?^ Ąu3FžPø PčF#^^øPč&^ ĄuFžPø Pč-#^^÷FžtĒÄjø’’‰ģ]ĆU‰åƒģĒFžFüPø!Pč#^^‹F!Fü’vž’FžX='}ƒ~ütہ~ž'| ĒÄjø’’Pė1ĄPX‰ģ]ĆU‰åPPĒFž’vø#PčØ"^^’vø"Pčœ"^^ĒFž~ž,}FüPø!Pč“"^^÷Fütė’Fžėށ~ž,u ĒÄjø’’Pé|č€"ĒFžƒ~ž}hFüPø!PčV"^^÷Fütė÷FüućĒÄjčV"ø’’PėB¹»‹Füčī,%= tĒÄjč3"ø’’Pė‹FžŃą‰Ć’·¤yø Pčš!^^’Fžė’č"1ĄPX‰ģ]ĆU‰åƒģFžPø"PčŻ!^^¹‹FžÓč%»č,‰Fų‹Fž%¹»č},‰Fö¹»”źmčn,‰Fś¹»”ģmč_,‰Fü1Ą¹‹^üćŃćŃŠāśPS¹»Xč@,1ŪYFśŁQ¹»č.,‰FņFō1ĄPø@PPø¤uPøPøf|Pč” ƒÄRP’vō’vņčg ƒÄ ¹‹FųÓą‰ĆøšuPĆ¤uSčž^^¹‹FöÓą‰ĆøuPĆ¤uSč†^^1ĄPøPPø¤uPøPøf|PčI ƒÄRP1ĄPøuPč ƒÄ ¹» ¤u0äč™+£ČyĒFžƒ~žsø÷fž‰Ć‹œu‰Öy‹Fž‰FžėßĒnzĒpz‹nz’6pz‰ŲyŚy”œu™RP”šu™‰Ę‰×[Xč–,¾1’‰Ć‰ŠčŠ,£Üy‰ŽyĒFžƒ~ž sø÷fž‰Ć‹’u‰Öy‹Fž‰Fžėß”’u™RP”u™‰Ę‰×[XčJ,¾1’‰Ć‰Šč>,£rz‰tzƒ>Čy~ č¢ś ĄtĒČyĒFž¹»”Čy’vžčŗ*[9Ćseø÷fž¹»č¦*£“yĒŗyǼyĒøyĒĀy¤uǶyś’Dzyø°yPčō^=t ’vžøXkPčiĢ^^ø÷fžPčk^‹Fž‰Fžė‡‰ģ]ĆU‰å‹^‹‹^‰‹^1ĄŠGP¹»Xč,*‹^‰G‹^‹v‹G‰D‹^‹v‹G‰D‹^1ĄŠGP¹»Xčū)‹^‰G‰ģ]ĆU‰åƒģ ĒFžƒ~ž|éźĒFöĒFų‹FFž»÷ć‰ĆĆŹy‰^ś¹»‹Fžč°)¹ÓąĘ¹»čŸ)‰Fü‹^ü‹vś‹‡¤u‹Ÿ¦u‰D‰\‹^ś’w’w1ĄPøPčŪ*ƒźƒŪu Ó ŪtO‹^ś‹G’w‰FöFų’w’w1ĄPøPčm)ƒŃ¾1’‰Ć‰Čč‹*‹^ś‰G‰W‹^ś‹G‹O+FöNų‰Fö‰Nų‹Fü‰Ć‹‡¤u‹¦u+FöNų‹^ś‰G‰O’Fžé ’‹F@»÷ć‰ĆĆŹySč^‰ģ]ĆU‰åPPĒFžƒ~ž|éšĒFüƒ~ü|éÜø÷fü‰Ć^‹G‹O-ƒŁu Į ÉuE‹Fü@»÷ć‰Ć^‹G‹O-ƒŁu Į Ét#‹Fü@»÷ć‰Ć^ø÷fü‰ĘvSVč‡^^érø÷fü‰Ć^‹Fü@¹÷į‰Ęv‹G‹O+DLu!ĄtA É~B‹Fü@»÷ć‰Ć^‹G‹O-ƒŁu Į Ét ‹Fü@»÷ć‰Ć^ø÷fü‰ĘvSVč^^’Füé’’Fžé’‰ģ]ĆU‰åƒģ‹v¹č)~⹉ęņ„‰ō‹v¹čš(‹~¹‰ęņ„‰ōvā¹čŪ(‹~¹‰ęņ„‰ō‰ģ]ĆU‰åPøžzPøtPčŒ"^^’6{é}øžzPč”^‰FžévøžzPč^‰FžėiøžzPč"^‰Fžė\øžzPčµ^‰FžėOøžzPč^‰FžėBøžzPč9^‰Fžė5øžzPč®^‰Fžė(øžzPčµ^‰FžėøžzPčš^‰FžėĒFž÷’뾂k[éŗ&‹^ž‰{øžzP’6žzčč!^^éQ’U‰åƒģ‹^‹G‰Fų‹G‰Fö‹G‰Fōƒ~ų|ƒ~ų} ƒ~ö|ƒ~ö|øõ’PéČøV÷fö°‰ĆĆŗ{‰^žøV÷fų°‰ĆĆŗ{‰^üøV÷fö°‰ĆĆŗ{‰^śĒFņV’vņ’NņX Ąt’vüƒFü[‹vśŠˆƒFśėą‹^žƒĆ‰^š’7¹»Xč.& ¹»č"&‹^š‰‹^ž‹Fō‰G4‹^žĒ‹^žĒG6ĒG8‹^žĒG:ĒG<‹^žĒG>ĒG@‹^žĒGBĒGD1ĄPX‰ģ]ĆU‰åƒģ ‹^‹‰Fč‹^‹G‰Fę‹G ‰Fāƒ~ęų|ƒ~ę|øõ’PémøV÷f氉Áú{‰^žøV÷f谉Áú{‰^üĒFź1Ą‹^ź‰^š‰Fņ‹Fā‰Fī‹^žƒĆ ‰^ģ’vź’vīøP’vüčŽƒÄ‰Fų‰Vś‹Fų‹^ś-ƒŪu Ć Ūu ø€PøškPč'Ē^^’vź’vģøPø¾}Pč£ƒÄ‰Fō‰Vö‹Fō‹^ö-ƒŪu Ć Ūu ø€PøøkPčģĘ^^’vņ’vš’vö’vō’vś’vųč8ƒÄ ‹^ž’w"¹»XčĮ$‹^ž‰G‹^ž’w(¹»Xč«$‹^ž‰G‹^ž’w(¹»Xč•$‹^ž‰G‹^ž’w(¹»Xč$‹^ž‰G‹^ž‹G‰FäƒĆ‰^ą’7¹»Xč^$%ż’¹»čR$‹^ą‰ƒ~ät‹^žƒu’vžč$ ^1ĄPX‰ģ]ĆU‰åƒģ‹^‹G‰Fü‹G ‰Fśƒ~ü|ƒ~ü|øõ’PėtøV÷fü°‰ĆĆŗ{‰^ž‹^ž‹Fś‰G‹^žĒG‹^žĒGFĒGH‹^žƒĆ‰^ų’7¹»XčĆ#%÷’¹»č·#‹^ų‰‹^žƒu’vžč ^’vś’vüčŠ^^1ĄPX‰ģ]ĆU‰åƒģ‹^‹G‰Fö‹G‰Fōƒ~ö|ƒ~ö} ƒ~ō|ƒ~ō|øõ’Pé!øV÷fö°‰ĆĆŗ{‰^žøV÷fō°‰ĆĆŗ{‰^ü‹^žƒĆ>‰^ņ‹^ņ‹vü‹~ü‹D>‹L@E6M8O‰‰O‹^žƒĆB‰^ņ‹^ņ‹vü‹~ü‹DB‹LDE:M<O‰‰O’vüčQ ^‹^üĒGFĒGH1ĄP’vōč^^‹^ü’w¹»Xčø"ØuélĒFžŗ{~žŹƒs`‹^žƒJuėO‹^ž‹Fü9GJu‹^ü‹vž‹GL‰DJė<‹^ž‹GJ‰Fś‹^ś‹GL‰Fųƒ~ųt‹Fü9Fųu‹^ų‹vś‹GL‰DLė‹Fų‰FśėӃFžV뙋^üĒG1ĄPX‰ģ]ĆU‰åPP‹^‹G‰Füƒ~ü|ƒ~ü|øõ’PėøV÷fü°‰ĆĆŗ{‰^ž‹^ž‹_‰{1ĄPX‰ģ]ĆU‰åPP‹^‹G‰Füƒ~ü|ƒ~ü|øõ’Pė]øV÷fü°‰ĆĆŗ{‰^ž‹^ž‹v‹G6‹_8‰D‰\‹^ž‹v‹G:‹_<‰D‰\ ‹^ž‹v‹G>‹_@‰D ‰\‹^ž‹v‹GB‹_D‰D‰\1ĄPX‰ģ]ĆU‰åø€PøÖkPčbĆ^^‰ģ]ĆU‰åƒģ‹^‹G‰Fī‹G‰Fģ‹G‰Fźƒ~ī|ƒ~ī|øõ’PéæøV÷fÁú{‰^žĒFōöz‹^ž‹G‰Fš’vģ’vžøözPč^ƒÄĒFņ‹Fš+Fņ‰Fš’vņ’vōøPø¾}PčnƒÄ‰Fś‰Vü’vņ’všøP’vžčUƒÄ‰Fö‰Vų‹Fö‹^ų-ƒŪu Ć Ūu ø€PøŲkPčžĀ^^1ĄP’vņ’vų’vö’vü’vśčźƒÄ ‹^ž‹Fš‰G‹^ž‹Fź‰G1ĄPX‰ģ]ĆU‰åƒģ‹^‹G‰Fž‹G‰FüŠG0䘉FśŠG0䘉Fų‹G ‰Fö‹G‰Fō‹G’w‰FčFź~žüu‹^‹G ’w ‰FšFņė%øV÷fž°‰Ć’vč’vö’vśĆŗ{Sč†ƒÄ‰Fš‰Vņ~üüu‹^‹G’w‰FģFīė%øV÷fü°‰Ć’vč’vō’vųĆŗ{SčIƒÄ‰Fģ‰Vī‹Fš‹^ņ-ƒŪu Ć Ūt‹Fģ‹^ī-ƒŪu Ć Ūuøņ’Pė’vź’vč’vī’vģ’vņ’vščŃƒÄ 1ĄPX‰ģ]ĆU‰åPPøV÷f°‰ĆĆŗ{‰^ž‹^žƒTu’ꋋ^žƒĆT‰^ü‹FH‰ĮøÓą ‰1ĄPč^‰ģ]ĆU‰åPPøV÷f°‰ĆĆŗ{‰^ü‹^ü’w¹»XčōØt ‹^üƒPtt‰ģ]ĆĒFžj~~žŹƒsc‹^žƒTtTĒ{@‹Fž-ŗ{»V™÷ū-£{‹^ž‹_T‰{’拸žzP’vø’’Pč ƒÄ Ąt ø€PøōkP艡^^‹^žĒGT‰ģ]ƃFžVė–‰ģ]ĆU‰åƒģ ƒ~ w1ĄPPéé‹F F-¹Óč‰Fžƒ~t‹^‹v‹G*D&9FžsøPėøPF¹‹FÓč‹^Pø÷fƋvø÷fʋG$D [9Ćr1ĄPP郋^ø÷fĆ1Ą‹_"‰^ś‰Fü¹‹Fś‹^üćŃąŃÓāś‰Fś‰^ü1Ą‹^‰^ö‰Fų¹»‹Fö’vųč°‹^Pø÷fù‹G Óą1ŪYZ)ĮŚRQ¹»Xčˆ‰FöFų‹Fö‹^ųFś^üSPXZ‰ģ]ĆU‰åƒģ ø PPč‹^^ƒ>Ź‹tƒ~śu ø Pø Pčq^^‹F÷Ų‰ĮøÓą‰Fų’v’vø’’PčšƒÄ ĄtC¹»”Ø{č‰Fśƒ~żu’ģ‹ėP¹»‹Fų’6Ø{čö[ Ų£Ø{‹F÷ŲŃą‰Ć‹F‰‡–{ė(‹Fų÷й»’6Ø{čĢ[!ƉØ{¹»”Ø{蹉Fśƒ~śt^ĒFžƒ~žS‹Nž‹FśÓųØtB‹FžŃą‰Ć‹Fž÷Ų’·–{Pø’’PčóƒÄ‰Füƒ~üu‹NžøÓą÷й»’6Ø{č^[!ƉØ{’Fžė§ƒ>°{tƒ>ź‹}>ź‹üuč˜‰ģ]ĆU‰åPPøV÷f°‰ĆĆŗ{‰^žƒ~ų| ƒ~|ƒ~tt ‹^žĒž’‰ģ]ƃ~tƒ~| ‹^žĒų’‰ģ]Ć÷Ft0’v ’v’včIƒÄ‰Füƒ~tƒ~üt‹Fü‹^ž‰ƒ~üt‰ģ]Ć÷Ft’v ’v’včĮƒÄ‰Fü‹Fü‹^ž‰‰ģ]ĆU‰åƒģƒ~|ƒ~t ƒ~tø’’P鋸V÷f°‰ĆĆŗ{‰^žøV÷f°‰ĆĆŗ{‰^ü‹^ü’w¹»Xč;Øtø’’PéL‹^ž‹G*‰Fņ‹F‰Fų¹‹FųÓč‰Fö‹Fų-Óč‰Fō‹Fö9Fōr‹^ž‹Fō+G&9Fņwøö’Pé‹^ü’w¹»XčŪØtn‹^üƒPtt ‹^ü‹F9GPuZ‹^ü‹vü‹~ž’wN’t(’v’u(’vč¤ƒÄ ‹^üƒĆ‰^š’7¹»Xč%÷’¹»č„‹^š‰‹^üƒt錒vüčY^邃~’uøü’Péx‹^ž‹F‰GN‹^žƒĆ‰^š’7¹»Xč> ¹»č2‹^š‰’vžč—^‹^ü‹GJ‰Fśƒ~śu ‹^ü‹Fž‰GJė‹^śƒLt ‹^ś‹GL‰Fśėģ‹^ś‹Fž‰GL‹^žĒGL1ĄPX‰ģ]ĆU‰åƒģ øV÷f°‰ĆĆŗ{‰^ž‹^ž‹GJ‰Füƒ~üu鳋Fü-ŗ{»V™÷ū-‰Fųƒ~tt ‹Fų9Fté~‹^ž‹vü‹~ü’v’w(’tN’u(’vųčsƒÄ ‹^üƒĆ‰^ö’7¹»Xč_%ū’¹»čS‹^ö‰‹^üƒu’vüč+^‹^ž‹GJ9Füu‹^ü‹vž‹GL‰DJė ‹^ü‹vś‹GL‰DL1ĄPėn‹Fü‰Fś‹^ü‹GL‰FüéD’‹^ž‹F‰GP‹^ž‹F‰GN‹^žƒĆ‰^ö’7¹»XčŪ ¹»čĻ‹^ö‰’vžč4^ƒ>ę‹~ƒ~u ƒ~tu1ĄP菳^1ĄPX‰ģ]ĆU‰åPƒ>°{tĒFžėƒ>²{tĒFžėĒFž‹ꋉ苋FžŃą‰Ćƒæ°{t:‹FžŃą‰Ć‹‡°{-ŗ{»V™÷ū-£ź‹‹FžŃą‰Ć‹Ÿ°{‰ø{ƒ>ź‹|‹ø{‰¶{ėĒź‹üĒø{~‹ø{‰¶{‰ģ]ĆU‰åPPč[ ‹F-ŗ{»V™÷ū-‰Füƒ~ü}1ĄPėƒ~ü}øPėøPFž‹FžŃą‰Ćƒæ°{u‹FžŃą‰Ć‹F‰‡°{ė‹FžŃą‰Ć‹ŸŖ{‹F‰GR‹FžŃą‰Ć‹F‰‡Ŗ{‹^ĒGRčģ ‰ģ]ĆU‰åƒģčÖ ‹F-ŗ{»V™÷ū-‰Füƒ~ü}1ĄPėƒ~ü}øPėøPFś‹FśŃą‰Ć‹‡°{‰Fžƒ~žu‰ģ]ƋF9Fžu‹^ž‹FśŃą‰Ę‹GR‰„°{肾ėO‹^ž‹F9GRt‹^ž‹GR‰Fžƒ~žuę‰ģ]Ƌ^ž‹_R‹vž‹GR‰DR‹^žƒRt ‹^ž‹GR‰Fžėģ‹FśŃą‰Ć‹Fž‰‡Ŗ{č' ‰ģ]ĆU‰åč ƒ>“{uč ‰ģ]Ƌ®{‹“{‰OR‹“{‰®{‹“{‹_R‰“{‹®{ĒGRčģżčā ‰ģ]ĆU‰åP謸({PøtPč„^^‹*{‰^ž’vžė-ø({PčH^ė*čjė%ø({Pč^ėč¹ė’6*{ølPč>ø^^ė¾l[éżĒ*{ƒ~žt§ø({P’6({č&^^ė˜U‰åƒģ ‹^‹G‰Fü‹G ’w ‰FųFś‹G‰FöøV÷fü°‰ĆĆŗ{‰^ž‹^ž‹GF‹OH-ƒŁu Į Éu1ĄPPė‹^ž‹GF‹OH+ī‹š‹QP1ĄPø<PčĻQP2{4{‹Fų‹^ś-ƒŪu Ć Ūu1ĄPPė‹Fų‹^śī‹š‹SP‹^žXY‰GF‰OHƒ~ü}‹Fü÷ŲŃą‰Ć‹Fö‰‡{ĒB{’’ĒD{’ĒFžŗ{~žŹƒsJ‹^ž‹GF‹OH-ƒŁu Į Ét-‹^ž‹GF‹OH+B{D{u!ĄtA É}‹^ž‹OF’wH‰B{D{ƒFžV믉ģ]ĆU‰åĒ*{’6š‹’6ī‹1ĄPø<PčłF{H{£2{‰4{‰ģ]ĆU‰å‹^’w ’w ’6š‹’6ī‹1ĄPø<PčČ[Z)ĆʉF{‰H{‰ģ]ĆU‰åƒģ ‹싉^ü‹Fü@™ī‹š‹£ī‹‰š‹‹ģ‹+^ü‰ģ‹‹B{‹D{+ī‹š‹u!ŪtA É~éŽĒB{’’ĒD{’ĒFžŗ{~žŹƒréƋ^ž‹GF‹OH-ƒŁu Į Éu颋^ž‹GF‹OH+ī‹š‹u!ĄtA ÉB‹Fž-ŗ{»V™÷ū-‰Fśƒ~ś|øP’vśčrō^^ė‹Fś÷ŲŃą‰Ć‹‡{’Š‹^žĒGFĒGH‹^ž‹GF‹OH-ƒŁu Į Ét-‹^ž‹GF‹OH+B{D{u!ĄtA É}‹^ž‹OF’wH‰B{D{ƒFžVé3’čH’lu>‹@{9¶{učˆüĒl‹¶{‰@{ƒ>†{tƒ>ˆ{~ ‹‚{9„{učŒ‹„{‰‚{‰ģ]ĆU‰åPƒ>č‹|‹¶{ƒĆ6‰^ž‹^ž‹‹OƒŃ‰‰Oė‹¶{ƒĆ:‰^ž‹^ž‹‹OƒŃ‰‰O‰ģ]ĆU‰åƒģĒFž®M‹Fž%’‰Fü¹‹FžÓč%’‰Fśø6PøCPčŃ^^’vüø@PčÅ^^’vśø@Pč¹^^‰ģ]ĆU‰åƒģč1Ū¹ćŃąŃÓāś£b{‰d{‹RfPf¹Óć1Ą‰R{£T{ĒN{ĒP{ øj{PøtPčN ^^ƒ>j{} ’6j{ø‹^ƒu‹Fü™RP’vö’vō’vś’vųč·ƒÄ ė‹Fü™RP’vś’vų’vö’vōčƒÄ ’vüX‰ģ]ĆU‰åP‹^‹G‰Fžƒ~ž|ƒ~ž|øś’PėJ‹^¹‹FžÓą‰Ę‹G ‹_ ‰„Z{‰œ\{‹^‹G™¾1’‰Ć‰Šč6‹^G W ¹‹^žÓ扇J{‰—L{1ĄPX‰ģ]ĆU‰åƒģč»FčPøtPč¼ ^^’vźėFčPč^ėäFčPčä^ėŚFčPč‰^ėŠ¾Xl[éKU‰åƒģĒFśƒ>†{tĒFśõ’‹^ƒĒFśź’‹^øV÷g°‰ĆĆŗ{‰^ö‹^‹v’w’tøP’vö襚ƒÄ‰Fņ‰Vō‹Fņ‹^ō-ƒŪu Ć ŪuĒFśö’ƒ~śtéĻčQ‹^‹‰’{‹^‹_‰{‹^‹_‰ˆ{‹^‹_‰Ž{¹‹Fņ‹^ōćŃūŃŲāś£Œ{¹»‹Fņ’vōčø[%ć£Š{ĒFž~žč}k‹”{CFųPSčĻ^^¹»‹Fųč†%°=udž{č×ĒFśüė8¹»‹Fųčb%°=uĒFü~üč}’Füėō’vųčų^ĒFśū’ė’Fžė޹»‹Fųč*%°=uĒFśõ’‹^‹v’vś’w’4øDP蕃ĉģ]ĆU‰åP‹^ƒu’6Ž{ėøū’PFž>{üt%’vž’6{’6’{øCPčZƒÄƒ~žūu ‹^’wčp^dž{‰ģ]ĆU‰åƒ>†{u‰ģ]Ćdž{Ēˆ{ǐ{ü‹^‹vøü’P’w’4øDPčƒÄ‰ģ]ĆU‰åƒģ‹F‰Fź‹F ‰Fī‹F‰FģFčP’vč\^^‰ģ]ĆU‰å¹»‹Fč< Ø tøhlPčX^¹»‹Fč$ Øuø‚lPč@^¹»‹Fč ØuøšlPč(^‰ģ]ĆU‰åPƒ>bptøxPėø¼P¹»XčŻ £”{dž{ø”{»SPčš^^ĒFžƒ~žd}’Fžėõø”{» SPčĻ^^‰ģ]ĆU‰åƒģ‹Ž{9ˆ{t ø PPč±^^ƒ>†{u‰ģ]ƃ>ˆ{驋”{CFžPSč^^¹»‹FžčT %°=un’6Š{’6Œ{čæ^^ˆFł¹»ŠFł0äč. ‰Fü’vü’6”{čL^^ø”{»SPč;^^ø”{»SPč*^^’Š{’ˆ{’„{ĒFśƒ~śd|éj’’Fśėņ¹»‹FžčŅ %°=u‰ģ]ĆĒŠ‹ƒ>ˆ{u1ĄPė’vžŌ‹øĪ‹Pøų’Pč4ī^^‰ģ]ÜśUPSQRVW‰å‹F‹~¹ŃŲŃßāśŽĒ‹F‹v¹ŃŲŃŽāśŽŽ‹~ē‹vę‹V ‹N÷Į€u÷Ā’’uė¹€‰Č÷Įtņ¤ėŃéņ„‹V ‹N1Ū)ĮŚ Éu Ņu _^ZY[X]Ć‰V ‰NF^F^éu’U‰åœśQVW‹F‹~ŽF‹v Ž^ &‰ƒĘƒĒ¹ ņ„_^Y]ĆS‰ćPR‹W‹GīZX[ĆS‰ćPR‹Wģ0ä‹_‰ZX[ÜśĄlĆūĆ’6ĄlĆU‰åSV‹^‹v‹F‰‹D‰G‹D‰G‹D‰G^[]Ć[U‰åWV)Ä;&Älv’ćĒÄl‹ø{ĒG2’6ꋸČlPčS¬ėäfü^_]ĆĶ$0<0tøĆ1ĄĆU‰åQRW¹ŗšüŽF‹~óm_ZZ‰ģ]ĆU‰åQRV¹ŗšüŽ^‹vóo^ZZ‰ģ]ĆU‰åVWSQR‹v‹~#>Bp‹N ŗŚ‰ūĖĖ+Bpƒė~Ńū)ىĘl÷bptģØtūœśŽFƒžt.š’Ālņ„ƒū~‰^ ĒFƒ~t§‹6ĘlövėœZY[_^]Ćøņ«ėŅU‰åŽF‹^&Š0ä]Ćśø ę č?ø@ŽŲø4£rø’’ŽŲ”P”PĖśø ę č1ĄĶø@ŽŲø4£rø’’ŽŲ”P”PĖü¹Ž¾ęl1’ŽĒņ„ĆūėżU‰åƒģønPč1^øPøō‹PøPø¾}P膼ƒÄ‰Fģ‰VīĒFžŗ{~žŹƒréÅ‹^ž’w¹»XčĪØté§‹^ž‹G"‰Fö‹vž‹G0D.‰Fō1Ą¹‹^öćŃćŃŠāśĆ‰^š‰Fņ’vņ’vš1ĄPøPčŖ‰Fś1Ą¹‹^ōćŃćŃŠāśĆ‰^š‰Fņ’vņ’vš1ĄPøPčz‰Fų‹Fž-ŗ{»V™÷ūPč0^‹^ž‹vž‹~ž’vų’vś’w<’w:’t8’t6’u’w’t’u4ø`nPč:ƒÄ‹^žƒu ø†nPč'^ė‹^ž‹GPPčŽ^‹Fž-ŗ{»V™÷ū-‰Fźƒ~ź鬹‹FźÓą‰Ć‹‡ Œ‹ Œ-ƒŁu Į Éu鉹‹FźÓą‰Ć1ĄPøP’vī’vģ’· Œ’· ŒčóūƒÄ ĒZŒĒ\ŒĒFüō‹~üŒs/‹^üŠ0ä˜= ~‹^üŠ0䘹»čV=r‹^üĘƒFüėŹƒ~źu øŽnPč_^ė øō‹Pø–nPčQ^^øšnPčH^ƒFžVé1žøœnPč9^‰ģ]ĆU‰åƒģøžnPč'^ĒFžj~~žŹƒr馋^ž’w¹»XčąØt鼋^ž‹G"‰Fų‹vž‹G0D.‰Fö1Ą¹‹^ųćŃćŃŠāśĆ‰^ņ‰Fō’vō’vņ1ĄPøP載Fü‹Fö+Fų1Ū¹ćŃąŃÓāśƒÓ‰Fņ‰^ō’vō’vņ1ĄPøP芉Fś‹Fž-ŗ{»V™÷ūPč@^‹^ž‹vž‹~ž’vś’vü’w0’t.’u,’w*’t(’u&’w$’t"’u øānPčGƒÄƒFžVé’‰ģ]ĆU‰åƒ~|u ønoPč)^ė2ƒ~|ƒ~ ‹FŃą‰Ć’·nøvoPč^^ė‹F-PøzoPčö^^‰ģ]ĆU‰åƒģ ƒ~u¹‹FÓą‰ĆLJ ŒĒ‡ Œ‰ģ]Ƌ^øV÷f°‰ĘøPƒĆSøPĘŗ{VčēƒÄ‰Fü‰Vž‹Fü‹^ž-ƒŪu Ć Ūu‰ģ]ĆøPøņ‹PøPø¾}PčŻęƒÄ‰Fų‰Vś1ĄPøP’vś’vų’vž’vü蒳ƒÄ øV÷f°‰ĆøP’6ņ‹øPĆŗ{SčœęƒÄ¹‹^Ó扇 Œ‰— Œ‰ģ]ùė ¹ė¹ėU‰å‹F‹^Ķ ]ĆU‰åƒģ$^‰^ų‹^€?uéc‹^€?%t’vƒF[Š0ä˜PčšĮ^ėŁĒFžƒF‹^Š0ä˜=0|+‹^Š0ä˜=9‹^Š0ä˜-0Pø ÷fž[Ɖ^žƒFėȋFų‰Fī‹Fų‰Fō‹^Š0ä˜P醒vųƒFų[‹‰Fü‹Fü™‰Fš‰VņĒFś én’vųƒFų[‹‰Fü¹»‹Füč‰Fö1Ą‹^ö‰^š‰FņĒFśé?’vųƒFų[‹‰Fü¹»‹Füčę‰Fö1Ą‹^ö‰^š‰FņĒFśé’vīƒFī[‹’w‰FšFņĒFś ‹Fī‰Fųéļ’vīƒFī[‹’w‰FšFņĒFś‹Fī‰FųéĪ’vīƒFī[‹’w‰FšFņĒFś‹Fī‰Fųé­’vųƒFų[‹‰Fü’vüčKĄ^ƒF酾’vōƒFō[‹‰Fą‹Fō‰Fų‹Fą‰FŽ’vąƒFą[Š0äˆFݘ Ąt ŠFŻ0ä˜Pč Ą^ė߃F‹Fą+FŽH+Fž÷Ų‰Füƒ~üé/ž’vü’NüX Ąué!žø PčŲæ^ėčø%PčĪæ^’vƒF[Š0ä˜P輿^éśż¾€oZ驍FāP’vś’vņ’vščUƒÄ‰Fü‹Fž+Fü‰Fśƒ~ś~’vś’NśX Ąt ø Pčzæ^ėė‹FüH‰Fśƒ~ś|‹vśŠBā0ä˜Pč]æ^’NśėēƒF钿‰ģ]ĆU‰åƒģ ĒFų‹F‹^-ƒŪu Ć Ūu ‹^ Ę0øPéČ‹F‹^-ƒŪu!ĄtC Ū}ƒ~ u‹^‹F÷Ų÷Ū‰^‰F’FųĒFüƒ~ü }‹^ü^ Ę’FüėģĒFüƒ~ u@’v’v1ĄPø Pč.‹^ü^ ˆ‹^ü^ Š0䘙‹^‹N)ĆŃQS1ĄPø PčƉF‰Nƒ~u=¹»‹F’včƒ[%ć‹^ü^ ˆ¹‹F‹^ćŃūŃŲāś%’’ć’‰F‰^ƒ~u=¹»‹F’vč@[%ć‹^ü^ ˆ¹‹F‹^ćŃūŃŲāś%’’ć’‰F‰^’Fü‹F‹^-ƒŪu Ć Ūté’ĒFś‹FüH‰Fžƒ~ž|b‹^ž^ €?uƒ~śu ‹^ž^ Ę ėA‹^ž^ Š0ä˜= }‹^ž^ ‰^ö‹^öŠ0ä˜0ˆė‹^ž^ ‰^ö‹^öŠ0ä˜7ˆ’Fś’Nžė˜ƒ~ųtø-P’v ’vü’Fü[XĆXˆ’vüX‰ģ]ÌŲĆøbŒĆśWø )’ŽĄ&Ǥ„1Ū&‹ū¤„u= uę_ūƋ+\;\w Ńć‹X…Ūu ‰Ó…ŪučR’歓­‘I|­9Š­u÷“…Ūuč;’ć_9Ėtƒūt ƒūuƒłu Z’ēƒłu1ŅR’ēPčU‰åW)’‹V ‹F‹^‹N…Ņy ÷Ś÷؃Ś÷ׅŪy ÷Ū÷كŪ÷×č…’t÷Ś÷؃Ś_]‰ŃĀ…Ūu‰Ć‰Š)Ņ÷ń“÷ń‰Ń‰Ś)ŪĆUWV)’„’u&GWˆßˆėˆĶ(É‰ĶˆÅ(ɈąˆŌˆņ(öRPQ‰é9Śr ø’’ė WWRP‰Š)Ņ÷ó‰Å÷į_)ĒƒŅ‰Ö‰Ų÷嚃Ņ^)ʃŅX)Šy MĻŽėō‰ł‰ó_…’tˆéˆŻˆūˆĒ‰č)Ņ^_]Ɖę‹\‹D Ąu1Ņ‹L‹D÷ó‘÷ó‰Ē1Ū‹D‹T¹ŃąŃŅŃÓ9ßwr 9TvāķėŪ+Tū@āćėŃ[‰ŹŃłs0ä¬P’ć)Ō‰ēņ„’ć÷ę‰Į‰ų÷ćĮ‰š÷ćŹĆ‰ę‹\‹D™9Āu1!Ņ}÷Ūt)1Ņ‹L‹D!Ą}÷Ų÷ŁŠ÷ó‘÷ó1ۃ|}÷Ū÷ڃŪ‰Ē1Ū!’}÷ß÷\ߋD‹T!Ņ}÷Ś÷ŲڹŃąŃŅŃÓ9ßwr»9Tvāķė¹+Tū@āć믉ę‹\‹D Ąu1Ņ‹L‹D÷ó‘÷ó1Ū‰Ē1Ū‹D‹T¹ŃąŃŅŃÓ9ßwrŻ9TvŲāķėŪ+Tū@āćėŃø¤oėø»oėøŅoėøéoėøpė øpėø-pė»SPøPčč…ōU‰åĒLŒ‹^‰NŒ‹^‰PŒ‹^‰TŒøJŒPøPø»JŒ¹Ķ ‰ģ]ĆoRRS232 interruptUnexpected trap: vector < 16 pc = 0x%x text+data+bss = 0x%x Unexpected trap: vector >= 16 This may be due to accidentally including a non-MINIX library routine that is trying to make a system call. pc = 0x%x text+data+bss = 0x%x Trap to vector 0: divide overflow. pc = 0x%x text+data+bss = 0x%x Kernel panic: %s %d Type space to reboot 1234567890-= qwertyuiop[] ‚asdfghjkl;'`€\zxcvbnm,./*ƒ „”¢£¤„¦§Ø©Ŗ…ˆ·ø¹‰“µ¶Œ±²³0!@#$%^&*()_+ QWERTYUIOP{} ‚ASDFGHJKL:"~€|ZXCVBNM<>?*ƒ „‘’“”•–—˜™š„‹789‰456Œ1230.1234567890-^ qwertyuiop@[ ‚asdfghjkl;:]€\zxcvbnm,./*ƒ „”¢£¤„¦§Ø©Ŗˆ·ø¹‰“µ¶Œ±²³0. Š “²¶ø‹ /«¬­®ÆŽ!"#$%&'()_=~ QWERTYUIOP`{ ‚ASDFGHJKL+*}€|ZXCVBNM<>?*ƒ „‘’“”•–—˜™šø789‰456Œ123‡¹ ŗ  »¼/›œžŸ½¾æöč“ö¾ĢŚŒ&/8Ac•t¾ trtŪtF  ø µ < Ė å ž÷’’’’’’’’’’’ ***##  Š` Š Š ---?!%!2!disk task got message from FS gave floppy disk driver bad addrTrying to DMA across 64K boundaryDiskette in drive %d is write protected. ---winchester task got message from %d FS gave winchester disk driver bad addrTrying to DMA across 64K boundaryHard disk won't reset Can't read partition table of winchester µ<goFoNoVo^ofo proc -pid- -pc- -sp- flag user -sys- base limit recv command %4d %4x %4x %4x %6D %7D %3dK %3dK /bin/sh%s PROC -----TEXT----- -----DATA----- ----STACK----- BASE SIZE %4x %4x %4x %4x %4x %4x %4x %4x %4x %3dK %3dK PRINTRTTY WINCHEFLOPPYRAMDSKCLOCK SYS HARDWRMM FS INIT ANY %s%4d Ī`Dā_O`X$`cE`de_o„_s``x³_Error: Division by 0 Illegal EM instruct'n Err in EM case instr Variable out of range Err in EM set instr Floating pt not impl. Heap overflow | 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 * |*=================================mopqrstuvwxy==========================================* | 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 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 * *=========================ƒ==================================================*/ 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 É_main † _main: ƒ ‚ ×ņ,#20 Ó_lock ĄŲ,#96 Ąå_ļzes+2 Äå_ļzes ĄŠ,ģ ĄäŠ ÄäŲ Ą-10(ń),ė ĄĘ,#_proc I0015: ĶĘ,#_proc+860 ja I0012 ĄĻö I0019: ĶĻ,#11 jge I0016  ž £ – Ąć6 sal äcl  ž – ĄåĻ sal ā1 ÄåĘ ° ÜĻ ĢI0019 ½ — ×į_proc Ąā86 cwd išv ģ ×į8 ¬ ĶĘ,#_proc+688 jae I001B Ąć8 £ sal äcl Äį2304 » Äā_t_stack ¦ ĢI001C I001B: 6 € I001C: §  Ą22Ē,ė § ĄčĘ Ąä22Ē Ą50(ļ),ė § Ķ50Ē,Ń je I001E § Äā50 Ą-1Ō,ģ Ąå-1Ō ÄĒ,#-248 I001E: £ sal į1 Äį16 » ĄčĘ Ąä_taskĒ Ą24(ļ),ė § Ķ24Ēö ŁI00110 ĶĻ,#0 jl I00111 I00110: ” Ó_ready Š I00111: § Ą28Ē,#512 § Ą30Ēö ĶĻö jge I00115 § Ą36Ē,#39328 § ĄäŲ Ą34Ē,ė § Ą42Ē,#39328 Ąå_ļzes ÄåŲ ĄčĘ Ą40(ļ),ģ § Ą48Ē,#39328 Ąå_ļzes ÄåŲ Äå_ļzes+2 ĄčĘ Ą46(ļ),ģ Ąå_ļzes+2 Äå_ļzes ĄčĘ Ą44(ļ),ģ š6 I00115: Ąį86 mul Ļ Äį602 » Ąä_proc+46Ē Ą-12(ń),ė £ sal į1 sal į1 Äį4 » ĄčĘ Ąä_ļzesĒ Ą36(ļ),ė ĶĻö ŁI00118 Į-10(ń) š9 I00118: Į-12(ń) I00119: §  Ą34Ē,ė £ sal į1 sal į1 Äį6 » ĄčĘ Ąä_ļzesĒ Ą42(ļ),ė £ sal į1 sal į1 Äį4 » ĄčĘ Ąä_ļzesĒ Ää34(ļ) Ą40(ļ),ė £ sal į1 sal į1 Äį6 » ĄčĘ Ąä_ļzesĒ Ą44(ļ),ė £ sal į1 sal į1 Äį6 » ĄčĘ Ąä_ļzesĒ Ää40(ļ) Ą46(ļ),ė I00116: § Į34Ē  ž  – § Ą18Ē,ė § Į40Ē  ž  – § Ą16Ē,ė § Į40Ē  ž  – § Ą20Ē,ė § Į40Ē  ž  – § Ą14Ē,ė ÄĘ,#86 ĢI0015   Ą_proc+624,#_k_stack Ä_proc+624,#256 Ą_proc+652,#_k_stack Ä_proc+652,#8 ĄĘ,#_proc+946 I0011D: ĶĘ,#_proc+2064 jae I0011A § Ą30Ē,#1 ÄĘ,#86 šD I0011A: Ó_get_chrome Ą_color,ė 4 € Ąį65535 € Ó_get_byte Š Š ¬  ž £ – Ķį252 ŁI0011F Ą_pc_at,#1 I0011F: “84 € Ąax,#_vec_table €  € Ąį_proc+602 € Ó_umap Äņ,#8 Ą-1Ī,ė Ą-1Å,ī ‡ € “84 € Į-1Å Į-1Ī ‡ € € Ó_phys_copy Äņ,#12 ĄĻö I00124: ĶĻ,Ń jge I00121 ĮŲ Ąį_surprise € ¹ Ó_set_vec ¤ ÜĻ ĢI00124 I00121: ĄĻ,Ń I00128: ĶĻ,#256 jge I00125 ĮŲ Ąį_trp € ¹ Ó_set_vec ¤ ÜĻ ĢI00128 I00125: ĮŲ Ąį_švide € ‡ € Ó_set_vec ¤ ĮŲ Ąį_s_call € Ąį32 € Ó_set_vec ¤ ĮŲ Ąį_clock_int € Ąį8 € Ó_set_vec ¤ ĮŲ Ąį_tty_int € Ąį9 € Ó_set_vec ¤ ĮŲ Ąį_šsk_int € 4 € Ó_set_vec ¤ ĮŲ Ąį_lpr_int € 5 € Ó_set_vec ¤ Ķ_pc_at,#0 ±A ĮŲ Ąį_wini_int € 18 € Ó_set_vec ¤ ĢI0012B I0012A: ĮŲ Ąį_wini_int € 3 € Ó_set_vec ¤ I0012B: ‡ € Ąį_proc € Ąį383 € Ó_set_vec ¤ Ą_bill_ptr,#_proc+602 Ó_pick_proc ‡ € Ąį33 € Ó_port_out Š Š ‡ € 61 € Ó_port_out Š Š Ó_restart … „  É_unexpected_int _unexpected_int: ƒ ‚ Ąį_1 € Ó_printk Š Ąå_proc_ptr Ąć4 Ąä42Ē sal äcl € Į24Ē Ąį_2 € Ó_printk ¤ … „  É_trap _trap: ƒ ‚ Ąį_3 € Ó_printk Š Ąį_4 € Ó_printk Š Ąį_5 € Ó_printk Š Ąå_proc_ptr Ąć4 Ąä42Ē sal äcl € Į24Ē Ąį_6 € Ó_printk ¤ … „  .globl _šv_trap _šv_trap: ƒ ‚ Ąį_7 € Ó_printk Š Ąå_proc_ptr Ąć4 Ąä42Ē sal äcl € Į24Ē Ąį_8 € Ó_printk ¤ … „  É_panic _panic: ƒ ‚ ’ cmpb Ēö je I0053 ˆ Ąį_9 € Ó_printk Š Š  ž ĄäĪ – Ķį32768 je I0056 “ Ąį_10 € Ó_printk Š Š I0056: Ąį_11 € Ó_printk Š I0053: Ąį_12 € Ó_printk Š Ó_wreboot … „  _set_vec: ƒ ‚ ×ņ,#12 ĄäĪ ¬ ĄäŌ ” ŪåĻ ĄŠ,ģ ‡ Ėåģ Ąę_ļzes ÄęŌ adc äģ € Įķ Ąį4 € Ćķ  ‰ © 2: sal į1 rcl ā1 Ø 1: Ą-10(ń),ė ĄŲ,ģ Ąć4 Ąā4 Ąä-10(ń) ĮŲ – Ėåģ Ćķ ÄäŠ adc ęģ Įķ Ąę#4 Ąā4 – Ą-10(ń),ė ĆŲ ¢ cwd Ąč#4 Ėéš » Ąäī Ėęķ Įķ Ąć4 Įķ Ó.mli4 œ € ĮŲ Į-10(ń) Ó_phys_copy Äņ,#12 … „  ® _1: Ā28245 Ā30821 Ā25968 Ā29795 æ01 Ā29728 Ā24946 Ā14960 Ā30240 Ā25445 Ā28532 Ā8306 Ā8252 Ā13873 Ā10 _2: Ā25456 Ā15648 Ā12320 Ā9592 Ā8312 ² Ā29728 Ā30821 Ā11124 Ā24932 Ā24948 Ā25131 Ā29555 Ā15648 Ā12320 Ā9592 Ā2680 ø _3: Ā21770 Ā25966 Ā28792 Ā25445 Ā25972 Ā8292 Ā29300 Ā28769 Ā8250 Ā25974 Ā29795 Ā29295 Ā15904 Ā8253 Ā13873 Ā32 _4: Ā26708 Ā29545 Ā27936 Ā31073 Ā25120 Ā8293 Ā30052 Ā8293 Ā28532 Ā24864 Ā25443 æ05 Ā28261 Ā24948 Ā27756 Ā8313 .word 28265 Ā27747 æ17 Ā28265 Ā2663 ø _5: Ā8289 Ā28526 Ā11630 Ā18765 Ā18766 Ā8280 Ā26988 Ā29282 Ā29281 Ā8313 Ā28530 Ā29813 Ā28265 Ā8293 Ā26740 Ā29793 Ā26912 Ā8307 Ā29300 Ā27001 Ā26478 Ā29728 Ā8303 Ā24941 Ā25963 Ā24864 Ā29472 Ā29561 Ā25972 Ā8301 Ā24931 Ā27756 Ā2606 ø _6: Ā25456 Ā15648 Ā12320 Ā9592 Ā8312 ² Ā29728 Ā30821 Ā11124 Ā24932 Ā24948 Ā25131 Ā29555 Ā15648 Ā12320 Ā9592 Ā2680 ø _7: Ā29268 Ā28769 Ā29728 Ā8303 Ā25974 Ā29795 Ā29295 Ā12320 Ā8250 Ā26980 Ā26998 Ā25956 Ā28448 Ā25974 Ā26226 Ā28524 Ā11895 ² ø _8: Ā25456 Ā15648 Ā12320 Ā9592 Ā8312 ² Ā29728 Ā30821 Ā11124 Ā24932 .word 24948 Ā25131 Ā29555 Ā15648 Ā12320 Ā9592 Ā2680 ø _9: Ā19210 Ā29285 Ā25966 Ā8300 Ā24944 Ā26990 Ā14947 Ā9504 Ā115 _10: Ā9504 Ā100 _11: Ā10 _12: Ā21514 Ā28793 Ā8293 Ā28787 Ā25441 Ā8293 Ā28532 Ā29216 Ā25189 Ā28527 Ā2676 ø † # 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 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); } É_mem_task † _mem_task: ƒ ‚ ×ņ,#6 ® _2: ĀI001D Ā3 Ā2 ĀI001A ĀI001B ĀI001C † Ó_get_base Ėåģ Ąć4 © 2: sal į1 rcl ā1 Ø 1: Ą_ram_origin+8,ė Ą_ram_origin+8+2,ģ Ąå_ļzes+2 Äå_ļzes Ąć4 sal åcl ‡ Ą_ram_limit+8,ģ Ą_ram_limit+8+2,ė Ą_ram_limit+4ö Ą_ram_limit+4+2,#10 • Ąį_mess € 16 € Ó_receive Š Š Ķ_messö jge I0016 Į_mess Ąį_1 € Ó_panic Š Š ½ Ąå_mess ĄĻ,ģ Ąå_mess+6 ĄŠ,ģ Į_mess+2 ĢI0018 I001A: Ąį_mess € Ó_do_mem Š ” ĢI0019 I001B: Ąį_mess € Ó_do_mem Š ” ĢI0019 I001C: Ąį_mess € Ó_do_setup Š ” ĢI0019 I001D: ĄĘ,#-22 ĢI0019 I0018: Ąč#_2 ‰ Ģ.csa2 I0019: Ą_mess+2,#68 ĄåŠ Ą_mess+4,ģ § Ą_mess+6,ģ mov į_mess € ¹ Ó_send Š Š ­ _do_mem: ƒ ‚ ×ņ,#14 ’ Ąä4Ē ” ĶĘö jl I0022 ĶĘ,#4 jl I0023 I0022: Ąį-6 € ĢI0021 I0023: ĶĘ,#3 ŁI0027 ’ Ķ2Ē,#3 ŁI002A Ąį-104 € ĢI0021 I002A: ’ Į8Ē ĢI0021 I0027: ’ Ąä10Ē Ąę12Ē ×į0 sbb ć0 Ł1f Öäė je 1f Üķ 1: or ęķ jge I002D Ąį-6 € ĢI0021 I002D:  — sal äcl » ĄčÅ Ąä_ram_originĒ Ąę_ram_origin+2Ē Ää10(ļ) adc ę12(ļ) ĄŲ,ė ĄŠ,ķ  — sal äcl » ĄäŲ ĄęŠ ×ä_ram_limitĒ sbb ę_ram_limit+2Ē Ł1f Öäė je 1f Üķ 1: or ęķ jl I00210 Ąį-104 € ĢI0021 I00210: ’ Ąä8Ē ¬ £ cwd ÄäŲ adc ēŠ  § sal åcl ×ė,_ram_limitĒ sbb ē_ram_limit+2Ē Ł1f Öäė je 1f Üī 1: or ēī jle I00213  — sal äcl » Ąä_ram_limitĒ Ąę_ram_limit+2Ē ×äŲ sbb ęŠ ¬ I00213: ’ Ąį86 mul 6Ē Äį688 » Äā_proc Ą-1Å,ģ  ž £ – ’ € Į18Ē  € Į-1Å Ó_umap Äņ,#8 Ą-12(ń),ė Ą-10(ń),ī Ąä-12(ń) Ąå-10(ń) ×į0 sbb ā0 Ł1f or åė 1: or åģ ŁI00216 Ąį-10 € ĢI0021 I00216: ’ Ķ2Ē,#3 ŁI00219 £ cwd œ € Į-10(ń) Į-12(ń) ĮŠ ĮŲ Ó_phys_copy Äņ,#12 ĢI0021A I00219: £ cwd œ € ĮŠ ĮŲ Į-10(ń) Į-12(ń) Ó_phys_copy Äņ,#12 I0021A: ¹ I0021:  … „  _do_setup: ƒ ‚ € ’ Ąä4Ē ” Ķ-2(ń)ö jl I0032 ĶĘ,#4 jl I0033 I0032: Ąį-6 € ĢI0031 I0033: ’  — sal äcl Ąčė Ąä10Ē Ąå12Ē Ą_ram_origin(ļ),ė Ą_ram_origin+2(ļ),ģ ’ Ąä8Ē cwd Ąč#1024 Ėéš » Ąäī Ó.mli4 ’ Ää10Ē adc ē12Ē  § sal åcl Ą_ram_limitĒ,ė Ą_ram_limit+2Ē,ī ‡ € I0031:  … „  ³ _ram_limit: .zerow 16/2 _ram_origin: .zerow 16/2 _mess: .zerow 24/2 ® _1: Ā25965 Ā8301 Ā24948 Ā27507 Ā26400 Ā29807 Ā27936 Ā29541 Ā24947 Ā25959 Ā26144 Ā28530 Ā8301 ø † | 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: /* 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; ¦Ø©port_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); } É_printer_task † _printer_task: ƒ ‚ ×ņ,#24 ® _1: ĀI0013 ø Ā4 ĀI0018 ĀI0013 ĀI0019 ĀI0013 ĀI0017 † Ó_print_init • Ūä-2Å € 16 € Ó_receive Š Š Į-22(ń) ĢI0015 I0017: Ūä-2Å € Ó_do_write Š ­ I0018: Ūä-2Å € Ó_do_cancel Š ­ I0019: Ūä-2Å € Ó_do_done Š ­ I0015: Ąč#_1 ‰ Ģ.csa2 _do_write: ƒ ‚ ×ņ,#14 ĄŠö Ķ_pr_busyö je I0023 ĄŠ,#-11 I0023: ’ Ķ8Ēö jg I0026 ĄŠ,#-22 I0026: ’ Ąį86 mul 6Ē Äį688 » Äā_proc Ą-10(ń),ģ ’ ĄčÅ Į8Ē Į18(ļ)  € Į-10(ń) Ó_umap Äņ,#8 Ą-1Å,ė Ą-12(ń),ī Ąä-1Å Ąå-12(ń) ×į0 sbb ā0 Ł1f or åė 1: or åģ ŁI0029 ĄŠ,#-10 I0029: ĶŠö jne I002C Ó_lock ’ ĄåĒ Ą_caller,ģ ’ Ąå6Ē Ą_proc_nr,ģ ’ Ąå8Ē Ą_pcount,ģ ’ Ąå8Ē Ą_orig_count,ģ Ąć4 Ąä-1Å Ąå-12(ń) © 2: sar ā1 rcr į1 Ø 1: Ą_es,ė Ąć4 Ąā4 Ąä-1Å Į-12(ń) – ‰ Öį15 Öā0 Ą_offset,ė ¼ I00211: ĶĘ,#1000 jge I002C Ąå_port_base Üģ ŪäŲ € ¦ Ó_port_in Š Š  ž ĄäŲ – Öį176 Ķį144 ŁI00213 Ą_pr_busy,#1 Ó_pr_char ĄŠ,#-998 ĢI002C I00213:  ž ĄäŲ – Öį176 Ķį16 ŁI00216 ĄĻö I0021B: ĶĻ,#1000 jge I002F ÜĻ ĢI0021B I00216: ĮŲ Ó_pr_error Š ĄŠ,#-5 ĢI002C I002F: ÜĘ ĢI00211 I002C:  ž ĄäŲ – Öį176 Ķį16 ŁI0021D ĄŠ,#-11 I0021D: ’ ĄčÅ ĮŠ Į6Ē Į(ļ) Ąį68 € Ó_reply Äņ,#8 … „  _do_done: ƒ ‚ € ’ Ķ6Ēö ŁI0033 Į_orig_count ĢI0034 I0033: Ąį-5 € I0034: ĆĘ Ķ_proc_nr,#-999 je I0036 ” Į_proc_nr Į_caller Ąį67 € Ó_reply Äņ,#8 ĶĘ,#-5 ŁI0036 ’ Į6Ē Ó_pr_error Š I0036: Ą_pr_busyö … „  _do_cancel: ƒ ‚ Ķ_pr_busyö ŁI0043 … „  I0043: Ą_pr_busyö Ą_pcountö Ą_proc_nr,#-999 ’ ĄčÅ Ąį-4 € Į6Ē Į(ļ) Ąį68 € Ó_reply Äņ,#8 … „  _reply: ƒ ‚ ×ņ,#24 ¢ Ą-22(ń),ė Ąä10(ń) Ą-1Ō,ė ĄäŌ Ą-20(ń),ė Ūä-2Å € “ Ó_send Š Š … „  _pr_error: ƒ ‚  ž ¢ – testb al,#32 je I0063 Ąį_2 € Ó_printk Š I0063:  ž ¢ – testb al,Ń ŁI0066 Ąį_3 € Ó_printk Š I0066:  ž ¢ – testb al,#8 ŁI0069 Ąį_4 € Ó_printk Š I0069: … „  _print_init: ƒ ‚ € Ķ_colorö je I0073 Ąį888 € ĢI0074 I0073: Ąį956 € I0074:  ž  – Ą_port_base,ė Ą_pr_busyö “ Ää_port_base Ąā8 ¦ € Ó_port_out Š Š ¼ I0078: ĶĘ,#100 jge I0075 ÜĘ ĢI0078 I0075: “ Ää_port_base Ąā12 ¦ € Ó_port_out Š Š … „  É_pr_char _pr_char: ƒ ‚ ×ņ,#8 Ąå_orig_count Ķ_pcount,ģ je I0083 Ąį32 € € Ó_port_out Š Š I0083: Ķ_pr_busyö ŁI0089 … „  I0089: Ķ_pcountö jle I0088 Ąå_port_base Üģ ŪäĘ € ¦ Ó_port_in Š Š  ž — – Öį176 Ķį144 ŁI008C Į_offset Į_es Ó_get_byte Š Š Ź-7(ń),al  ž Źal,-7(ń) Ž – ¬ ¹ Į_port_base Ó_port_out Š Š “ Ää_port_base ž9 ¦ € Ó_port_out Š Š “ Ää_port_base ž8 ¦ € Ó_port_out Š Š Ü_offset ó_pcount Ü_cum_count ĄŠö I00811: ĶŠ,#100 jge I0089 ÜŠ ĢI00811 I008C:  ž — – Öį176 Ķį16 ŁI0088 … „  I0088: Ą_int_mess+2,#2 Ķ_pcountö ŁI00816 ‡ € ĢI00817 I00816: ” I00817: Ć_int_mess+6 Ąį_int_mess € Ąį-8 € Ó_interrupt Š Š … „  É_prev_ct ³ _prev_ct: .zerow 2/2 É_cum_count _cum_count: .zerow 2/2 É_pr_busy _pr_busy: .zerow 2/2 É_pcount _pcount: .zerow 2/2 _offset: .zerow 2/2 _es: .zerow 2/2 _orig_count: .zerow 2/2 _proc_nr: .zerow 2/2 _caller: .zerow 2/2 _port_base: .zerow 2/2 ® _2: Ā29264 Ā28265 Ā25972 Ā8306 Ā29545 Ā28448 Ā29813 Ā28448 Ā8294 Ā24944 Ā25968 Ā2674 ø _3: Ā29264 Ā28265 Ā25972 Ā8306 Ā29545 Ā28192 Ā29807 Ā28448 Ā8302 Ā26988 Ā25966 Ā10 _4: Ā29264 Ā28265 Ā25972 Ā8306 Ā29285 Ā28530 Ā2674 ø † /* 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“¶·ø¹ŗnk = 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 */ } /* 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 */ É_interrupt † _interrupt: ƒ ‚ ×ņ,#10 Ąį32 € € Ó_port_out Š Š Ķ_pc_atö je I0013 ĶÅ,#-6 ŁI0013 Ąį32 € 60 € Ó_port_out Š Š • ¢ ōė Ąęė  sal äcl ĄŲ,ė “ ˆ Ąį-1 € Ó_mini_send ¤ ™ je I0017  ž Ąä_busy_map – ĄŠ,ė ĶÅ,#-3 ŁI001A Ü_lost_ticks ĢI0018 I001A:  ž ĄäŲ Į_busy_map – ‰ or äģ Ą_busy_map,ė ¢ ōė sal į1 » ĄäĪ Ą_task_messĒ,ė ĢI0018 I0017: ĄäŲ not ė  ž Į_busy_map – ‰ Öåė Ą_busy_map,ģ  ž Ąä_busy_map – ĄŠ,ė I0018: ĶŠö je I001D ĄĘ,#2 I00112: ĶĘ,#8 jg I001D ĄęĘ ĄäŠ sar äcl testb al,#1 je I00110 — sal į1 Ąåė — ōė Į_task_messĒ € Ąį-1 € Ó_mini_send ¤ ¬ ĶĻö ŁI00110 ĄęĘ  sal äcl not ė  ž Į_busy_map – ‰ Öåė Ą_busy_map,ģ I00110: ÜĘ š2 I001D: Ķ_rdy_headö je I0011A Ķ_cur_procö jge I00119 Ķ_cur_proc,#-999 ŁI0011A I00119: Ó_pick_proc I0011A: … „  É_sys_call _sys_call: ƒ ‚ € € Ąį86 mul Ī Äį688 » Äā_proc ĄĘ,ģ ĶŌ,#-8 jl I0022 ĶŌ,Ń jl I0023 ĶŌ,#116 je I0023 I0022: § ĄĒ,#-2 … „  I0023: ĶÅ,#3 je I0028 ĶĪ,#2 jl I0028 § ĄĒ,#-8 … „  I0028: test Å,#1 je I002C Į10(ń) ĮŌ “ Ó_mini_send ¤ ¬ ĶÅ,#1 je I002E ĶĻö je I002F I002E: £ § ° I002F: ĶĻö je I002C Ąsp,ń „  I002C: test Å,#2 je I00216 Į10(ń) ĮŌ “ Ó_mini_rec ¤ ¬ £ § ° I00216: … „  É_mini_send _mini_send: ƒ ‚ ×ņ,Ń ĶÅ,#2 jl I0033 ĶĪ,#1 je I0033 ĶĪö je I0033 Ąį-1 € ĢI0031 I0033: Ąį86 mul Å Äį688 » Äā_proc ĄĘ,ģ Ąį86 mul Ī Äį688 » Äā_proc ĄĻ,ģ ĄåĻ Į30Ē  ž  – testb al,#1 je I0038 Ąį-1 € ĢI0031 I0038: § Ąä42Ē Ą-1Å,ė ĄäŌ ĄŲ,ė Ąć4 ĄäŲ shr äcl Ą-10(ń),ė ĄäŲ Äį24 ×į1 shr äcl Ą-12(ń),ė Ąä-10(ń) Ķ-12(ń),ė jb I003A § Ąä-12(ń) ×ä38Ē Ķ-1Å,ė ja I003B I003A: Ąį-10 € ĢI0031 I003B: ĄåĻ Į30Ē  ž  – testb al,#8 je I003F ĄåĻ Ķ80Ē,#116 je I003E ĄåĻ ¢ Ķ80Ē,ė ŁI003F I003E: ĄåĻ ĄčĻ ĄéĘ Į78Ē Į40(ļ) ĮŌ Į40(š) ˆ Ó_cp_mess Äņ,#10 ĄåĻ Äā30 Ą-1Ī,ģ ĮĒ  ž  – Öį65527  ž – Ąå-1Ī ° ĄåĻ Ķ30Ēö ŁI00310 ¹ Ó_ready Š ĢI00310 I003F: ĶÅ,#-1 ŁI00317 Ąį-4 € ĢI0031 I00317: § ĄäŌ Ą78Ē,ė § Äā30 Ą-1Ī,ģ ĮĒ  ž  – or į4  ž – Ąå-1Ī ° ” Ó_unready Š ĄåĻ Ąä74Ē ĄŠ,ė ĶŠö ŁI0031D ĄåĻ — Ą74Ē,ė ĢI0031B I0031D: ĄåŠ Ķ76Ēö je I0031C ĄåŠ Ąä76Ē ĄŠ,ė ĢI0031D I0031C: ĄåŠ — Ą76(bx),ė I0031B: § Ą76Ēö I00310: ‡ € I0031:  … „  _mini_rec: ƒ ‚ ×ņ,#10 Ąį86 mul Å Äį688 » Äā_proc ĄĘ,ģ § Ąä74Ē ¬ I0043: ĶĻö je I0042 £ ×į_proc Ąā86 cwd išv ģ ×į8 ĄŲ,ė ĶĪ,#116 je I0045 ĄäŲ ĶĪ,ė ŁI0046 I0045: § ĄčĻ ĄéĻ ĮŌ Į40Ē Į78(ļ) Į40(š) ĮŲ Ó_cp_mess Äņ,#10 ĄåĻ Äā30 Ą-10(ń),ģ ĮĒ  ž  – Öį65531  ž – Ąå-10(ń) ° ĄåĻ Ķ30Ēö ŁI004A ¹ Ó_ready Š I004A: § Ąä74Ē ĶĻ,ė ŁI004D ĄåĻ ĄčĘ Ąä76Ē Ą74(ļ),ė ĢI004E I004D: ĄåĻ ĄčŠ Ąä76Ē Ą76(ļ),ė I004E: ‡ € ĢI0041 I0046: Ąä-4(ń) ĄŠ,ė ĄåĻ Ąä76Ē ¬ ĢI0043 I0042: § ĄäĪ Ą80Ē,ė § ĄäŌ Ą78Ē,ė § Äā30 Ą-10(ń),ģ ĮĒ  ž  – or į8  ž – Ąå-10(ń) ° ” Ó_unready Š Ķ_ļg_procsö jle I00410 ĶÅö ŁI00410 ĶĪ,#116 ŁI00410 ‡ € Ó_inform Š I00410: ‡ € I0041:  … „  É_pick_proc _pick_proc: ƒ ‚ € Ķ_rdy_headö je I0053 ¼ ĢI0054 I0053: Ķ_rdy_head+2ö je I0056 ĄĘ,#1 ĢI0054 I0056: ĄĘ,#2 I0054: Ąå_cur_proc Ą_prev_proc,ģ — sal į1 » Ķ_rdy_headĒö je I0059 — sal į1 » Ąä_rdy_headĒ ×į_proc Ąā86 cwd išv ģ ×į8 Ą_cur_proc,ė — sal į1 » Ąå_rdy_headĒ Ą_proc_ptr,ģ Ķ_cur_proc,#2 jl I005A Ąå_proc_ptr Ą_bill_ptr,ģ ĢI005A I0059: Ą_cur_proc,#-999 Ą_proc_ptr,#_proc+602 Ąå_proc_ptr Ą_bill_ptr,ģ I005A: … „  É_ready _ready: ƒ ‚ € € Ó_lock ¢ ×į_proc Ąā86 cwd išv ģ ×į8 ¬ ĶĻö jge I0063 ‡ € ĢI0064 I0063: ĶĻ,#2 jge I0066  € ĢI0064 I0066: “ € I0064: ĆĘ — sal į1 » Ķ_rdy_headĒö ŁI0069 — sal į1 » ¢ Ą_rdy_headĒ,ė ĢI006A I0069: — sal į1 » Ąå_rdy_tailĒ ¢ Ą82Ē,ė I006A: — sal į1 » ¢ Ą_rdy_tailĒ,ė ’ Ą82Ēö Ó_restore … „  É_unready _unready: ƒ ‚ ×ņ,#6 Ó_lock ¢ ×į_proc Ąā86 cwd išv ģ ×į8 ¬ ĶĻö jge I0073 ‡ € jmp I0074 I0073: ĶĻ,#2 jge I0076  € ĢI0074 I0076: “ € I0074: ĆŠ ĄäŠ sal į1 » Ąä_rdy_headĒ ” ĶĘö ŁI0079 … „  I0079: ¢ ĶĘ,ė ŁI007F § ĄäŠ sal į1 Ąčė Ąä82Ē Ą_rdy_head(ļ),ė Ó_pick_proc ĢI007D I007F: § ¢ Ķ82Ē,ė je I007E § Ąä82Ē ” ĶĘö ŁI007F … „  I007E: § Ąå82Ē ĄčĘ Ąä82Ē Ą82(ļ),ė I00715: § Ķ82Ēö je I00714 § Ąä82Ē ” ĢI00715 I00714: ĄäŠ sal į1 » — Ą_rdy_tailĒ,ė I007D: Ó_restore … „  É_sched _sched: ƒ ‚ Ó_lock Ķ_rdy_head+4ö ŁI0083 Ó_restore … „  I0083: Ąå_rdy_tail+4 Ąę_rdy_head+4 Ą82Ē,ķ Ąå_rdy_head+4 Ą_rdy_tail+4,ģ Ąå_rdy_head+4 Ąå82Ē Ą_rdy_head+4,ģ Ąå_rdy_tail+4 Ą82Ēö Ó_pick_proc Ó_restore … „  /* 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; /* sÉĖĢĶĪĻŠŃŅÓŌÕet 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); } É_sys_task † _sys_task: ƒ ‚ € ® _1: ĀI00110 Ā1 Ā8 ĀI001A ĀI001B ĀI001E ĀI0017 ĀI0018 ĀI001F ĀI0019 ĀI001C ĀI001D † • Ąį_m € 16 € Ó_receive Š Š Į_m+2 ĢI0015 I0017: Ąį_m € Ó_do_fork Š ” ĢI0016 I0018: Ąį_m € Ó_do_newmap Š ” ĢI0016 I0019: Ąį_m € Ó_do_exec Š ” ĢI0016 I001A: Ąį_m € Ó_do_xit Š ” ĢI0016 I001B: Ąį_m € Ó_do_getņ Š ” ĢI0016 I001C: Ąį_m € Ó_do_times Š ” ĢI0016 I001D: Ąį_m € Ó_do_abort Š ” ĢI0016 I001E: Ąį_m € Ó_do_ļg Š ” ĢI0016 I001F: Ąį_m € Ó_do_copy Š ” ĢI0016 I00110: ĄĘ,#-9 ĢI0016 I0015: Ąč#_1 ‰ Ģ.csa2 I0016: § Ą_m+2,ģ Ąį_m € Į_m Ó_send Š Š ­ _do_fork: ƒ ‚ ×ņ,Ń ’ Ąä4Ē ĄŲ,ė Ąä6Ē Ą-10(ń),ė Ąä8Ē Ą-12(ń),ė ĶŲö jl I0022 ĶŲ,Ń jge I0022 Ķ-10(ń)ö jl I0022 Ķ-10(ń),Ń jl I0023 I0022: Ąį-11 € ĢI0021 I0023: Ąį86 mul -10(ń) Äį688 » Äā_proc ĄĘ,ģ Ąį86 mul Ų Äį688 » Äā_proc ĄĻ,ģ Ąį86 mul -10(ń) Äį688 » Äā_proc ĄŠ,ģ Ą-1Å,#86 I0029: Į-1Å ó-1Å  ™ je I0028 ¹ ÄĻ,#1 ‰ ĄčŠ Œ Ź(ļ),al ÄŠ,#1 ĢI0029 I0028: § Äā30 Ą-1Ī,ģ ĮĒ  ž  – or į2  ž – Ąå-1Ī ° § Ąä-12(ń) Ą52Ē,ė § ĄĒö § Ą54Ēö Ą56(ģ)ö § Ą58Ēö Ą60Ēö § Ą62Ēö Ą64Ēö § Ą66Ēö Ą68Ēö ‡ € I0021:  … „  _do_newmap: ƒ ‚ ×ņ,#32 ’ « Ą-2Å,ė ’ Ąä4Ē Ą-2Ī,ė Ąä10Ē Ą-30(ń),ė Ķ-2Ī,#-8 jl I0032 Ķ-2Ī,Ń jl I0033 I0032: Ąį-11 € ĢI0031 I0033: Ąį86 mul -2Ī Äį688 » Äā_proc ĄĘ,ģ Ąį86 mul -2Å Äį688 » Äā_proc ĄĻ,ģ Ą-22(ń),#18 ‡ Ąå-22(ń) Ą-1Ī,ģ Ą-1Å,ė Ąä-30(ń) Ą-1Ō,ė § Äā32 Ą-20(ń),ģ Į-22(ń) Į-1Ō  € ¹ Ó_umap Äņ,#8 ĄŲ,ė ĄŠ,ī ĄäŲ ĄåŠ ×į0 sbb ā0 Ł1f or åė 1: or åģ ŁI0037 Ąį32768 € Ąį_2 € Ó_panic Š Š I0037: Į-22(ń) Į-20(ń)  push ė Ąį_proc+516 € Ó_umap Äņ,#8 Ą-12(ń),ė Ą-10(ń),ī Ąä-12(ń) Ąå-10(ń) ×į0 sbb ā0 Ł1f or åė 1: or åģ ŁI003A Ąį32768 € Ąį_3 € Ó_panic Š Š I003A: Į-1Å Į-1Ī Į-10(ń) Į-12(ń) ĮŠ ĮŲ Ó_phys_copy Äņ,#12 § Į34Ē  ž  – § Ą18Ē,ė § Į40Ē  ž  – § Ą16Ē,ė § Į40Ē  ž  – § Ą20Ē,ė § Į40Ē  ž  – § Ą14Ē,ė § Ąä30Ē Ą-2Ō,ė Äā30 Ą-32(ń),ģ ĮĒ  ž  – Öį65533  ž – Ąå-32(ń) ° Ķ-2Ōö je I003D § Ķ30Ēö ŁI003D ” Ó_ready Š I003D: ‡ € I0031:  … „  _do_exec: ƒ ‚ ×ņ,#8 ’ Ąä4Ē ¬ Ąä10Ē ĄŠ,ė ĶĻö jl I0042 ĶĻ,Ń jl I0043 I0042: Ąį-11 € ĢI0041 I0043: Ąį86 mul Ļ Äį688 » Äā_proc ĄĘ,ģ § ĄäŠ Ą22Ē,ė § Ą24Ēö § Ą70Ēö Ą72Ēö § Äā30 ĄŲ,ģ ĮĒ  ž  – Öį65527  ž – ĄåŲ ° § Ķ30Ēö ŁI0047 ” Ó_ready Š I0047: ĮŠ ¹ Ó_set_name Š Š ‡ € I0041:  … „  _do_xit: ƒ ‚ ×ņ,#14 ’ Ąä4Ē Ą-10(ń),ė Ąä6Ē Ą-12(ń),ė Ķ-10(ń)ö jl I0052 Ķ-10(ń),Ń jge I0052 Ķ-12(ń)ö jl I0052 Ķ-12(ń),Ń jl I0053 I0052: Ąį-11 € ĢI0051 I0053: Ąį86 mul -10(ń) Äį688 » Äā_proc ĄĘ,ģ Ąį86 mul -12(ń) Äį688 » Äā_proc ĄĻ,ģ § Äā62 Ą-1Å,ģ Ąå-1Å ĄčĻ ĄéĻ Ąä62(ļ) Ąę64(ļ) Ää54(š) adc ę56(š) ÄäĒ adc ę2Ē ° Ą2Ē,ķ § Äā66 Ą-1Å,ģ Ąå-1Å ĄčĻ ĄéĻ Ąä66(ļ) Ąę68(ļ) Ää58(š) adc ę60(š) ÄäĒ adc ę2Ē ° Ą2Ē,ķ ¹ Ó_unready Š ĄåĻ Ą70Ēö Ą72Ēö ‡ € Į-12(ń) Ó_set_name Š Š ĄåĻ Į30Ē  ž  – testb al,#4 je I0059 ĄĘ,#_proc I005E: ĶĘ,#_proc+2064 jae I0059 § Ķ74Ēö ŁI00510 ĢI005C I00510: § £ Ķ74Ē,ė ŁI00513 ĄåĻ ĄčĘ Ąä76Ē Ą74(ļ),ė ĢI0059 I00513: § Ąä74Ē ĄŠ,ė I00516: Ąå-6(ń) Ąä76Ē ĄŲ,ė ĶŲö je I005C £ ĶŲ,ė ŁI00519 ĄåŲ ĄčŠ Ąä76Ē Ą76(ļ),ė ĢI005C I00519: ĄäŲ ĄŠ,ė ĢI00516 I005C: ÄĘ,#86 ĢI005E I0059: ĄåĻ Ą30Ē,#1 ‡ € I0051:  … „  _do_getņ: ƒ ‚ € € ’ Ąä4Ē ¬ ĶĻö jl I0062 ĶĻ,Ń jl I0063 I0062: Ąį-11 € ĢI0061 I0063: Ąį86 mul Ļ Äį688 » Äā_proc ĄĘ,ģ § Ąå22Ē Ą_m+10,ģ ‡ € I0061:  … „  _do_times: ƒ ‚ € € ’ Ąä4Ē ¬ ĶĻö jl I0072 ĶĻ,Ń jl I0073 I0072: Ąį-11 € ĢI0071 I0073: Ąį86 mul Ļ Äį688 » Äā_proc ĄĘ,ģ § ĄčÅ Ąä54Ē Ąå56Ē Ą4(ļ),ė Ą6(ļ),ģ § ĄčÅ Ąä58Ē Ąå60Ē Ą8(ļ),ė Ą10(ļ),ģ § ĄčÅ Ąä62Ē Ąå64Ē Ą12(ļ),ė Ą14(ļ),ģ § ĄčÅ Ąä66Ē Ąå68Ē Ą16(ļ),ė Ą18(ļ),ģ ‡ € I0071:  … „  _do_abort: ƒ ‚ Ąį32768 € Ąį_4 € Ó_panic Š Š … „  _do_ļg: ƒ ‚ ×ņ,#24 ’ Ąä4Ē Ą-1Ō,ė Ąä6Ē Ą-20(ń),ė Ąä14Ē Ą-22(ń),ė Ķ-1Ō,#2 jl I0092 Ķ-1Ō,Ń jl I0093 I0092: Ąį-11 € ĢI0091 I0093: Ąį86 mul -1Ō Äį688 » Äā_proc ĄĘ,ģ Ą-12(ń),#_ļg_stuff § Ąä22Ē Ą-1Ī,ė Į-20(ń) ” Ąį_ļg_stuff € Ó_build_ļg ¤ Ą-1Å,#8 Ąä-1Ī ×ä-1Å Ą-1Ī,ė Į-1Å Į-12(ń)  € Ąį_proc+516 € Ó_umap Äņ,#8 ĄŠ,ė Ą-4(ń),ī Į-1Å Į-1Ī “ € ” Ó_umap Äņ,#8 Ą-10(ń),ė ĄŲ,ī Ąä-10(ń) ĄåŲ ×į0 sbb ā0 Ł1f or åė 1: or åģ ŁI0097 Ąį32768 € Ąį_5 € Ó_panic Š Š I0097: ‡ € Į-1Å ĮŲ Į-10(ń) ¹ ĮŠ Ó_phys_copy Äņ,#12 § Ąä-1Ī Ą22Ē,ė § Ąä-22(ń) Ą24Ē,ė ‡ € I0091:  … „  _do_copy: ƒ ‚ ×ņ,#24 ’ Ąä6Ē ” Ąä8Ē ¬ Źal,4Ē Ž ‹ ĄŠ,ė Źal,5Ē Ž ‹ ĄŲ,ė Ąä10Ē Ą-10(ń),ė Ąä14Ē Ą-12(ń),ė Ąä18Ē Į20Ē Ą-2Å,ė Ć-22(ń) ĶĘ,#-999 ŁI00A3 ’ Ąä10Ē Į12Ē Ą-1Ī,ė Ć-1Å ĢI00A4 I00A3: Ąį86 mul Ę Äį688 » Į-2Å Į-10(ń) ĮŠ Äbx,#_proc ¦ Ó_umap Äņ,#8 Ą-1Ī,ė Ą-1Å,ī I00A4: ĶĻ,#-999 ŁI00A6 ’ Ąä14Ē Į16Ē Ą-20(ń),ė Ć-1Ō ĢI00A7 I00A6: Ąį86 mul Ļ Äį688 » Į-2Å Į-12(ń) ĮŲ Äā_proc ¦ Ó_umap Äņ,#8 Ą-20(ń),ė Ą-1Ō,ī I00A7: Ąä-1Ī Ąå-1Å ×į0 sbb ā0 Ł1f or åė 1: or åģ je I00A8 Ąä-20(ń) Ąå-1Ō ×į0 sbb ā0 Ł1f or åė 1: or åģ ŁI00A9 I00A8: Ąį-14 € ĢI00A1 I00A9: Į-22(ń) Į-2Å Į-1Ō Į-20(ń) Į-1Å Į-1Ī Ó_phys_copy Äņ,#12 ‡ € I00A1:  … „  É_cause_ļg _cause_ļg: ƒ ‚ € € Ąį86 mul Å Äį688 » Äā_proc ĄĘ,ģ § Ķ84Ēö ŁI00B3 Ü_ļg_procs I00B3: § Äā84 ĄĻ,ģ ĄäĪ óė Ąęė  sal äcl or äĒ ° ‡ € Ó_inform Š … „  É_inform _inform: ƒ ‚ € € Ąį86 mul Å Äį688 » Äā_proc ĄĻ,ģ ĄåĻ Į30Ē  ž  – testb al,#8 je I00C2 ĄåĻ Ķ80Ē,#116 je I00C3 I00C2: … „  I00C3: ĄĘ,#_proc+688 I00C9: ĶĘ,#_proc+2064 jae I00C6 § Ķ84Ēö je I00C7 Ą_m+2,#64 — ×į_proc Ąā86 cwd išv ģ ×į8 Ą_m+4,ė § Ąå84Ē Ą_m+6,ģ ó_ļg_procs Ąį_m € ˆ Ąį-1 € Ó_mini_send ¤ ™ je I00CE Ąį32768 € Ąį_6 € Ó_panic Š Š I00CE: § Ą84Ēö … „  I00C7: ÄĘ,#86 ĢI00C9 I00C6: … „  É_umap _umap: ƒ ‚ ×ņ,#12 Ķ10(ń)ö ja I00D3 ‡ € € ĢI00D1 I00D3: Ąä10(ń) ÄäŌ ×į1 Ąć4 shr äcl ” ĶĪö je I00D6 ’ ĄčÅ Ąä42Ē Ää38(ļ) ĶĘ,ė jae I00D9  € ĢI00DA I00D9: “ € I00DA: ĆĪ I00D6: Ąć4 ĄäŌ shr äcl ’ € Ąį6 mul Ī Äåė ĄčÅ Ąį6 mul Ī Äčė Ąä32+4Ē Ää32(ļ) ‰ Ķåė jb I00DC ‡ € € ĢI00D1 I00DC: ’ Ąį6 mul Ī Äåė ‡ Ąå32+2Ē ĄŠ,ģ ¬ Ąć4 ĄäŠ ĄåĻ © 2: sal į1 rcl ā1 Ø 1: ĄŠ,ė ĄĻ,ģ ‡ › Ą-10(ń),ģ ĄŲ,ė Ąć4 Ąā4 Ąä-10(ń) ĮŲ – ’ € Ąį6 mul Ī Äåė Ąć4 Ąä32Ē sal äcl Ėåģ Ćķ Ćī ×ęė sbb ēģ œ Įķ Ąć4 Ąā4  – Ą-10(ń),ė ĆŲ Ąä-10(ń) ĄåŲ ÄäŠ adc å-4(ń) ¦ € I00D1:  Ćī … „  ³ _ļg_stuff: .zerow 8/2 _m: .zerow 24/2 ® _2: Ā24930 Ā8292 Ā24931 Ā27756 Ā29728 Ā8303 Ā31091 Ā24435 Ā25966 Ā28023 Ā28769 Ā10272 Ā29299 Ā10595 ø _3: Ā24930 Ā8292 Ā24931 Ā27756 Ā29728 Ā8303 Ā31091 Ā24435 Ā25966 Ā28023 Ā28769 Ā10272 Ā29540 Ā10612 ø _4: ø _5: Ā28516 Ā29535 Ā26473 Ā25376 Ā28257 Ā29735 Ā29472 Ā26473 Ā24942 Ā15212 Ā21280 Ā8272 Ā24930 Ā100 _6: Ā24931 Ā10094 Ā8308 Ā28265 Ā28518 Ā28018 Ā19744 Ā77 † /* 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 }; É_task ® _task: Ā_printer_task Ā_tty_task Ā_winchester_task Ā_floppy_task Ā_mem_task Ā_clock_task Ā_sys_task ø ø ø É_task_mess ø ³ _task_mess: .zerow 18/2 É_busy_map _busy_map: .zerow 2/2 É_rdy_tail _rdy_tail: .zerow 6/2 É_rdy_head _rdy_head: .zerow 6/2 É_bill_ptr _bill_ptr: .zerow 2/2 É_proc_ptr _proc_ptr: .zerow 2/2 É_proc _proc: .zerow 2064/2 É_k_stack _k_stack: .zerow 256/2 É_t_stack _t_stack: .zerow 1792/2 É_pc_at _pc_at: .zerow 2/2 É_olivetti _olivetti: .zerow 2/2 É_int_mess _int_mess: .zerow 24/2 É_ļg_procs _ļg_procs: .zerow 2/2 É_prev_proc _prev_proc: .zerow 2/2 É_cur_proc _cur_proc: .zerow 2/2 É_lost_ticks _lost_ticks: .zerow 2/2 É_realtime _realtime: .zerow 4/2 † /* 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ęčéźėģķīļšńņóōõö÷ųłśūüżž’     oid 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 * *===========================================================================*/ 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 ® _caps_off: Ā1 _num_off: Ā1 _unsh: Ā6912 Ā12849 Ā13363 Ā13877 Ā14391 Ā12345 Ā15661 Ā2312 Ā30577 Ā29285 Ā31092 Ā26997 Ā28783 Ā23899 Ā-32243 Ā29537 Ā26212 Ā26727 Ā27498 Ā15212 Ā24615 Ā23680 Ā30842 Ā30307 Ā28258 Ā11373 Ā12078 Ā10881 Ā8323 Ā-24188 Ā-23646 Ā-23132 Ā-22618 Ā-22104 Ā-31318 Ā-18552 Ā-17992 Ā-19319 Ā-18763 Ā-20084 Ā-19534 Ā32560 _sh: Ā6912 Ā16417 Ā9251 Ā24101 Ā10790 Ā10536 Ā11103 Ā2312 Ā22353 Ā21061 Ā22868 Ā18773 Ā20559 Ā32123 Ā-32243 Ā21313 Ā17988 Ā18503 Ā19274 Ā14924 Ā32290 Ā31872 Ā22618 Ā22083 Ā20034 Ā15437 Ā16190 Ā10881 Ā8323 Ā-28284 Ā-27758 Ā-27244 Ā-26730 Ā-26216 Ā-31590 Ā14219 Ā14648 Ā13449 Ā13877 Ā12684 Ā13106 Ā11824 _unm24: Ā6912 Ā12849 Ā13363 Ā13877 Ā14391 Ā12345 Ā24109 Ā2312 Ā30577 Ā29285 Ā31092 Ā26997 Ā28783 Ā23360 Ā-32243 Ā29537 Ā26212 Ā26727 Ā27498 Ā15212 Ā23866 Ā23680 Ā30842 Ā30307 Ā28258 Ā11373 Ā12078 Ā10881 Ā8323 Ā-24188 Ā-23646 Ā-23132 Ā-22618 Ā-22104 Ā5034 Ā-18552 Ā-17992 Ā-19319 Ā-18763 Ā-20084 Ā-19534 Ā11824 Ā3104 Ā3466 Ā-19788 Ā-18250 Ā-29926 Ā12064 Ā-21333 Ā-20819 Ā-29265 Ā-28786 _m24: Ā6912 Ā8737 Ā9251 Ā9765 Ā10279 Ā24361 Ā32317 Ā2312 Ā22353 Ā21061 Ā22868 Ā18773 Ā20559 Ā31584 Ā-32243 Ā21313 Ā17988 Ā18503 Ā19274 Ā11084 Ā32042 Ā31872 Ā22618 Ā22083 Ā20034 Ā15437 Ā16190 .word 10881 Ā8323 Ā-28284 Ā-27758 Ā-27244 Ā-26730 Ā-26216 Ā-18278 Ā14099 Ā14648 Ā13449 Ā13877 Ā12684 Ā13106 Ā32647 Ā3257 Ā3514 Ā2568 Ā7692 Ā-17638 Ā12220 Ā-25445 Ā-24931 Ā-16993 É_tty_task Ā-16450 † _tty_task: ƒ ‚ ×ņ,#26 ® _1: ĀI001D ø Ā5 ĀI001B ĀI0017 ĀI001D ĀI0018 ĀI0019 ĀI001A † Ó_tty_init • Ūä-2Å € 16 € Ó_receive Š Š Ąį898 mul -20(ń) » Äā_tty_struct Ą-2Ī,ģ Į-22(ń) ĢI0015 I0017: Ūä-2Å € Ó_do_charint Š ­ I0018: Ūä-2Å € Į-2Ī Ó_do_read Š Š ­ I0019: Ūä-2Å € Į-2Ī Ó_do_write Š Š ­ I001A: Ūä-2Å € Į-2Ī Ó_do_ioctl Š Š ­ I001B: Ūä-2Å € Į-2Ī Ó_do_cancel Š Š ­ I001D: ‡ € € € € Ąį-22 € Į-1Ō Į-2Å Ąį68 € Ó_tty_reply ‘ ­ I0015: Ąč#_1 ‰ Ģ.csa2 _do_charint: ƒ ‚ ×ņ,#20 Ó_lock ’ Ąä18Ē Ą-12(ń),ė Ą-1Å,#_tty_copy_buf Ąå-12(ń) Œ Ž ‹ ¬ £ ĄŠ,ė £ ÄĻ,ė Ä-12(ń),#2 I0023: ¹ óĻ  ™ jle I0022 Į-12(ń) Ä-12(ń),#1 ‰ Ąč-1Å Œ Ź(ļ),al Ä-1Å,#1 ĢI0023 I0022: ’ Ąä18Ē Ą-12(ń),ė Ąå-12(ń) ŹĒö Ó_unlock Ą-1Å,#_tty_copy_buf I0026: ĮŠ óŠ  ™ jle I0025 Į-1Å Ä-1Å,#1 ‰ Œ Ź-15(ń),al Į-1Å Ä-1Å,#1 ‰ Œ Ž ‹ ¬ Źal,-15(ń) Ž ‹ € ¹ Ó_in_char Š pop ļ Ąį898 mul Ļ » Äā_tty_struct Ą-1Ō,ģ Ąå-1Ō Ķ882Ēö jle I0026 Ąå-1Ō Į860Ē  ž  – Öį34  ž – ” Ąå-1Ō Ķ206Ēö jg I002B ĶĘö je I0026 Ąå-1Ō Ķ204Ēö jle I0026 I002B: Į-1Ō Ó_rd_chars Š ” Ąå-1Ō Źal,878Ē Ž ‹ ĄŲ,ė Źal,879Ē Ž ‹ Ą-10(ń),ė ‡ € € € € ” Į-10(ń) ĮŲ Ąį67 € Ó_tty_reply ‘ ĢI0026 I0025: … „  _in_char: ƒ ‚ ×ņ,#10 Ąį898 mul Å » Äā_tty_struct ĄĘ,ģ Źal,Ī Ž ‹ Ķį59 jl I0033 Źal,Ī Ž ‹ Ķį68 jg I0033 Źal,Ī Ž ‹ € Ó_func_key Š … „  I0033: § Ķ204Ē,#200 jl I0037 … „  I0037: mov åĘ Į860Ē  ž  – Öį34  ž – ¬ § cmpb 869Ēö je I003A Źal,Ī Ž ‹ € Ó_make_break Š ŹĪ,al ĢI003B I003A:  ž £ – Ķį32 je I003B  ž Źal,Ī Ž – Öį127  ž – ŹĪ,al I003B: cmpb Īö ŁI00310 … „  I00310: ĶĻö je I00312  ž £ – Ķį2 ŁI00313 I00312: ĶĻö ŁI00317 Źal,Ī Ž ‹ § € Źal,871Ē Ž ‹ ‰ Ķåė ŁI0031A § cmpb 867Ēö ŁI0031A ” Ó_chuck Š Ķį-1 je I0031E Ąį8 € ” Ó_echo Š Š Ąį32 € ” Ó_echo Š Š Ąį8 € ” Ó_echo Š Š I0031E: … „  I0031A: Źal,Ī Ž ‹ § € Źal,872Ē Ž ‹ ‰ Ķåė ŁI00321 § cmpb 867Ēö ŁI00321 I00325: ” Ó_chuck Š ™ ŁI00324 ĢI00325 I00324: § Źal,872Ē Ž ‹ € ” Ó_echo Š Š 0 € ” Ó_echo Š Š … „  I00321: § cmpb 867Ēö ŁI00328 cmpb Ī,#92 ŁI0032B § Ź867Ē,#1 Źal,Ī Ž ‹ € ” Ó_echo Š Š … „  I0032B: Źal,Ī Ž ‹ § € Źal,877Ē Ž ‹ ‰ Ķåė ŁI00317 ŹĪö ĢI00317 I00328: § Ź867Ēö Źal,Ī Ž ‹ § € Źal,871Ē Ž ‹ ‰ Ķåė je I00317 Źal,Ī Ž ‹ § € Źal,872Ē Ž ‹ ‰ Ķåė je I00317 Źal,Ī Ž ‹ § € Źal,877Ē Ž ‹ ‰ Ķåė je I00317 § Äā200 Ą-10(ń),ģ Ąč-10(ń) Ąį92 € ĮĒ Ä(ļ),#1 ‰  ŗ § Äā204 Ą-10(ń),ģ « Üė ° § ĄčĘ Äč#200 Ķ200Ē,ļ ŁI00317 § — Ą200Ē,ė I00317: cmpb Ī,#13 ŁI00339 § Į860Ē  ž  – testb al,Ń je I00339 ŹĪ,#10 I00339: Źal,Ī Ž ‹ § € Źal,873Ē Ž ‹ ‰ Ķåė je I0033C Źal,Ī Ž ‹ § € Źal,874Ē Ž ‹ ‰ Ķåė ŁI0033D I0033C: Źal,Ī Ž ‹ § € Źal,873Ē Ž ‹ ‰ Ķåė ŁI00341 “ € ĢI00342 I00341: Ąį3 € I00342: ĆŠ ĮŠ ˆ ” Ó_ļgchar ¤ … „  I0033D: Źal,6(ń) Ž ‹ § € Źal,876Ē Ž ‹ ‰ Ķåė ŁI00344 § Ź868Ē,#1 … „  I00344: Źal,Ī Ž ‹ § € Źal,875Ē Ž ‹ ‰ Ķåė ŁI00313 § Ź868Ēö § Ąä858Ē ” Ó(ė) Š … „  I00313: cmpb Ī,#10 je I00349 cmpb Īö ŁI0034A I00349: § Äā206 Ą-10(ń),ģ « Üė ° I0034A: § Äā200 Ą-10(ń),ģ Ąč-10(ń) ‡ Źal,Ī € ĮĒ Ä(ļ),#1 ‰  ŗ § ĄčĘ Äč#200 Ķ200Ē,ļ ŁI0034E § — Ą200Ē,ė I0034E: § Äā204 Ą-10(ń),ģ « Üė ° Źal,Ī Ž ‹ € ” Ó_echo Š Š … „  _make_break: ƒ ‚ ×ņ,Ń ® _2: ĀI00444 ø .word 5 ĀI00445 ĀI00446 ĀI00447 ĀI00448 ĀI00449 ĀI0044E † Źal,Å Ž ‹  ž – Öį127  ž – ” Źal,Å Ž ‹  ž – testb al,#128 je I0043 ‡ € ĢI0044 I0043:  € I0044: ĆĻ Ķ_olivettiö ŁI0046 Ķ_shift1ö ŁI0048 Ķ_shift2ö je I0049 I0048: § Źal,_shĒ Ž ‹ € ĢI004A I0049: § Źal,_unshĒ Ž ‹ € I004A: ĆŠ Ķ_controlö je I004D ĶĘ,#14 jge I004D § Źal,_shĒ Ž ‹ ĄŠ,ė I004D: ĶĘ,#70 jle I0047 Ķ_numlockö je I0047 Ķ_shift1ö ŁI00414 Ķ_shift2ö je I00415 I00414: § Źal,_unshĒ Ž ‹ € ĢI00416 I00415: § Źal,_shĒ Ž ‹ € I00416: ĆŠ ĢI0047 I0046: Ķ_shift1ö ŁI00418 Ķ_shift2ö je I00419 I00418: § Źal,_m24Ē Ž ‹ € ĢI0041A I00419: § Źal,_unm24Ē Ž ‹ € I0041A: ĆŠ Ķ_controlö je I0041D ĶĘ,#14 jge I0041D § Źal,_shĒ Ž ‹ ĄŠ,ė I0041D: ĶĘ,#70 jle I0047 Ķ_numlockö je I0047 Ķ_shift1ö ŁI00424 Ķ_shift2ö je I00425 I00424: § Źal,_unm24Ē Ž ‹ € ĢI00426 I00425: § Źal,_m24Ē Ž ‹ € I00426: ĆŠ I0047:  ž ĄäŠ – „  ž – ĄŠ,ė  ž ĄäŠ – Ķį128 jb I00428  ž ĄäŠ – Ķį134 jb I00429 I00428: Ķ_capslockö je I0042D ĶŠ,#65 jl I00430 ĶŠ,#90 jg I00430 ÄŠ,#32 ĢI0042D I00430: ĶŠ,#97 jl I0042D ĶŠ,#122 jg I0042D ĄäŠ ×į32 ĄŠ,ė I0042D: Ķ_altö je I00438  Ąbx,#2 ĄäŠ – or į128  ž – ĄŠ,ė I00438: Ķ_controlö je I0043B  ž ĄäŠ – Öį31  ž – ĄŠ,ė I0043B: ĶŠö ŁI0043E ĄŠ,#144 I0043E: ĶĻö ŁI00441 ĄŠö I00441: ĮŠ ĢI0041 I00429:  ž ĄäŠ – ×į128 € ĢI00443 I00445: ĄåĻ Ą_shift1,ģ ĢI00444 I00446: ĄåĻ Ą_shift2,ģ ĢI00444 I00447: ĄåĻ Ą_control,ģ ĢI00444 I00448: ĄåĻ Ą_alt,ģ ĢI00444 I00449: ĶĻö je I0044B Ķ_caps_offö je I0044B  ×ä_capslock Ą_capslock,ė I0044B:  ×äĻ Ą_caps_off,ė ĢI00444 I0044E: ĶĻö je I00450 Ķ_num_offö je I00450  ×ä_numlock Ą_numlock,ė I00450:  ×äĻ Ą_num_off,ė ĢI00444 I00443: Ąč#_2 ‰ Ģ.csa2 I00444: ‡ € I0041:  … „  _echo: ƒ ‚ ’ Į860Ē  ž  – testb al,#8 ŁI0053 … „  I0053: cmpb Īö je I0056 Źal,Ī Ž ‹ € ˆ Ó_out_char Š Š I0056: ˆ Ó_flush Š … „  _chuck: ƒ ‚ € € ’ Ķ204Ēö ŁI0063 Ąį-1 € ĢI0061 I0063: ’ ¢ Ķ200Ē,ė je I0066 ’ Ąä200Ē óė € ĢI0067 I0066: ’ Äā199 ¦ I0067: ĆĘ § cmpb Ē,#10 je I0068 § cmpb Ē,#13 ŁI0069 I0068: Ąį-1 € ĢI0061 I0069: ’ — Ą200Ē,ė ’ Äā204 ĄĻ,ģ « óė ° ‡ € I0061:  … „  _do_read: ƒ ‚ € € ’ Ķ882Ēö jle I0073 Æ ĄčĪ ‡ € € € Įė Ąį-3 € Į6Ē Į(ļ) Ąį68 € Ó_tty_reply ‘ … „  I0073: Æ ĄčÅ « Ź878(ļ),al Æ ĄčÅ Ąä6Ē Ź879(ļ),al Æ ĄčÅ Ąä18Ē Ą880(ļ),ė Æ ĄčÅ Ąä8Ē Ą882(ļ),ė ˆ Ó_rd_chars Š ” ’ Źal,879Ē Ž ‹ ¬ Æ ‡ € € € € ” ¹ ĮĒ Ąį68 € Ó_tty_reply ‘ … „  _rd_chars: ƒ ‚ ×ņ,#38 ’ Į860Ē  ž  – testb al,#34 je I0083 ‡ € ĢI0084 I0083:  € I0084: ĆĘ ’ Ķ204Ēö je I0085 ĶĘö je I0086 ’ Ķ206Ēö ŁI0086 I0085: Ąį-998 € ĢI0081 I0086: ’ Źal,879Ē Ž ‹ Ąā86 mul ģ Äį688 » add ā_proc Ą-32(ń),ģ ’ Ąä880Ē Ą-1Ī,ė Į882Ē  ž  – Ą-1Ō,ė Į-1Ō Į-1Ī  € Į-32(ń) Ó_umap Äņ,#8 Ą-22(ń),ė Ą-20(ń),ī Ąä-22(ń) Ąå-20(ń) ×į0 sbb ā0 Ł1f or åė 1: or åģ ŁI008B Ąį-10 € ĢI0081 I008B: “56 € Ąį_tty_buf €  € Ąį_proc+86 € Ó_umap Äņ,#8 Ą-2Ī,ė Ą-2Å,ī Ą-10(ń)ö Ą-12(ń)ö Ą-1Åö I008E: ’ Ķ882Ēö jle I008D ’ ĄčÅ Ąä204(ļ) Ķ882Ē,ė jge I00811 ’ Į882Ē ĢI00812 I00811: ’ Į204Ē I00812: ĆŲ ĶŲ,#256 jge I00814 ĮŲ ĢI00815 I00814: “56 € I00815: ĆŲ ĄĻö Ą-30(ń),#_tty_buf I00817: ĮŲ óŲ  ™ jle I00816 ’ Äā202 Ą-3Å,ģ Ąč-3Å ĮĒ Ä(ļ),#1 pop ģ Œ Ź-27(ń),al ’ ĄčÅ Äč#200 Ķ202Ē,ļ ŁI0081A ’ ¢ Ą202Ē,ė I0081A: Ąå-30(ń) Źal,-27(ń) ŗ Ä-30(ń),#1 ÜĻ cmpb -27(ń),#10 je I0081C cmpb -27(ń)ö ŁI00817 I0081C: ’ Äā206 Ą-3Å,ģ « óė ° ĶĘö je I00821 cmpb -27(ń)ö ŁI00821 Ü-1Å I00821: Ü-12(ń) ĶĘö je I00817 I00816: Ķ-1Åö je I00828 £ óė € ĢI00829 I00828: ¹ I00829: ĆŠ ĄäŠ cwd œ € Į-20(ń) Į-22(ń) Į-2Å Į-2Ī Ó_phys_copy Äņ,#12 ĄäŠ cwd Ää-22(ń) adc ē-20(ń) Ą-22(ń),ė Ą-20(ń),ī ĄäŠ Ä-10(ń),ė ’ Äā882 Ą-3Ō,ģ « ×äĻ ° ’ Äā204 Ą-3Ō,ģ « ×äĻ ° ’ Ķ204Ēö je I008D Ķ-12(ń)ö je I008E I008D: Ąå4(bp) Ą882Ēö Į-10(ń) I0081:  … „  _finish: ƒ ‚ € € ’ Ą848Ēö ’ Ą892Ēö ’ cmpb 870Ēö ŁI0093 … „  I0093: ’ Źal,884Ē Ž ‹ ” Źal,885Ē Ž ‹ ¬ ‡ € € € € “ ¹ ” Ąį68 € Ó_tty_reply ‘ ’ Ź870Ēö … „  _do_write: ƒ ‚ ×ņ,#6 Æ ĄčÅ « Ź884(ļ),al Æ ĄčÅ Ąä6Ē Ź885(ļ),al Æ ĄčÅ Ąä18Ē Ą886(ļ), ė Æ ĄčÅ Ąä8Ē Ą892(ļ),ė ’ Ź870Ē,#1 ’ Ą894Ēö ’ Źal,885Ē Ž ‹ Ąā86 mul ģ Äį688 » Äā_proc ĄŠ,ģ ’ Ąä886Ē ” Į892Ē  ž  call Ś ¬ ¹ ”  € ĮŠ Ó_umap Äņ,#8 ’ Ą888Ē,ė Ą890Ē,ī ’ Ąä888Ē Ąę890Ē ×į0 sbb ć0 Ł1f or ęė 1: or ęķ ŁI00A3 ’ Ą894Ē,#-10 ’ Ą892Ēö I00A3: ’ Ąä858Ē ˆ Ó(ė) Š … „  _do_ioctl: ƒ ‚ ×ņ,#38 ® _3: ĀI00B8 Ā4 Ā29704 ĀI00B6 Ā29705 ĀI00B4 Ā29713 ĀI00B5 Ā29714 ĀI00B7 † Ą-3Ōö ĄĻö ¼ ĄŲö ĄŠö Æ Į8Ē ĢI00B2 I00B4: Æ Ąć8 Ąä10Ē Ąē12Ē © 2: sar ē#1 rcr į1 Ø 1: Ąć4 Ąā4 œ – ‰ „ Öā0 ’ Ź871Ē,al Æ Ėęķ Ąä10Ē Ąē12Ē © 2: sar ē#1 rcr į1 Ø 1: Ąć4 Ąā4 œ – ‰ „ Öā0 mov åÅ Ź872Ē,al Æ ĄčÅ Ąä14Ē Ą860(ļ),ė ĢI00B3 I00B5: Æ 4 Ąä10Ē Ąē12Ē © 2: sar ē#1 rcr į1 Ø 1: Ąć4 Ąā4 œ – ‰ „ Öā0 ’ Ź873Ē,al Æ Ąć16 Ąä10Ē Ąē12Ē © 2: sar ē#1 rcr į1 Ø 1: Ąć4 Ąā4 œ – ‰ „ Öā0 ’ Ź874Ē,al Æ Ąć8 Ąä10Ē Ąē12Ē © 2: sar ē#1 rcr į1 Ø 1: Ąć4 Ąā4 œ – ‰ „ Öā0 ’ Ź875Ē,al Æ Ėęķ Ąä10Ē Ąē12Ē © 2: sar ē#1 rcr į1 Ø 1: Ąć4 Ąā4 œ – ‰ „ Öā0 ’ Ź876Ē,al Æ Ąć8 Ąä14Ē Ąē16Ē © 2: sar ē#1 rcr į1 Ø 1: Ąć4 Ąā4 œ – ‰ „ Öā0 Ąå4(ń) Ź877Ē,al ĢI00B3 I00B6: ’ Źal,871Ē Ž ‹ cwd Ąć4 Ąā4 œ – ‰ „ Öā0 ¦ Ąć4 Ąā4 – Ą-12(ń),ė Ć-10(ń) ’ Źal,872Ē Ž ‹ cwd Ąć4 Ąā4 œ – ‰ „ Öā0 ¦ Ąć4 Ąā4 – Ą-1Ī,ė Ć-1Å Ąć8 Ąä-12(ń) Ąå-10(ń) © 2: sal į1 rcl ā1 Ø 1: or ä-1Ī or å-1Å ĄŲ,ė ĄŠ,ģ ’ Ąä860Ē cwd ¬ ĄĘ,ī ĢI00B3 I00B7: ’ Źal,873Ē Ž ‹ cwd Ąć4 Ąā4 œ – ‰ „ Öā0 ¦ Ąć4 Ąā4 – Ą-20(ń),ė Ć-1Ō ’ Źal,874Ē Ž ‹ cwd Ąć4 Ąā4 œ – ‰ „ Öā0 ¦ Ąć4 Ąā4 – Ą-2Å,ė Ć-22(ń) ’ Źal,875Ē Ž ‹ cwd mov ć4 Ąā4 œ – ‰ „ Öā0 ¦ Ąć4 Ąā4 – Ą-2Ō,ė Ć-2Ī ’ Źal,876Ē Ž ‹ cwd Ąć4 Ąā4 œ – ‰ „ Öā0 ¦ Ąć4 Ąā4 – Ą-32(ń),ė Ć-30(ń) ’ Źal,877Ē Ž ‹ cwd Ąć4 Ąā4 œ – ‰ „ Öā0 ¦ Ąć4 Ąā4 – Ą-3Ī,ė Ć-3Å Ąć16 Ąä-2Å Ąå-22(ń) © 2: sal į1 rcl ā1 Ø 1: 4 Ąē-20(ń) Ąč-1Ō © 2: sal ē#1 rcl č#1 Ø 1: or ēė or čģ Ąć8 Ąä-2Ō Ąå-2Ī © 2: sal į1 rcl ā1 Ø 1: or äī or åļ Ėęķ Ąē-32(ń) Ąč-30(ń) © 2: sal ē#1 rcl č#1 Ø 1: or ēė or čģ ĄŲ,ī ĄŠ,ļ Ąć8 Ąä-3Ī Ąå-3Å © 2: sal į1 rcl ā1 Ø 1: ¬ ĄĘ,ģ ĢI00B3 I00B8: Ą-3Ō,#-22 ĢI00B3 I00B2: Ąč#_3 Ćī Ģ.csb2 I00B3: Æ ĄčĪ ĮŠ ĮŲ ” ¹ Į-3Ō Į6Ē Į(ļ) Ąį68 € Ó_tty_reply ‘ … „  _do_cancel: ƒ ‚ ’ Ķ882Ēö ŁI00C3 ’ Ķ892Ēö ŁI00C3 … „  I00C3: ’ ¢ Ą200Ē,ė ’ ¢ Ą202Ē,ė ’ Ą204Ēö ’ Ą206Ēö ’ Ą882Ēö ’ Ą892Ēö ’ Ź870Ēö ’ Ź868Ēö Æ ĄčĪ ‡ € € € € Ąį-4 € Į6Ē Į(ļ) Ąį68 € Ó_tty_reply ‘ … „  _tty_reply: ƒ ‚ ×ņ,#24 ¢ Ą-22(ń),ė ĄäŌ Ą-20(ń),ė Ąä10(ń) Ą-1Ō,ė Ąä12(ń) Ą-10(ń),ė Ąä1Å ĄŲ,ė Ąä1Ī Ą-1Å,ax Ąä1Ō Ą-12(ń),ė Ūä-2Å € “ Ó_send Š Š … „  _ļgchar: ƒ ‚ ’ Ź868Ēö Ąį-4 € ˆ Ó_finish Š Š ’ ¢ Ą200Ē,ė ’ ¢ Ą202Ē,ė ’ Ą204Ēö ’ Ą206Ēö ĄäĪ Äį3 ĮŌ € Ó_cause_ļg Š Š … „  É_keyboard _keyboard: ƒ ‚ ×ņ,#10 ŪäĻ € Ąį96 € Ó_port_in Š Š ŪäĘ € Ąį97 € Ó_port_in Š Š  ž — – or į128 € Ąį97 € Ó_port_out Š Š ” Ąį97 € Ó_port_out Š Š  ž £ – ×į128  ž – ĄŠ,ė ĶŠö jle I00F3 ĶŠ,#29 je I00F4 ĶŠ,#42 je I00F4 ĶŠ,#54 je I00F4 cmp Š,#56 je I00F4 ĶŠ,#58 je I00F4 ĶŠ,#69 je I00F4 Ąį32 € € Ó_port_out Š Š … „  I00F3:  ž Ąä_tty_struct+860 – Öį32  ž – ĄŲ,ė Źcl,_tty_struct+876 Ź-9(ń),cl ĶŲö ŁI00F4 Ķ_controlö je I00F4 ĶĻ,#31 ŁI00F4 cmpb -9(ń),#19 ŁI00F4 Ź_tty_struct+868,#1 Ąį32 € € Ó_port_out Š Š … „  I00F4: Ķ_controlö je I00F14 Ķ_altö je I00F14 ĶĻ,#83 ŁI00F14 Ó_reboot I00F14: Źal,_tty_driver_buf Ž ‹ ĄŠ,ė Źal,_tty_driver_buf+1 Ž ‹ ĶŠ,ė jge I00F19 ĄäŠ ÄŠ,ė ĄäŠ Äį2 » £ Ź_tty_driver_bufĒ,al ĄäŠ Äį3 » Ź_tty_driver_bufĒö Źal,_tty_driver_buf Ž ‹ Üė Ź_tty_driver_buf,al Ą_keybd_mess+2,#1 Ą_keybd_mess+18,#_tty_driver_buf Ąä#_keybd_mess € Ąį-7 € Ó_interrupt Š Š ĢI00F1A I00F19: Ąį32 € € Ó_port_out Š Š I00F1A: … „  _console: ƒ ‚ ×ņ,#12 ’ Ąć4 Ąä888Ē Ąē890Ē © 2: sar ē#1 rcr į1 Ø 1: Ąć4 Ąā4 œ – ‰ Öį65535 Öā0 ĄŠ,ė ’ Į890Ē Į888Ē Ąć4 Ąā4  – ‰ Öį15 Öā0 ĄŲ,ė ĄäŲ Ą-10(ń),ė ¼ I0103: ’ Ķ892Ēö jle I0102 ’ cmpb 868Ēö ŁI0102 ĮŲ ĮŠ Ó_get_byte Š Š Ź-3(ń),al Źal,-3(ń) Ž ‹ € ˆ Ó_out_char Š Š ĄäŲ Äį1 ĄŲ,ė ’ Äā892 Ą-12(ń),ģ « óė ° ĢI0103 I0102: ˆ Ó_flush Š ĄäŲ ×ä-10(ń)  ž – ” Ąå4(bp) Äā888 Ą-12(ń),ģ Ąå-12(ń) — cwd ÄäĒ adc ē2Ē ° Ą2Ē,ī ’ Äā894 Ą-12(ń),ģ « ÄäĘ ° ’ Ķ892Ēö ŁI0107 ’ Į894Ē ˆ Ó_finish Š Š I0107: … „  _out_char: ƒ ‚ € ® _4: ĀI0111F Ā9 Ā7 ĀI011A Ā8 ĀI011E Ā9 ĀI01117 Ā10 ĀI011F Ā11 ĀI011B Ā12 ĀI011C Ā13 ĀI01116 Ā14 ĀI011D Ā27 ĀI0111E † ’ cmpb 854Ē,#1 ŁI0113 ’ Źal,Ī Ź855Ē,al ’ Ź854Ē,#2 … „  I0113: ’ cmpb 854Ē,#2 ŁI0116 Źal,Ī Ž ‹ ’ € Źal,855Ē Ž ‹ € ˆ Ó_escape ¤ ’ Ź854Ēö … „  I0116: Źal,Ī Ž ‹ € ĢI0118 I011A: ˆ Ó_flush pop ļ 331 € Ó_beep Š … „  I011B: ’ Ąä864Ē Üė € Į862Ē ˆ Ó_move_to ¤ … „  I011C: ’ ĄčÅ Ąä862(ļ) Üė Į864Ē € ˆ Ó_move_to ¤ … „  I011D: ’ ĄčÅ Ąä862(ļ) Üė Į864Ē € ˆ Ó_move_to ¤ … „  I011E: ’ ĄčÅ Ąä862(ļ) óė Į864Ē € ˆ Ó_move_to ¤ … „  I011F: ’ Į860Ē  ž  – testb al,Ń je I01111 3 € ˆ Ó_out_char Š Š I01111: ’ Ķ864Ēö ŁI01114 ‡ € ˆ Ó_scroll_screen Š Š ĢI01115 I01114: ’ Äā864 ĄĘ,ģ « óė ° I01115: ’ ĄčÅ Į864Ē Į862(ļ) ˆ Ó_move_to Äņ,#6 … „  I01116: ’ Į864Ē ‡ € ˆ Ó_move_to ¤ … „  I01117: ’ Į860Ē  ž  – Öį3072 Ķį3072 ŁI01119 I0111D: Ąį32 € ˆ Ó_out_char Š Š ’ Į862Ē  ž  – testb al,#7 ŁI0111D … „  I01119: … „  I0111E: ˆ Ó_flush Š ’ Ź854Ē,#1 … „  I0111F: ’ Ķ862Ē,#80 jl I01121 … „  I01121: ’ Ķ848Ē,#320 ŁI01124 ˆ Ó_flush Š I01124: Źal,Ī Ž ‹ ’ or ä856Ē ĄčÅ Äč#848 ĄĘ,ļ ĄéĘ Ąę(š) Üķ € Äā208 ¦ Į(ļ) Ą(š),ķ  sal į1 ‰ Äåė ĆĒ ’ Äā862 ĄĘ,ģ « Üė ° … „  I0118: Ąč#_4 Ćī jmp .csb2 _scroll_screen: ƒ ‚ € € ĶĪö ŁI0123 60 € ĢI0124 I0123: Ąį-160 € I0124: ĆĘ ’ Ąä850Ē ÄäĘ Öä_vid_mask Ą850Ē,ė ĶĪö ŁI0126 ’ Ąä850Ē Äį3840 Öä_vid_mask ¬ ĢI0127 I0126: ’ Ąä850Ē ¬ I0127: Ąį80 € ¹ Į_vid_base ‡ € Ó_vid_copy Äņ,#8 ’ Ąä850Ē sar į1 € 2 € Ó_set_6845 Š Š … „  _flush: ƒ ‚ ’ Ķ848Ēö ŁI0133 … „  I0133: ’ ĄčÅ ĄéÅ Į848Ē Į852(ļ) Į_vid_base Äé#208 Įš Ó_vid_copy Äņ,#8 ’ Ąä848Ē sal į1 Ää852Ē Ą852Ē,ė ’ Ąä852Ē sar į1 € 4 € Ó_set_6845 Š Š ’ Ą848Ēö … Ćbp  _move_to: ƒ ‚ ˆ Ó_flush Š ĶĪö jl I0142 ĶĪ,#80 jge I0142 ĶŌö jl I0142 ĶŌ,#25 jl I0143 I0142: … „  I0143: ’ ĄäĪ Ą862Ē,ė ’ ĄäŌ Ą864Ē,ė “4 ×äŌ sal į1 Ąā80 mul ģ ’ Ää850Ē Æ sal ā1 Äåė ĄčÅ Ą852(ļ),ģ ’ Ąä852Ē sar į1 € 4 € Ó_set_6845 Š Š … „  _escape: ƒ ‚ ×ņ,#10 cmpb Ī,#122 ŁI0153 Źal,Ō Ž ‹ Ąć8 sal äcl ’ Ą856Ē,ė … „  I0153: cmpb Ī,#126 ŁI0156 cmpb Ō,#48 ŁI0159 ’ 60 mul 864Ē Äį160 Ąå862Ē sal ā1 ×åė ōģ ĄĘ,ģ ’ Ąä852Ē ĄŠ,ė I015C: ĶĘö jle I015A Ąå_vid_race ĶĘ,ģ jge I015F ” ĢI01510 I015F: Į_vid_race I01510: ĆĻ ž £ cwd išv ģ € ĮŠ Į_vid_base ‡ € Ó_vid_copy Äņ,#8 £ ÄŠ,ė — ×äĻ ” ĢI015C I0159: cmpb Ō,#49 ŁI015A  € ˆ Ó_scroll_screen Š Š I015A: … „  I0156: Źal,Ō Ž ‹ ×į32 € Źal,Ī Ž ‹ ×į32 € ˆ Ó_move_to ¤ … „  _set_6845: ƒ ‚ Ąį4 Ää_vid_port ˆ € Ó_port_out Š Š Ąć8 ĄäĪ sar äcl  ž – „ Ąā5 Äå_vid_port € ¦ Ó_port_out Š Š ¢ Üė Ąā4 Äå_vid_port € ¦ Ó_port_out Š Š  ž ĄäĪ – „ Ąā5 Äå_vid_port € ¦ Ó_port_out Š Š … „  _beep: ƒ ‚ € € Ó_lock 82 € Ąį67 € Ó_port_out Š Š  ž ¢ – „ € Ąį66 € Ó_port_out Š Š Ąć8 ¢ sar äcl  ž – „ € Ąį66 € Ó_port_out Š Š ŪäĘ € Ąį97 € Ó_port_in Š Š — or į3 € Ąį97 € Ó_port_out Š Š ĄĻö I0175:  ž £ – Ķį8192 jae I0172 ÜĻ ĢI0175 I0172: ” Ąį97 € Ó_port_out Š Š Ó_unlock … „  _tty_init: ƒ ‚ ×ņ,Ń Ąį4 € Ąį964 € Ó_port_out Š Š  € Ąį965 € Ó_port_out Š Š ĄĘ,#_tty_struct I0185: ĶĘ,#_tty_struct+898 jae I0182 § — Ą200Ē,ė § — Ą202Ē,ė Ąbx,Ę Ą860Ē,#3096 § Ą858Ē,#_console § Ź871Ē,#8 § Ź872Ē,#64 § Ź873Ē,#127 § Ź874Ē,#28 § Ź875Ē,#17 § Ź876Ē,#19 § Ź877Ē,#4 ÄĘ,#898 ĢI0185 I0182: Ź_tty_struct+869,#1 Ķ_colorö je I0187 Ą_vid_base,#47104 Ą_vid_mask,Ń383 Ą_vid_port,#976 Ą_vid_race,#768 ĢI0188 I0187: Ą_vid_base,#45056 Ą_vid_mask,#4095 Ą_vid_port,#944 Ą_vid_race,#28672 I0188: Ą_tty_struct+856,#1792 Ź_tty_driver_buf+1,Ń Ąį31 € 0 € Ó_set_6845 Š Š ‡ € 2 € Ó_set_6845 Š Š ‡ € € Ąį_tty_struct € Ó_move_to ¤ Ķ_scan_code,#12 ŁI018A Ą_olivetti,#1 I018A: … „  É_putc _putc: ƒ ‚ Źal,Å Ž ‹ € Ąį_tty_struct € Ó_out_char Š Š mov ņ,ń „  _func_key: ƒ ‚ cmpb Å,#59 ŁI01A3 Ó_p_dmp I01A3: cmpb Å,#60 ŁI01A6 Ó_map_dmp I01A6: cmpb Å,#67 ŁI01A9 Ąį9 € ‡ € Ąį_tty_struct € Ó_ļgchar ¤ I01A9: … „  ³ _vid_port: .zerow 2/2 É_vid_mask _vid_mask: .zerow 2/2 _vid_base: .zerow 2/2 _vid_race: .zerow 2/2 É_keybd_mess _keybd_mess: .zerow 24/2 É_scan_code _scan_code: .zerow 2/2 É_color _color: .zerow 2/2 _alt: .zerow 2/2 _control: .zerow 2/2 _numlock: .zerow 2/2 _capslock: .zerow 2/2 _shift2: .zerow 2/2 _shift1: .zerow 2/2 _tty_buf: .zerow 256/2 _tty_copy_buf: .zerow 32/2 _tty_driver_buf: .zerow 34/2 _tty_struct: .zerow 898/2 † /* 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 /* This file contains a driver for the IBM or DTC winchester controller. * This version of the driver contains Dan Dugger's Z-150 modifications * to the com_out() function. * * It also contains some minor hacks to support the Adaptec ACB-2070a * RLL controller, marked with a HERE. * * things hacked so far: * NR_SECTORS changed from 0x11 to 0x19 -- 25 sectors/track * things to check further: * are ports the same for IBM/DTC and ACB2070a? * -- apparently * are we reading the partition table correctly? * -- we're ignoring it for the moment * how should we handle the on-disk parameter [BIOS?] * -- find out from Adaptec how to read the bugger * * 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 task. */ #define WIN_DATA 0x320 /* winchester disk controller data register */ #define WIN_STATUS 0x321 /* winchester disk controller status register */ #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 */ #define CTRL_BYTE 5 /* Control byte for controller */ /* 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 */ #if 0 #define NR_SECTORS 0x11 /* number of sectors per track */ #endif /* HERE */ #define NR_SECTORS 0x19 /* number of sectors per track, Adaptec */ /* 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 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 */ 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 */ } 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_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 * 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 */ } )+,-./0123456789return(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 << 5)); command[2] = (((wn->wn_cylinder & 0x0300) >> 2) | wn->wn_sector); command[3] = (wn->wn_cylinder & 0xFF); command[4] = BLOCK_SIZE/SECTOR_SIZE; command[5] = CTRL_BYTE; if (com_out(DMA_INT) != OK) return(ERR); port_out(DMA_INIT, 3); /* initialize DMA */ /* Block, waiting for disk interrupt. */ receive(HARDWARE, &w_mess); /* 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)) return(OK); command[0] = WIN_SENSE; command[1] = (wn->wn_drive << 5); if (com_out(NO_DMA_INT) != OK) return(ERR); /* Loop, extracting bytes from WIN */ for (i = 0; i < MAX_RESULTS; i++) { if (hd_wait(1) != OK) return(ERR); port_in(WIN_DATA, &status); wn->wn_results[i] = status & BYTE; } if (wn->wn_results[0] & 63) 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. */ if (w_need_reset) return; /* if controller is not listening, return */ if (hd_wait(1) == OK) 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 = 1, i; /* Strobe reset bit low. */ port_out(WIN_STATUS, r); for (i = 0; i < 10000; i++) { port_in(WIN_STATUS, &r); if ( (r&01) == 0)break; } if (r & 2) { printf("Hard disk won't reset\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] = WIN_SPECIFY; /* Specify some parameters */ command[1] = 0; /* Drive 0 */ if (com_out(NO_DMA_INT) != OK) /* Output command block */ return(ERR); lock(); /* No. of cylinders (high byte) */ win_out(param0.nr_cyl >> 8); /* No. of cylinders (low byte) */ win_out(param0.nr_cyl & 0xFF); /* No. of heads */ win_out(param0.nr_heads); /* Start reduced write (high byte) */ win_out(param0.reduced_wr >> 8); /* Start reduced write (low byte) */ win_out(param0.reduced_wr & 0xFF); /* Start write precompensation (high byte) */ win_out(param0.wr_precomp >> 8); /* Start write precompensation (low byte) */ win_out(param0.wr_precomp & 0xFF); /* Ecc burst length */ win_out(param0.max_ecc); unlock(); if (check_init() != OK) { /* See if controller accepted parameters */ w_need_reset = TRUE; return(ERR); } if (nr_drives > 1) { command[1] = (1 << 5); /* Drive 1 */ if (com_out(NO_DMA_INT) != OK) /* Output command block */ return(ERR); lock(); /* No. of cylinders (high byte) */ win_out(param1.nr_cyl >> 8); /* No. of cylinders (low byte) */ win_out(param1.nr_cyl & 0xFF); /* No. of heads */ win_out(param1.nr_heads); /* Start reduced write (high byte) */ win_out(param1.reduced_wr >> 8); /* Start reduced write (low byte) */ win_out(param1.reduced_wr & 0xFF); /* Start write precompensation (high byte) */ win_out(param1.wr_precomp >> 8); /* Start write precompensation (low byte) */ win_out(param1.wr_precomp & 0xFF); /* Ecc burst length */ win_out(param1.max_ecc); unlock(); if (check_init() != OK) { /* See if controller accepted parameters */ w_need_reset = TRUE; return(ERR); } } for (i=0; i= MAX_WIN_RETRY) { w_need_reset = TRUE; return(ERR); } else return(OK); } /*============================================================================* * com_out * *============================================================================*/ /* From: n0ano@wldrdg.UUCP (Don Dugger) Date: 13 Apr 87 17:28:28 GMT Organization: Wildridge Consulting, Boulder, CO Having recently installed MINIX on my Zenith 150 I discovered that I had a read-only hard disk. When writing, every few blocks would confuse the driver which would try to reset the disk (a very lengthy process) and would frequently not write out the block. After debugging the Zenith BIOS I came up with the following fix which seems to correct the problem. Using this fix I have copied all of the MINIX source to my hard disk and I've recompiled the kernel about a half-dozen times, all with no errors. The fix is a new version of routine `com_out' for the file `kernel/wini.c'. Just replace `com_out' with this one, recompile the kernel and the hard disk should work. Don Dugger Wildridge Consulting ...nbires!onecom!wldrdg!n0ano */ PRIVATE com_out(mode) int mode; { /* Output the command block to the winchester controller and return status */ register int i = 0; int r; port_out(WIN_DMA, mode); port_out(WIN_SELECT, mode); for (i=0; i<300; i++) { port_in(WIN_STATUS, &r); if (r & 8) break; } if (i == 300) { w_need_reset = TRUE; return(ERR); } lock(); for (i=0; i<6; i++) { for (;;) { port_in(WIN_STATUS, &r); if (r & 1) break; if ((r & 8) == 0) { w_need_reset = TRUE; unlock(); return(ERR); } } if ((r & 0xe) != 0xc) { w_need_reset = TRUE; unlock(); return(ERR); } port_out(WIN_DATA, command[i]); } unlock(); return(OK); } /*============================================================================* * init_params * *============================================================================*/ PRIVATE init_params() { /* This routine is called at startup to initialize the partition table, * the number of drives and the controller */ unsigned int i, segment, offset; int type_0, type_1; phys_bytes address; extern phys_bytes umap(); extern int vec_table[]; /* Read the switches from the controller */ port_in(WIN_SELECT, &i); /* Calculate the drive types */ type_0 = (i >> 2) & 3; type_1 = i & 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 = ((long)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); /* Get the nummer of drives from the bios */ phys_copy(0x475L, umap(proc_addr(WINCHESTER), D, buf, 1), 1L); nr_drives = (int) *buf; /* Set the parameters in the drive structure */ for (i=0; i<5; i++) wini[i].wn_heads = param0.nr_heads; wini[0].wn_low = wini[5].wn_low = 0L; wini[0].wn_size = (long)((long)param0.nr_cyl * (long)param0.nr_heads * (long)NR_SECTORS); for (i=5; i<10; i++) wini[i].wn_heads = param1.nr_heads; wini[5].wn_size = (long)((long)param1.nr_cyl * (long)param1.nr_heads * (long)NR_SECTORS); /* Initialize the controller */ if ((nr_drives > 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); copy_prt(i * 5); } } /*============================================================================* * 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 5 */ 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]; } /*============================================================================* * 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; } ® _w_need_reset: É_winchester_task ø † _winchester_task: ƒ ‚ ×ņ,#6 ® _2: ĀI001C Ā3 Ā1 ĀI001B ĀI001B † Ó_init_param • Ąį_w_mess € 16 € Ó_receive Š Š Ķ_w_messö jge I0016 Į_w_mess Ąį_1 € Ó_printk Š Š ­ ½ Ąå_w_mess ĄĻ,ģ Ąå_w_mess+6 ĄŠ,ģ Į_w_mess+2 ĢI0018 I001B: Ąį_w_mess € Ó_w_do_rdwt Š ” ĢI0019 I001C: ĄĘ,#-22 ĢI0019 I0018: Ąč#_2 ‰ Ģ.csa2 I0019: Ą_w_mess+2,#68 ĄåŠ Ą_w_mess+4,ģ § Ą_w_mess+6,ģ Ąį_w_mess € ¹ Ó_send Š Š ­ _w_do_rdwt: ƒ ‚ ×ņ,#14 ĄŲö ’ Ąä4Ē ĄŠ,ė ĶŠö jl I0022 ĶŠ,#10 jl I0023 I0022: Ąį-5 € ĢI0021 I0023: ’ Ķ8Ē,#1024 je I0027 Ąį-22 € ĢI0021 I0027: Ąį30 mul Š » Äā_wini ĄĘ,ģ Ąā5 ĄäŠ cwd išv ģ § Ą4Ē,ė § Ąę_nr_drives Ķ4Ē,ķ jl I002A Ąį-5 € ĢI0021 I002A: ’ Ąä2Ē § ° ’ Į12Ē Į10Ē ‡ € 024 € Ó.rmi4 ×ē#0 sbb ā0 Ł1f or åī 1: or åģ je I002D Ąį-22 € ĢI0021 I002D: ’ Į12Ē Į10Ē ‡ € Ąį512 € Ó.dvi4 Ą-12(ń),ė Ą-10(ń),ķ Ąä-12(ń) Ąå-10(ń) Äį2 adc ā0 ĄčĘ ×ä18(ļ) sbb å20(ļ) Ł1f Öäė je 1f Üģ 1: or åģ jle I00210 Ąį-104 € ĢI0021 I00210: § Ąä-12(ń) Ąę-10(ń) Ää14Ē adc ę16Ē Ą-12(ń),ė Ą-10(ń),ķ Ąć4 Ąā4 Ąä-12(ń) Į-10(ń) – § € Į12Ē  ž  – ž5 mul ģ Ėåģ ¦ € Ó.dvu4 § Ą6Ē,ė Ąć4 Ąā4 Ąä-12(ń) Į-10(ń) – € ‡ € “5 € Ó.rmu4 § Ą8Ē,ī Ąć4 Ąā4 Ąä-12(ń) Į-10(ń) – § € Į12Ē  ž  – ž5 mul ģ Ėåģ ¦ € Ó.rmu4 ¦ œ ‡ € “5 € Ó.dvu4 § Ą10Ē,ė ’ ĄčĘ Ąä8Ē Ą22(ļ),ė ’ ĄčĘ Ąä18Ē Ą24(ļ),ė ’ ĄčĘ Ąä6Ē Ą2(ļ),ė I00213: ĶŲ,#4 jg I00212 ÜŲ ĶŲ,#4 jl I00216 Ąį-5 € ĢI0021 I00216: Ķ_w_need_resetö je I00219 Ó_w_reset I00219: ” Ó_w_dma_setup Š ” Ó_w_transfer Š ¬ ĶĻö ŁI00213 I00212: ĶĻö ŁI0021F 024 € ĢI0021 I0021F: Ąį-5 € I0021:  … „  _w_dma_setup: ƒ ‚ ×ņ,#22 ’ ĶĒ,#3 ŁI0033 Ąį71 € ĢI0034 I0033: Ąį75 € I0034:  ž  – ” ’ Ąä24Ē Ą-1Ī,ė Į22Ē  ž  – Ą-1Ō,ė ’ Ąį86 mul 2Ē Äį688 » Į-1Ō Į-1Ī  € Äā_proc ¦ Ó_umap Äņ,#8 Ą-22(ń),ė Ą-20(ń),ī  ž Ąä-22(ń) – „  ž – ¬ Ąć8 Ąä-22(ń) Ąå-20(ń) © 2: sar ā1 rcr į1 Ø 1:  ž – „  ž – ĄŠ,ė Ąć16 Ąä-22(ń) Ąå-20(ń) © 2: sar ā1 rcr į1 Ø 1:  ž – „  ž – ĄŲ,ė Ąä-1Ō ×į1  ž –  ž – „  ž Ó.cuu Ą-10(ń),ė Ąä-1Ō ×į1 Ąć8 shr äcl  ž –  ž – „  ž – Ą-12(ń),ė Ąä-22(ń) Ąå-20(ń) ×į0 sbb ā0 Ł1f or åė 1: or åģ ŁI0036  ž Ąä-1Ī – € Ąį_3 € Ó_panic Š Š I0036: ‡ € Ąć4 Ąā4 Ąä-22(ń) Į-1Ō Į-20(ń) – ‰ Ćķ Ćī Äęė adc ēģ ×ć1 sbb ē#0 œ Įķ 6 € Ćķ  ‰ © 2: shr ā1 rcr į1 Ø 1: „ Öā0 Ą-1Å,ė ĄäŲ Ķ-1Å,ė je I0039 ĮŲ Ąį_4 € Ó_panic Š Š I0039: Ó_lock ” 2 € Ó_port_out Š Š ” 1 € Ó_port_out Š Š ¹ Ąį6 € Ó_port_out Š Š ĮŠ Ąį6 € Ó_port_out Š Š ĮŲ mov į130 € Ó_port_out Š Š Į-10(ń) Ąį7 € Ó_port_out Š Š Į-12(ń) Ąį7 € Ó_port_out Š Š Ó_unlock … „  _w_transfer: ƒ ‚ ’ ĶĒ,#3 ŁI0043 Ąį8 € ĢI0044 I0043: 0 € I0044:  ž  – Ą_command,ė ’ Ąć5 Ąä4Ē sal äcl or ä10Ē Ą_command+2,ė Į6Ē  ž  – Öį768  shr äcl ’ € Į8Ē ž  – ‰ or äģ  ž – Ą_command+4,ė ’ Į6Ē  ž  – „  ž – Ą_command+6,ė Ą_command+8,#2 Ą_command+10,#5 Ąį3 € Ó_com_out Š ™ je I0046 Ąį-1 € ĢI0041 I0046: Ąį3 € 0 € Ó_port_out Š Š Ąį_w_mess € Ąį-1 € call _receive Š Š ˆ Ó_win_results Š ™ ŁI0049 ‡ € ĢI0041 I0049: ’ Źal,26Ē Ž ‹ Öį63 Ķį24 ŁI004C Ó_read_ecc ĢI004D I004C: Ą_w_need_reset,#1 I004D: Ąį-1 € I0041:  … „  _win_results: ƒ ‚ € € ŪäĻ € Ąį800 € Ó_port_in Š Š ‡ € Ąį803 € Ó_port_out Š Š test Ļ,#2 ŁI0053 ‡ € ĢI0051 I0053: Ą_command,#3 ’ Ąć5 Ąä4Ē sal äcl Ą_command+2,ė ‡ € Ó_com_out Š ™ je I0056 Ąį-1 € ĢI0051 I0056: ¼ I005B: ĶĘ,#4 jge I0058  € Ó_hd_wait Š ™ je I005D Ąį-1 € ĢI0051 I005D: ŪäĻ € Ąį800 € Ó_port_in Š Š  ž £ – „  ž – mov åÅ ÄåĘ Ź26Ē,al ÜĘ ĢI005B I0058: ’ Źal,26Ē Ž ‹ testb al,#63 je I00510 Ąį-1 € ĢI0051 I00510: ‡ € I0051:  … „  _win_out: ƒ ‚ Ķ_w_need_resetö je I0063 … „  I0063:  € Ó_hd_wait Š ™ ŁI0066 ˆ Ąį800 € Ó_port_out Š Š I0066: … „  _w_reset: ƒ ‚ € € ĄĘ,#1 ” Ąį801 € Ó_port_out Š Š ĄĻö I0075: ĶĻ,#10000 jge I0072 ŪäĘ € Ąį801 € Ó_port_in Š Š  ž — – testb al,#1 ŁI0073 ĢI0072 I0073: ÜĻ ĢI0075 I0072: test Ę,#2 je I007A Ąį_5 € Ó_printk Š Ąį-1 € ĢI0071 I007A: Ą_w_need_resetö Ó_win_init € I0071:  … „  _win_init: ƒ ‚ push ė Ą_command,#12 Ą_command+2ö ‡ € Ó_com_out Š ™ je I0083 Ąį-1 € ĢI0081 I0083: Ó_lock Ąć8 Ąå_param0 sar åcl ¦ Ó_win_out Š  ž Ąä_param0 – „ € Ó_win_out Š Į_param0+2 Ó_win_out Š Ąć8 Ąå_param0+4 sar åcl ¦ Ó_win_out Š  ž Ąä_param0+4 – „ € Ó_win_out Š Ąć8 Ąå_param0+6 sar åcl ¦ Ó_win_out Š  ž Ąä_param0+6 – „ € Ó_win_out Š Į_param0+8 Ó_win_out Š Ó_unlock Ó_check_init ™ je I0086 Ą_w_need_reset,#1 Ąį-1 € ĢI0081 I0086: Ķ_nr_drives,#1 jle I0089 Ą_command+2,#32 ‡ € Ó_com_out Š ™ je I008C Ąį-1 € ĢI0081 I008C: Ó_lock Ąć8 Ąå_param1 sar åcl ¦ Ó_win_out Š  ž Ąä_param1 – „ € Ó_win_out Š Į_param1+2 Ó_win_out Š Ąć8 Ąå_param1+4 sar åcl ¦ Ó_win_out Š  ž Ąä_param1+4 – „ € Ó_win_out Š Ąć8 Ąå_param1+6 sar åcl ¦ Ó_win_out Š  ž Ąä_param1+6 – „ € Ó_win_out Š Į_param1+8 Ó_win_out Š Ó_unlock Ó_check_init ™ je I0089 Ą_w_need_reset,#1 Ąį-1 € ĢI0081 I0089: ¼ I00814: Ąå_nr_drives ĶĘ,ģ jge I00811 Ą_command,#1 Ąć5 — sal äcl Ą_command+2,ė Ą_command+10,#5 “ € Ó_com_out Š ™ je I00816 Ąį-1 € ĢI0081 I00816: Ąį_w_mess € Ąį-1 € Ó_receive Š Š Ó_win_results ™ je I00812 Ą_w_need_reset,#1 Ąį-1 € ĢI0081 I00812: ÜĘ ĢI00814 I00811: ‡ € I0081:  … „  _check_init: ƒ ‚ € “ € Ó_hd_wait Š ™ ŁI0093 ŪäĘ € Ąį800 € Ó_port_in Š Š test Ę,#2 je I0096 Ąį-1 € ĢI0091 I0096: ‡ € ĢI0091 I0093: … „  I0091:  … „  _read_ecc: ƒ ‚ € Ą_command,#13 ‡ € Ó_com_out Š ™ ŁI00A3  € Ó_hd_wait Š ™ ŁI00A3 ŪäĘ € Ąį800 € Ó_port_in Š Š  € Ó_hd_wait Š ™ ŁI00A3 ŪäĘ € Ąį800 € Ó_port_in Š Š test Ę,#1 je I00A3 Ą_w_need_reset,#1 I00A3: Ąį-1 … „  _hd_wait: ƒ ‚ ×ņ,#6 ¼ I00B4: ŪäĻ € Ąį801 € Ó_port_in Š Š ¢ ÖĻ,ė ” ÜĘ  Ķį10000 jge I00B2 ĶĻö je I00B4 I00B2: ĶĘ,#10000 jl I00B7 Ą_w_need_reset,#1 Ąį-1 € ĢI00B1 I00B7: ‡ € I00B1:  … „  _com_out: ƒ ‚ € € ¼ ˆ Ąį803 € Ó_port_out Š Š ˆ Ąį802 € Ó_port_out Š Š ¼ I00C5: ĶĘ,#300 jge I00C2 ŪäĻ € Ąį801 € Ó_port_in Š Š test Ļ,#8 je I00C3 ĢI00C2 I00C3: ÜĘ ĢI00C5 I00C2: ĶĘ,#300 ŁI00CA Ą_w_need_reset,#1 Ąį-1 € ĢI00C1 I00CA: Ó_lock ¼ I00CF: ĶĘ,#6 jge I00CC I00C13: ŪäĻ € Ąį801 € Ó_port_in Š Š test Ļ,#1 je I00C15 ĢI00C10 I00C15: test Ļ,#8 ŁI00C13 Ą_w_need_reset,#1 Ó_unlock Ąį-1 € ĢI00C1 I00C10:  ž £ – Öį14 Ķį12 je I00C1B Ą_w_need_reset,#1 Ó_unlock Ąį-1 € ĢI00C1 I00C1B: ĄäĘ sal į1 » Į_commandĒ Ąį800 € Ó_port_out Š Š ÜĘ ĢI00CF I00CC: Ó_unlock ‡ € I00C1:  … „  _init_params: ƒ ‚ ×ņ,#14 ŪäĘ € Ąį802 € Ó_port_in Š Š  — shr äcl Öį3 ž – ĄŲ,ė — Öį3  ž – Ą-10(ń),ė  ž Ąä_vec_table+260 – ĄŠ,ė  ž Ąä_vec_table+262 – ¬ ‡ Ąć4 ĄåĻ © 2: sal ā1 rcl į1 Ø 1: € ¦ Ąć4 Ąā4  – Ėåģ Ćķ ÄäŠ adc ęģ Įķ Ąć4 Ąā4 – Ą-1Å,ė Ć-12(ń) ‡ € Ąį64 € € Ąį_buf €  € Ąį_proc+172 € Ó_umap Äņ,#8 œ € Į-12(ń) Į-1Å Ó_phys_copy Äņ,#12 Ąķ,#4 ĄäŲ sal äcl » Ąį_param0 € Äā_buf ¦ Ó_copy_param Š Š Ąć4 Ąä-10(ń) sal äcl » Ąį_param1 € Äā_buf ¦ Ó_copy_param Š Š ‡ €  € € Ąį_buf €  € Ąį_proc+172 € Ó_umap Äņ,#8 œ € ‡ € 141 € Ó_phys_copy Äņ,#12  ž Źal,_buf Ž – Ą_nr_drives,ė ¼ I00D5: ĶĘ,#5 jae I00D2 Ąį30 mul Ę » Ąę_param0+2 Ą_wini+12Ē,ķ — Äį1 ” ĢI00D5 I00D2: Ą_wini+164ö Ą_wini+164+2ö Ąå_wini+164 Į_wini+164+2 Ą_wini+14,ģ Ć_wini+14+2 Ąä_param0+2 cwd œ € Ąä_param0 cwd Ąčė Ąéī ‰  Ó.mli4 Ąč#25 Ėéš » Ąäī Ó.mli4 Ą_wini+18,ė Ą_wini+18+2,ī ĄĘ,#5 I00D9: ĶĘ,#10 jae I00D6 mov į30 mul Ę » Ąę_param1+2 Ą_wini+12Ē,ķ — Äį1 ” ĢI00D9 I00D6: Ąä_param1+2 cwd œ € Ąä_param1 cwd Ąčė Ąéī ‰  Ó.mli4 Ąč#25 Ėéš » Ąäī Ó.mli4 Ą_wini+168,ė Ą_wini+168+2,ī Ķ_nr_drivesö jle I00DB Ó_win_init ™ je I00DB Ą_nr_drivesö I00DB: ¼ I00D11:  ž Ąä_nr_drives ” – ‰ Ķåė jae I00DE Ąį5 mul Ę  ž – Ą_w_mess+4,ė Ą_w_mess+10ö Ą_w_mess+10+2ö Ą_w_mess+8,#1024 Ą_w_mess+18,#_buf Ą_w_mess+6,#-6 Ą_w_mess+2,#3 Ąį_w_mess € Ó_w_do_rdwt Š Ķį1024 je I00D13 ” Ąį_6 € Ó_panic Š Š I00D13: Ąį5 mul Ę € Ó_copy_prt Š — Äį1 ” ĢI00D11 I00DE: … „  _copy_params: ƒ ‚ ’ « Æ mov Ē,ė ’ ‡ Źal,2Ē €  ž  – Æ Ą2Ē,ė ’ ĄčĪ Ąä3Ē Ą4(ļ),ė ’ ĄčĪ Ąä5Ē Ą6(ļ),ė ’ ‡ Źal,7Ē €  ž  – Æ Ą8Ē,ė … „  _copy_prt: ƒ ‚ ×ņ,#10 ¼ I00F5: ĶĘ,#4 jge I00F2 Ą-10(ń)ö ĄŲö ¢ ÄäĘ Ąā30 mul ģ Äį30 » Äā_wini ĄŠ,ģ  ž — – Ąć4 sal äcl Äį454  ž – ¬ ĄåĻ ĄčŠ Ąä_bufĒ Ąå_buf+2Ē Ą14(ļ),ė Ą16(ļ),ģ ĄåŠ Į16Ē Į14Ē ‡ € “ € Ó.rmi4 ×ē#0 sbb ā0 Ł1f or åī 1: or åģ je I00F7 ĄåŠ Ąä14Ē Į16Ē Ą-10(ń),ė ĆŲ Į16Ē Į14Ē ‡ € “ € Ó.dvi4 Äį1 adc ć0 Ąč#2 Ėéš » Ąäķ Ó.mli4 ĄåŠ Ą14Ē,ė Ą16Ē,ī ĄåŠ Ąä14Ē Ąę16Ē ×ä-10(ń) sbb ęŲ Ą-10(ń),ė ĄŲ,ķ I00F7: £ Äį4 » Ąä_bufĒ Ąę_buf+2Ē ×ä-10(ń) sbb ęŲ ĄåŠ Ą18Ē,ė Ą20Ē,ķ ÜĘ ĢI00F5 I00F2: ¢ Üė Ąā30 mul ģ » Äā_wini ¦ Ó_sort Š … „  É_sort _sort: ƒ ‚ € € ¼ I0105: ĶĘ,#4 jge I0102 ĄĻö I0109: ĶĻ,#3 jge I0103 Ąį30 mul Ļ » ÄåÅ Ąä14Ē Ąę16Ē ×į0 sbb ć0 Ł1f or ęė 1:ACD or ęķ ŁI010B £ Üė Ąā30 mul ģ » ÄåÅ Ąä14Ē Ąę16Ē ×į0 sbb ć0 Ł1f or ęė 1: or ęķ je I010B £ Üė Ąā30 mul ģ » ÄåÅ Ąį30 mul Ļ Ąčė ÄčÅ ¦ Įļ Ó_swap Š Š ĢI0107 I010B: Ąį30 mul Ļ » ÄåÅ £ Üė Ąć30 mul ķ Ąčė ÄčÅ Ąä14Ē Ąę16Ē ×ä14(ļ) sbb ę16(ļ) Ł1f Öäė je 1f Üķ 1: or ęķ jle I0107 £ Üė Ąā30 mul ģ » ÄåÅ Ąä14Ē Ąę16Ē ×į0 sbb ć0 Ł1f or ęė 1: or ęķ je I0107 £ Üė Ąā30 mul ģ » ÄåÅ Ąį30 mul Ļ Ąčė ÄčÅ ¦ Įļ Ó_swap Š Š I0107: ÜĻ ĢI0109 I0103: ÜĘ ĢI0105 I0102: … „  É_swap _swap: ƒ ‚ ×ņ,#30 ĄčÅ Ąć30 Ó.loi Ūé-30(ń) Ąć15 Ąčņ rep mov Ąņ,ļ ĄčĪ Ąć30 Ó.loi ĄéÅ Ąć15 Ąčņ rep mov Ąņ,ļ Ūč-30(ń) Ąć30 Ó.loi ĄéĪ Ąć15 Ąčņ rep mov Ąņ,ļ … „  ³ _param1: .zerow 10/2 _param0: .zerow 10/2 _buf: .zerow 1024/2 _command: .zerow 12/2 _w_mess: .zerow 24/2 _nr_drives: .zerow 2/2 _wini: .zerow 300/2 ® _1: Ā26999 Ā25454 Ā25960 Ā29811 Ā29285 Ā29728 Ā29537 Ā8299 Ā28519 Ā8308 Ā25965 Ā29555 Ā26465 Ā8293 Ā29286 Ā28015 Ā9504 Ā8292 ø _3: Ā21318 Ā26400 Ā30305 Ā8293 Ā26999 Ā25454 Ā25960 Ā29811 Ā29285 Ā25632 Ā29545 Ā8299 Ā29284 Ā30313 Ā29285 Ā25120 Ā25697 Ā24864 æ00 Ā114 _4: Ā29268 Ā27001 Ā26478 Ā29728 Ā8303 Ā19780 Ā8257 Ā25441 Ā28530 Ā29555 Ā13856 Ā19252 Ā25120 Ā30063 æ10 Ā29281 Ā121 _5: Ā24904 æ14 Ā25632 Ā29545 Ā8299 Ā28535 Ā10094 Ā8308 Ā25970 Ā25971 Ā2676 ø _6: Ā24899 Ā10094 Ā8308 Ā25970 .word 25697 Ā28704 Ā29281 Ā26996 Ā26996 Ā28271 Ā29728 Ā25185 Ā25964 Ā28448 Ā8294 Ā26999 Ā25454 Ā25960 Ā29811 Ā29285 Ā32 † ööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööE2’