void write_pic1(ioport_t port, Bit8u value) { /* if port == 0 this must be either an ICW1, OCW2, or OCW3 */ /* if port == 1 this must be either ICW2, ICW3, ICW4, or load IMR */ static char /* icw_state, */ /* !=0 => port 1 does icw 2,3,(4) */ icw_max_state; /* number of icws expected */ int ilevel; /* level to reset on outb 0x20 */ port -= 0xa0; ilevel = 32; if (pic_isr) ilevel=find_bit(pic_isr); if (ilevel != 32 && !test_bit(ilevel, &pic_irqall)) { /* this is a fake IRQ, don't allow to reset its ISR bit */ pic_print(1, "Protecting ISR bit for lvl ", ilevel, " from spurious EOI"); ilevel = 32; } if (in_dpmi_pm()) dpmi_return_request(); /* we have to leave the signal context */ if(!port){ /* icw1, ocw2, ocw3 */ if(value&0x10){ /* icw1 */ icw_max_state = (value & 1) + 1; if(value&2) ++icw_max_state; pic1_icw_state = 1; pic1_cmd=1; } else if (value&0x08) { /* ocw3 */ if(value&2) pic1_isr_requested = value&1; if(value&64)pic_smm = value&32; /* must be either 0 or 32, conveniently */ pic1_cmd=3; } else if((value&0xb8) == 0x20) { /* ocw2 */ /* irqs on pic1 require an outb20 to each pic. we settle for any 2 */ if(!clear_bit(ilevel,&pic1_isr)) { clear_bit(ilevel,&pic_isr); /* the famous outb20 */ pic_print(1,"EOI resetting bit ",ilevel, " on pic0"); } else pic_print(1,"EOI resetting bit ",ilevel, " on pic1"); pic0_cmd=2; } } else /* icw2, icw3, icw4, or mask register */ switch(pic1_icw_state){ case 0: /* mask register */ set_pic1_imr(value); pic_print(1, "Set mask to ", value, " on pic1"); break; case 1: /* icw 2 */ set_pic1_base(value); default: /* icw 2,3 and 4 */ if(pic1_icw_state++ >= icw_max_state) pic1_icw_state=0; } }
void pic_sched(int ilevel, int interval) { char mesg[35]; /* default for interval is 65536 (=54.9ms) * There's a problem with too small time intervals - an interrupt can * be continuously scheduled, without letting any time to process other * code. * * BIG WARNING - in non-periodic timer modes pit[0].cntr goes to -1 * at the end of the interval - was this the reason for the following * [1u-15sec] range check? */ if(interval > 0 && interval < 0x3fffffff) { if(pic_ltime[ilevel]==NEVER) { pic_itime[ilevel] = pic_itime[32] + interval; } else { pic_itime[ilevel] = pic_itime[ilevel] + interval; } } if (debug_level('r') > 2) { /* avoid going through sprintf for non-debugging */ sprintf(mesg,", delay= %d.",interval); pic_print(2,"Scheduling lvl= ",ilevel,mesg); pic_print2(2,"pic_itime set to ",pic_itime[ilevel],""); } }
/* DANG_BEGIN_FUNCTION pic_activate * * pic_activate requests any interrupts whose scheduled time has arrived. * anything after pic_dos_time and before pic_sys_time is activated. * pic_dos_time is advanced to the earliest time scheduled. * DANG_END_FUNCTION */ static void pic_activate(void) { hitimer_t earliest; int timer, count; unsigned pic_newirr = pic_pirr & ~(pic_irr | pic_isr); pic_irr |= pic_newirr; pic_pirr &= ~pic_newirr; /*if(pic_irr&~pic_imr) return;*/ earliest = pic_sys_time; count = 0; for (timer=0; timer<32; ++timer) { if ((pic_itime[timer] != NEVER) && (pic_itime[timer] < pic_sys_time)) { if (pic_itime[timer] != pic_ltime[timer]) { if ((earliest == NEVER) || (pic_itime[timer] < earliest)) earliest = pic_itime[timer]; pic_request(timer); ++count; } } } if(count) pic_print(2,"Activated ",count, " interrupts."); pic_print2(2,"Activate ++ dos time to ",earliest, " "); pic_print2(2,"pic_sys_time is ",pic_sys_time," "); /*if(!pic_icount)*/ pic_dos_time = pic_itime[32] = earliest; }
void pic_untrigger(int inum) { if (!((pic_irr | pic_pirr) & (1<<inum))) return; /* Note: untriggering works similar in both edge and level modes. * In fact, these modes are very similar in that IRR bits mirror * the IRQ lines as-is. The only difference is that in edge mode * the IRR bit is reset by INTA and remains at 0 until the first * low->high transition is detected. After which, it again mirrors * the IRQ pin directly. In level mode it always mirrors the input. * This means that with "level-delivering" devices there is no * difference AT ALL with what triggering mode is used. Their * inactive state is low, so on the first transition to high, IRR * bit starts following the input in any trigger mode. The difference * is there only with the "edge-delivering" devices: their inactive * state is high and the IRQ delivery start from the short pulse to * low and back. Before that starting pulse, IRR bit remains low even * though the IRQ line is high. There are probably not much of the * "edge-delivering" devices though: PIT in mode 2 is the only * one known to me. * In short: we should allow untriggering the interrupt regardless * of the trigger mode. * * Note: spurious IRQ should be generated in any trigger mode if * all IRQ lines went low before INTA. This is because even in edge * mode the IRR bit gets cleared when IRQ line goes down, so at * INTA time there will be nothing to read from IRR. */ pic_pirr &= ~(1<<inum); pic_irr &= ~(1<<inum); pic_print(2,"Requested irq lvl ", inum, " untriggered"); }
void run_irqs(void) /* find the highest priority unmasked requested irq and run it */ { int local_pic_ilevel, ret; /* don't allow HW interrupts in force trace mode */ pic_activate(); if (!isset_IF()) { if (pic_pending()) set_VIP(); return; /* exit if ints are disabled */ } clear_VIP(); /* check for and find any requested irqs. Having found one, we atomic-ly * clear it and verify it was there when we cleared it. If it wasn't, we * look for the next request. There are two in_service bits for pic1 irqs. * This is needed, because irq 8-15 must do 2 outb20s, which, if the dos * irq code actually runs, will reset the bits. We also reset them here, * since dos code won't necessarily run. */ while((local_pic_ilevel = pic_get_ilevel()) != -1) { /* while something to do*/ pic_print(1, "Running irq lvl ", local_pic_ilevel, ""); clear_bit(local_pic_ilevel, &pic_irr); /* pic_isr bit is set in do_irq() */ ret = (pic_iinfo[local_pic_ilevel].func ? pic_iinfo[local_pic_ilevel].func(local_pic_ilevel) : 1); /* run the function */ if (ret) { do_irq(local_pic_ilevel); } } }
int main(void) { pic_insert("1.jpg"); pic_insert("2.jpg"); pic_insert("3.jpg"); pic_insert("4.jpg"); pic_print(); pic_destroy(); return 0; }
/* DANG_BEGIN_FUNCTION pic_request * * pic_request triggers an interrupt. There is presently no way to * "un-trigger" an interrupt. The interrupt will be initiated the * next time pic_run is called, unless masked or superceded by a * higher priority interrupt. pic_request takes one argument, an * interrupt level, which specifies the interrupt to be triggered. * If that interrupt is already active, the request will be queued * until all active interrupts have been completed. The queue is * only one request deep for each interrupt, so it is the responsibility * of the interrupt code to retrigger itself if more interrupts are * needed. * * DANG_END_FUNCTION */ int pic_request(int inum) { static char buf[81]; int ret=PIC_REQ_NOP; if ((pic_irr|pic_isr)&(1<<inum)) { if (pic_pirr&(1<<inum)){ ret=PIC_REQ_LOST; pic_print(2,"Requested irq lvl ", inum, " lost "); } else { ret=PIC_REQ_PEND; pic_print(2,"Requested irq lvl ", inum, " pending "); } pic_pirr|=(1<<inum); if(pic_itime[inum] == pic_ltime[inum]) { pic_print(2,"pic_itime and pic_ltime for timer ",inum," matched!"); pic_itime[inum] = pic_itime[32]; } pic_ltime[inum] = pic_itime[inum]; } else { pic_print(2,"Requested irq lvl ", inum, " successfully"); pic_irr|=(1<<inum); if(pic_itime[inum] == pic_ltime[inum]) pic_itime[inum] = pic_itime[32]; pic_ltime[inum] = pic_itime[inum]; ret=PIC_REQ_OK; } if (debug_level('r') >2) { /* avoid going through sprintf for non-debugging */ sprintf(buf,", k%d",(int)pic_dpmi_count); pic_print(2,"Zeroing vm86, DPMI from ",pic_vm86_count,buf); } pic_vm86_count=pic_dpmi_count=0; return ret; }
/* DANG_BEGIN_FUNCTION write_pic0,write_pic1 * * write_pic_0() and write_pic1() implement dos writes to the pic ports. * They are called by the code that emulates inb and outb instructions. * Each function implements both ports for the pic: pic0 is on ports * 0x20 and 0x21; pic1 is on ports 0xa0 and 0xa1. These functions take * two arguments: a port number (0 or 1) and a value to be written. * * DANG_END_FUNCTION */ void write_pic0(ioport_t port, Bit8u value) { /* if port == 0 this must be either an ICW1, OCW2, or OCW3 * if port == 1 this must be either ICW2, ICW3, ICW4, or load IMR */ #if 0 static char icw_state, /* !=0 => port 1 does icw 2,3,(4) */ #endif static char icw_max_state; /* number of icws expected */ int ilevel; /* level to reset on outb 0x20 */ port -= 0x20; ilevel = 32; if (pic_isr) ilevel=find_bit(pic_isr); if (ilevel != 32 && !test_bit(ilevel, &pic_irqall)) { /* this is a fake IRQ, don't allow to reset its ISR bit */ pic_print(1, "Protecting ISR bit for lvl ", ilevel, " from spurious EOI"); ilevel = 32; } if (in_dpmi_pm()) dpmi_return_request(); /* we have to leave the signal context */ if(!port){ /* icw1, ocw2, ocw3 */ if(value&0x10){ /* icw1 */ icw_max_state = (value & 1) + 1; if(value&2) ++icw_max_state; pic0_icw_state = 1; pic0_cmd=1; } else if (value&0x08){ /* ocw3 */ if(value&2) pic0_isr_requested = value&1; if(value&64)pic_smm = value&32; /* must be either 0 or 32, conveniently */ pic0_cmd=3; } else if((value&0xb8) == 0x20) { /* ocw2 */ /* irqs on pic1 require an outb20 to each pic. we settle for any 2 */ if(!clear_bit(ilevel,&pic1_isr)) { clear_bit(ilevel,&pic_isr); /* the famous outb20 */ pic_print(1,"EOI resetting bit ",ilevel, " on pic0"); #if 1 /* XXX hack: to avoid timer interrupt re-entrancy, * we try to disable interrupts in a hope IRET will re-enable * them. This fixes Tetris Classic problem: * https://github.com/stsp/dosemu2/issues/99 * Need to check also IMR because DPMI uses another hack * that masks the IRQs. */ if (ilevel == PIC_IRQ0 && isset_IF() && !(pic_imr & (1 << ilevel))) { r_printf("PIC: disabling interrupts to avoid reentrancy\n"); clear_IF_timed(); } #endif } else pic_print(1,"EOI resetting bit ",ilevel, " on pic1"); pic0_cmd=2; } } else /* icw2, icw3, icw4, or mask register */ switch(pic0_icw_state){ case 0: /* mask register */ set_pic0_imr(value); pic_print(1, "Set mask to ", value, " on pic0"); break; case 1: /* icw2 */ set_pic0_base(value); default: /* icw2, 3, and 4*/ if(pic0_icw_state++ >= icw_max_state) pic0_icw_state=0; } }