DOSBox-X
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Defines
src/hardware/pic.cpp
00001 /*
00002  *  Copyright (C) 2002-2019  The DOSBox Team
00003  *
00004  *  This program is free software; you can redistribute it and/or modify
00005  *  it under the terms of the GNU General Public License as published by
00006  *  the Free Software Foundation; either version 2 of the License, or
00007  *  (at your option) any later version.
00008  *
00009  *  This program is distributed in the hope that it will be useful,
00010  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
00011  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00012  *  GNU General Public License for more details.
00013  *
00014  *  You should have received a copy of the GNU General Public License
00015  *  along with this program; if not, write to the Free Software
00016  *  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1335, USA.
00017  */
00018 
00019 #include "dosbox.h"
00020 #include "inout.h"
00021 #include "cpu.h"
00022 #include "callback.h"
00023 #include "pic.h"
00024 #include "timer.h"
00025 #include "setup.h"
00026 #include "control.h"
00027 
00028 #if defined(_MSC_VER)
00029 # pragma warning(disable:4244) /* const fmath::local::uint64_t to double possible loss of data */
00030 #endif
00031 
00032 #define PIC_QUEUESIZE 512
00033 
00034 unsigned long PIC_irq_delay_ns = 0;
00035 
00036 bool never_mark_cascade_in_service = false;
00037 bool ignore_cascade_in_service = false;
00038 
00039 struct PIC_Controller {
00040     Bitu icw_words;
00041     Bitu icw_index;
00042     bool special;
00043     bool auto_eoi;
00044     bool rotate_on_auto_eoi;
00045     bool single;
00046     bool request_issr;
00047     Bit8u vector_base;
00048 
00049     Bit8u irr;        // request register
00050     Bit8u imr;        // mask register
00051     Bit8u imrr;       // mask register reversed (makes bit tests simpler)
00052     Bit8u isr;        // in service register
00053     Bit8u isrr;       // in service register reversed (makes bit tests simpler)
00054     Bit8u isr_ignore; // in service bits to ignore
00055     Bit8u active_irq; //currently active irq
00056 
00057 
00058     void set_imr(Bit8u val);
00059 
00060     void check_after_EOI(){
00061         //Update the active_irq as an EOI is likely to change that.
00062         update_active_irq();
00063         if((irr&imrr)&isrr) check_for_irq();
00064     }
00065 
00066     void update_active_irq() {
00067         if (auto_eoi) {
00068             assert(isr == 0);
00069         }
00070 
00071         if(isr == 0) {active_irq = 8; return;}
00072         for(Bit8u i = 0, s = 1; i < 8;i++, s<<=1){
00073             if (isr & s) {
00074                 active_irq = i;
00075                 return;
00076             }
00077         }
00078     }
00079 
00080     void check_for_irq();
00081 
00082     //Signals master/cpu that there is an irq ready.
00083     void activate();
00084 
00085     //Removes signal to master/cpu that there is an irq ready.
00086     void deactivate();
00087 
00088     void raise_irq(Bit8u val);
00089 
00090     void lower_irq(Bit8u val){
00091         Bit8u bit = 1 << ( val);
00092         if(irr & bit) { //value will change (as it is currently active)
00093             irr&=~bit;
00094             if((bit&imrr)&isrr) { //not masked and not in service
00095                 //This irq might have toggled PIC_IRQCheck/caused irq 2 on master, when it was raised.
00096                 //If it is active, then recheck it, we can't just deactivate as there might be more IRQS raised.
00097                 if(special || val < active_irq) check_for_irq();
00098             }
00099         }
00100     }
00101 
00102     //handles all bits and logic related to starting this IRQ, it does NOT start the interrupt on the CPU.
00103     void start_irq(Bit8u val);
00104 };
00105 
00106 int master_cascade_irq = -1;
00107 static PIC_Controller pics[2];
00108 static PIC_Controller& master = pics[0];
00109 static PIC_Controller& slave  = pics[1];
00110 Bitu PIC_Ticks = 0;
00111 Bitu PIC_IRQCheck = 0; //Maybe make it a bool and/or ensure 32bit size (x86 dynamic core seems to assume 32 bit variable size)
00112 Bitu PIC_IRQCheckPending = 0; //Maybe make it a bool and/or ensure 32bit size (x86 dynamic core seems to assume 32 bit variable size)
00113 bool enable_slave_pic = true; /* if set, emulate slave with cascade to master. if clear, emulate only master, and no cascade (IRQ 2 is open) */
00114 bool enable_pc_xt_nmi_mask = false;
00115 
00116 void PIC_Controller::check_for_irq(){
00117     const Bit8u possible_irq = (irr&imrr)&isrr;
00118     if (possible_irq) {
00119         Bit8u a_irq = special?8:active_irq;
00120 
00121         if (ignore_cascade_in_service && this == &master && a_irq == (unsigned char)master_cascade_irq)
00122             a_irq++;
00123 
00124         for(Bit8u i = 0, s = 1; i < a_irq;i++, s<<=1){
00125             if ( possible_irq & s ) {
00126                 //There is an irq ready to be served => signal master and/or cpu
00127                 activate();
00128                 return;
00129             }
00130         }
00131     }
00132     deactivate(); //No irq, remove signal to master and/or cpu
00133 }
00134 
00135 void PIC_Controller::raise_irq(Bit8u val){
00136     Bit8u bit = 1 << (val);
00137     if((irr & bit)==0) { //value changed (as it is currently not active)
00138         irr|=bit;
00139         if((bit&imrr)&isrr) { //not masked and not in service
00140             if(special || val < active_irq) activate();
00141             else if (ignore_cascade_in_service && this == &master && val == (unsigned char)master_cascade_irq) activate();
00142         }
00143     }
00144 }
00145 
00146 void PIC_IRQCheckDelayed(Bitu val) {
00147     (void)val;//UNUSED
00148     PIC_IRQCheck = 1;
00149     PIC_IRQCheckPending = 0;
00150 }
00151 
00152 void PIC_Controller::set_imr(Bit8u val) {
00153     Bit8u change = (imr) ^ (val); //Bits that have changed become 1.
00154     imr  =  val;
00155     imrr = ~val;
00156 
00157     //Test if changed bits are set in irr and are not being served at the moment
00158     //Those bits have impact on whether the cpu emulation should be paused or not.
00159     if((irr & change)&isrr) check_for_irq();
00160 }
00161 
00162 void PIC_Controller::activate() { 
00163     //Stops CPU if master, signals master if slave
00164     if(this == &master) {
00165         //cycles 0, take care of the port IO stuff added in raise_irq base caller.
00166         if (!PIC_IRQCheckPending) {
00167             /* NTS: PIC_AddEvent by design caps CPU_Cycles to make the event happen on time */
00168             PIC_IRQCheckPending = 1;
00169             PIC_AddEvent(PIC_IRQCheckDelayed,(double)PIC_irq_delay_ns / 1000000,0);
00170         }
00171     } else {
00172         master.raise_irq(master_cascade_irq);
00173     }
00174 }
00175 
00176 void PIC_Controller::deactivate() { 
00177     //removes irq check value if master, signals master if slave
00178     if(this == &master) {
00179         /* NTS: DOSBox code used to set PIC_IRQCheck = 0 here.
00180          *
00181          *      That's actually not the way to handle it. Here's why:
00182          *
00183          *      What would happen if one device raised an IRQ (setting PIC_IRQCheck=1)
00184          *      then before dispatching IRQs, another device lowered an IRQ (setting PIC_IRQCheck=0).
00185          *
00186          *      Lowering the IRQ would reset the flag, and PIC_runIRQs() would never process
00187          *      any pending IRQs even though some are waiting.
00188          *
00189          *      It's better if we set the flag when raising an IRQ, and leave the flag set until
00190          *      PIC_runIRQs() determines there are no more IRQs to dispatch. Then and only then
00191          *      will PIC_runIRQs() clear the flag. */
00192     } else {
00193         /* just because ONE IRQ on the slave finished doesn't mean there aren't any others needing service! */
00194         if ((irr&imrr) == 0)
00195             master.lower_irq(master_cascade_irq);
00196         else
00197             LOG_MSG("Slave PIC: still to handle irr=%02x imrr=%02x isrr=%02x",irr,imrr,isrr);
00198     }
00199 }
00200 
00201 void PIC_Controller::start_irq(Bit8u val){
00202     irr&=~(1<<(val));
00203     if (!auto_eoi) {
00204         if (never_mark_cascade_in_service && this == &master && val == master_cascade_irq) {
00205             /* do nothing */
00206         }
00207         else {
00208             active_irq = val;
00209             isr |= 1<<(val);
00210             isrr = (~isr) | isr_ignore;
00211         }
00212     } else if (GCC_UNLIKELY(rotate_on_auto_eoi)) {
00213         LOG_MSG("rotate on auto EOI not handled");
00214     }
00215 }
00216 
00217 struct PICEntry {
00218     pic_tickindex_t index;
00219     Bitu value;
00220     PIC_EventHandler pic_event;
00221     PICEntry * next;
00222 };
00223 
00224 static struct {
00225     PICEntry entries[PIC_QUEUESIZE];
00226     PICEntry * free_entry;
00227     PICEntry * next_entry;
00228 } pic_queue;
00229 
00230 static void write_command(Bitu port,Bitu val,Bitu iolen) {
00231     (void)iolen;//UNUSED
00232     PIC_Controller * pic=&pics[(port==0x20/*IBM*/ || port==0x00/*PC-98*/) ? 0 : 1];
00233 
00234     if (GCC_UNLIKELY(val&0x10)) {       // ICW1 issued
00235         if (val&0x04) LOG_MSG("PIC: 4 byte interval not handled");
00236         if (val&0x08) LOG_MSG("PIC: level triggered mode not handled");
00237         if (val&0xe0) LOG_MSG("PIC: 8080/8085 mode not handled");
00238         pic->single=(val&0x02)==0x02;
00239         pic->icw_index=1;           // next is ICW2
00240         pic->icw_words=2 + (val&0x01);  // =3 if ICW4 needed
00241     } else if (GCC_UNLIKELY(val&0x08)) {    // OCW3 issued
00242         if (val&0x04) LOG_MSG("PIC: poll command not handled");
00243         if (val&0x02) {     // function select
00244             if (val&0x01) pic->request_issr=true;   /* select read interrupt in-service register */
00245             else pic->request_issr=false;           /* select read interrupt request register */
00246         }
00247         if (val&0x40) {     // special mask select
00248             if (val&0x20) pic->special = true;
00249             else pic->special = false;
00250             //Check if there are irqs ready to run, as the priority system has possibly been changed.
00251             pic->check_for_irq();
00252             LOG(LOG_PIC,LOG_NORMAL)("port %X : special mask %s",(int)port,(pic->special)?"ON":"OFF");
00253         }
00254     } else {    // OCW2 issued
00255         if (val&0x20) {     // EOI commands
00256             if (GCC_UNLIKELY(val&0x80)) LOG_MSG("rotate mode not supported");
00257             if (val&0x40) {     // specific EOI
00258                 pic->isr &= ~(1<< (val-0x60));
00259                 pic->isrr = (~pic->isr) | pic->isr_ignore;
00260                 pic->check_after_EOI();
00261 //              if (val&0x80);  // perform rotation
00262             } else {        // nonspecific EOI
00263                 if (pic->active_irq != 8) { 
00264                     //If there is no irq in service, ignore the call, some games send an eoi to both pics when a sound irq happens (regardless of the irq).
00265                     pic->isr &= ~(1 << (pic->active_irq));
00266                     pic->isrr = (~pic->isr) | pic->isr_ignore;
00267                     pic->check_after_EOI();
00268                 }
00269 //              if (val&0x80);  // perform rotation
00270             }
00271         } else {
00272             if ((val&0x40)==0) {        // rotate in auto EOI mode
00273                 if (val&0x80) pic->rotate_on_auto_eoi=true;
00274                 else pic->rotate_on_auto_eoi=false;
00275             } else if (val&0x80) {
00276                 LOG(LOG_PIC,LOG_NORMAL)("set priority command not handled");
00277             }   // else NOP command
00278         }
00279     }   // end OCW2
00280 }
00281 
00282 static void write_data(Bitu port,Bitu val,Bitu iolen) {
00283     (void)iolen;//UNUSED
00284     PIC_Controller * pic=&pics[(port==0x21/*IBM*/ || port==0x02/*PC-98*/) ? 0 : 1];
00285 
00286     switch(pic->icw_index) {
00287     case 0:                        /* mask register */
00288         pic->set_imr(val);
00289         break;
00290     case 1:                        /* icw2          */
00291         LOG(LOG_PIC,LOG_NORMAL)("%d:Base vector %X",port==0x21 ? 0 : 1,(int)val);
00292         pic->vector_base = val&0xf8;
00293         if(pic->icw_index++ >= pic->icw_words) pic->icw_index=0;
00294         else if(pic->single) pic->icw_index=3;      /* skip ICW3 in single mode */
00295         break;
00296     case 2:                         /* icw 3 */
00297         LOG(LOG_PIC,LOG_NORMAL)("%d:ICW 3 %X",port==0x21 ? 0 : 1,(int)val);
00298         if(pic->icw_index++ >= pic->icw_words) pic->icw_index=0;
00299         break;
00300     case 3:                         /* icw 4 */
00301         /*
00302             0       1 8086/8080  0 mcs-8085 mode
00303             1       1 Auto EOI   0 Normal EOI
00304             2-3    0x Non buffer Mode 
00305                    10 Buffer Mode Slave 
00306                    11 Buffer mode Master    
00307             4       Special/Not Special nested mode 
00308         */
00309         pic->auto_eoi=(val & 0x2)>0;
00310         
00311         LOG(LOG_PIC,LOG_NORMAL)("%d:ICW 4 %X",port==0x21 ? 0 : 1,(int)val);
00312 
00313         if ((val&0x01)==0) LOG_MSG("PIC:ICW4: %x, 8085 mode not handled",(int)val);
00314         if ((val&0x10)!=0) LOG_MSG("PIC:ICW4: %x, special fully-nested mode not handled",(int)val);
00315 
00316         if(pic->icw_index++ >= pic->icw_words) pic->icw_index=0;
00317         break;
00318     default:
00319         LOG(LOG_PIC,LOG_NORMAL)("ICW HUH? %X",(int)val);
00320         break;
00321     }
00322 }
00323 
00324 
00325 static Bitu read_command(Bitu port,Bitu iolen) {
00326     (void)iolen;//UNUSED
00327     PIC_Controller * pic=&pics[(port==0x20/*IBM*/ || port==0x00/*PC-98*/) ? 0 : 1];
00328     if (pic->request_issr){
00329         return pic->isr;
00330     } else {
00331         /* HACK: I found a PC-98 game "Steel Gun Nyan" that relies on setting the timer to Mode 3 (Square Wave)
00332          *       then polling the output through the master PIC's IRR to do delays. */
00333         if (pic == &master) {
00334             void TIMER_IRQ0Poll(void);
00335             TIMER_IRQ0Poll();
00336         }
00337 
00338         return pic->irr;
00339     }
00340 }
00341 
00342 
00343 static Bitu read_data(Bitu port,Bitu iolen) {
00344     (void)iolen;//UNUSED
00345     PIC_Controller * pic=&pics[(port==0x21/*IBM*/ || port==0x02/*PC-98*/) ? 0 : 1];
00346     return pic->imr;
00347 }
00348 
00349 /* PC/XT NMI mask register 0xA0. Documentation on the other bits
00350  * is sparse and spread across the internet, but many seem to
00351  * agree that bit 7 is used to enable/disable the NMI (1=enable,
00352  * 0=disable)
00353  *
00354  * Confirmed: IBM PCjr technical reference, BIOS source code.
00355  *            Some part of the code writes 0x80 to this port,
00356  *            then does some work, then writes 0x00.
00357  *
00358  * IBM PCjr definitions:
00359  *   bit[7]: Enable NMI
00360  *   bit[6]: IR test enable
00361  *   bit[5]: Select clock 1 input
00362  *   bit[4]: Disable HRQ */
00363 static void pc_xt_nmi_write(Bitu port,Bitu val,Bitu iolen) {
00364     (void)iolen;//UNUSED
00365     (void)port;//UNUSED
00366     CPU_NMI_gate = (val & 0x80) ? true : false;
00367     CPU_Check_NMI();
00368 }
00369 
00370 /* FIXME: This should be called something else that's true to the ISA bus, like PIC_PulseIRQ, not Activate IRQ.
00371  *        ISA interrupts are edge triggered, not level triggered. */
00372 void PIC_ActivateIRQ(Bitu irq) {
00373     /* Remember what was once IRQ 2 on PC/XT is IRQ 9 on PC/AT */
00374     if (IS_PC98_ARCH) {
00375         if (irq == 7) {
00376             LOG(LOG_PIC,LOG_ERROR)("Attempted to raise IRQ %u, which is cascade IRQ",(int)irq);
00377             return; /* don't raise cascade IRQ */
00378         }
00379     }
00380     else if (enable_slave_pic) { /* PC/AT emulation with slave PIC cascade to master */
00381         if (irq == 2) irq = 9;
00382     }
00383     else { /* PC/XT emulation with only master PIC */
00384         if (irq == 9) irq = 2;
00385         if (irq >= 8) {
00386             LOG(LOG_PIC,LOG_ERROR)("Attempted to raise IRQ %u when slave PIC does not exist",(int)irq);
00387             return;
00388         }
00389     }
00390 
00391     Bitu t = irq>7 ? (irq - 8): irq;
00392     PIC_Controller * pic=&pics[irq>7 ? 1 : 0];
00393 
00394     pic->raise_irq(t);
00395 }
00396 
00397 void PIC_DeActivateIRQ(Bitu irq) {
00398     /* Remember what was once IRQ 2 on PC/XT is IRQ 9 on PC/AT */
00399     if (IS_PC98_ARCH) {
00400         if (irq == 7) return;
00401     }
00402     else if (enable_slave_pic) { /* PC/AT emulation with slave PIC cascade to master */
00403         if (irq == 2) irq = 9;
00404     }
00405     else { /* PC/XT emulation with only master PIC */
00406         if (irq == 9) irq = 2;
00407         if (irq >= 8) {
00408             LOG(LOG_PIC,LOG_ERROR)("Attempted to lower IRQ %u when slave PIC does not exist",(int)irq);
00409             return;
00410         }
00411     }
00412 
00413     Bitu t = irq>7 ? (irq - 8): irq;
00414     PIC_Controller * pic=&pics[irq>7 ? 1 : 0];
00415     pic->lower_irq(t);
00416 }
00417 
00418 unsigned int PIC_IRQ_hax[16] = { PIC_irq_hack_none };
00419 
00420 void PIC_Set_IRQ_hack(int IRQ,unsigned int hack) {
00421     if (IRQ < 0 || IRQ >= 16) return;
00422     PIC_IRQ_hax[IRQ] = hack;
00423 }
00424 
00425 unsigned int PIC_parse_IRQ_hack_string(const char *str) {
00426     unsigned int res = PIC_irq_hack_none;
00427     std::string what;
00428 
00429     while (*str != 0) {
00430         while (*str == ' ') str++;
00431         if (*str == 0) break;
00432 
00433         what.clear();
00434         while (*str != 0 && *str != ' ')
00435             what += *str++;
00436 
00437         while (*str == ' ') str++;
00438 
00439         LOG_MSG("IRQ HACK: '%s'",what.c_str());
00440 
00441         if (what == "none")
00442             res  = PIC_irq_hack_none;
00443         else if (what == "cs_equ_ds")
00444             res |= PIC_irq_hack_cs_equ_ds;
00445     }
00446 
00447     return res;
00448 }
00449 
00450 static bool IRQ_hack_check_cs_equ_ds(const int IRQ) {
00451     (void)IRQ;
00452 
00453     uint16_t s_cs = SegValue(cs);
00454     uint16_t s_ds = SegValue(ds);
00455 
00456     if (s_cs >= 0xA000)
00457         return true; // don't complain about the BIOS ISR
00458 
00459     if (s_cs != s_ds) {
00460 #if 0
00461         LOG(LOG_PIC,LOG_DEBUG)("Not dispatching IRQ %d according to IRQ hack. CS != DS",IRQ);
00462 #endif
00463         return false;
00464     }
00465 
00466     return true;
00467 }
00468 
00469 static void slave_startIRQ(){
00470     Bit8u pic1_irq = 8;
00471     bool skipped_irq = false;
00472     const Bit8u p = (slave.irr & slave.imrr)&slave.isrr;
00473     const Bit8u max = slave.special?8:slave.active_irq;
00474     for(Bit8u i = 0,s = 1;i < max;i++, s<<=1) {
00475         if (p&s) {
00476             if (PIC_IRQ_hax[i+8] & PIC_irq_hack_cs_equ_ds) {
00477                 if (!IRQ_hack_check_cs_equ_ds(i+8)) {
00478                     skipped_irq = true;
00479                     continue; // skip IRQ
00480                 }
00481             }
00482 
00483             pic1_irq = i;
00484             break;
00485         }
00486     }
00487 
00488     if (GCC_UNLIKELY(pic1_irq == 8)) {
00489         if (!skipped_irq) {
00490             /* we have an IRQ routing problem. this code is supposed to emulate the fact that
00491              * what was once IRQ 2 on PC/XT is routed to IRQ 9 on AT systems, because IRQ 8-15
00492              * cascade to IRQ 2 on such systems. but it's nothing to E_Exit() over. */
00493             LOG(LOG_PIC,LOG_ERROR)("ISA PIC problem: IRQ %d (cascade) is active on master PIC without active IRQ 8-15 on slave PIC.",master_cascade_irq);
00494             slave.lower_irq(master_cascade_irq); /* clear it */
00495         }
00496 
00497         return;
00498     }
00499 
00500     slave.start_irq(pic1_irq);
00501     master.start_irq(master_cascade_irq);
00502     CPU_HW_Interrupt((unsigned int)slave.vector_base + (unsigned int)pic1_irq);
00503 }
00504 
00505 static void inline master_startIRQ(Bitu i){
00506     master.start_irq(i);
00507     CPU_HW_Interrupt(master.vector_base + i);
00508 }
00509 
00510 void PIC_runIRQs(void) {
00511     if (!GETFLAG(IF)) return;
00512     if (GCC_UNLIKELY(!PIC_IRQCheck)) return;
00513     if (GCC_UNLIKELY(cpudecoder==CPU_Core_Normal_Trap_Run)) return; // FIXME: Why?
00514     if (GCC_UNLIKELY(CPU_NMI_active)) return;
00515     if (GCC_UNLIKELY(CPU_NMI_pending)) {
00516         CPU_NMI_Interrupt(); // converts pending to active
00517         return; /* NMI has higher priority than PIC */
00518     }
00519 
00520     const Bit8u p = (master.irr & master.imrr)&master.isrr;
00521     Bit8u max = master.special?8:master.active_irq;
00522     Bit8u i,s;
00523 
00524     if (ignore_cascade_in_service && max == (unsigned char)master_cascade_irq)
00525         max++;
00526 
00527     for (i = 0,s = 1;i < max;i++, s<<=1){
00528         if (p&s) {
00529             if (PIC_IRQ_hax[i] & PIC_irq_hack_cs_equ_ds)
00530                 if (!IRQ_hack_check_cs_equ_ds(i))
00531                     continue; // skip IRQ
00532 
00533             if ((int)i == master_cascade_irq) { //second pic, or will not match if master_cascade_irq == -1
00534                 slave_startIRQ();
00535             } else {
00536                 master_startIRQ(i);
00537             }
00538             break;
00539         }
00540     }
00541 
00542     if (slave.auto_eoi)
00543         slave.check_for_irq();
00544     if (master.auto_eoi)
00545         master.check_for_irq();
00546 
00547     /* continue (delayed) processing if more interrupts to handle */
00548     PIC_IRQCheck = 0;
00549     if (i != max) {
00550         if (!PIC_IRQCheckPending) {
00551             PIC_IRQCheckPending = 1;
00552             PIC_AddEvent(PIC_IRQCheckDelayed,(double)PIC_irq_delay_ns / 1000000,0);
00553         }
00554     }
00555 }
00556 
00557 void PIC_SetIRQMask(Bitu irq, bool masked) {
00558     Bitu t = irq>7 ? (irq - 8): irq;
00559     PIC_Controller * pic=&pics[irq>7 ? 1 : 0];
00560     //clear bit
00561     Bit8u bit = 1 <<(t);
00562     Bit8u newmask = pic->imr;
00563     newmask &= ~bit;
00564     if (masked) newmask |= bit;
00565     pic->set_imr(newmask);
00566 }
00567 
00568 void DEBUG_PICSignal(int irq,bool raise) {
00569     if (irq >= 0 && irq <= 15) {
00570         if (raise)
00571             PIC_ActivateIRQ((unsigned int)irq);
00572         else
00573             PIC_DeActivateIRQ((unsigned int)irq);
00574     }
00575 }
00576 
00577 void DEBUG_PICAck(int irq) {
00578     if (irq >= 0 && irq <= 15) {
00579         PIC_Controller * pic=&pics[irq>7 ? 1 : 0];
00580 
00581         pic->isr &= ~(1u << ((unsigned int)irq & 7U));
00582         pic->isrr = (~pic->isr) | pic->isr_ignore;
00583         pic->check_after_EOI();
00584     }
00585 }
00586 
00587 void DEBUG_PICMask(int irq,bool mask) {
00588     if (irq >= 0 && irq <= 15)
00589         PIC_SetIRQMask((unsigned int)irq,mask);
00590 }
00591 
00592 static void AddEntry(PICEntry * entry) {
00593     PICEntry * find_entry=pic_queue.next_entry;
00594     if (GCC_UNLIKELY(find_entry ==0)) {
00595         entry->next=0;
00596         pic_queue.next_entry=entry;
00597     } else if (find_entry->index>entry->index) {
00598         pic_queue.next_entry=entry;
00599         entry->next=find_entry;
00600     } else while (find_entry) {
00601         if (find_entry->next) {
00602             /* See if the next index comes later than this one */
00603             if (find_entry->next->index > entry->index) {
00604                 entry->next=find_entry->next;
00605                 find_entry->next=entry;
00606                 break;
00607             } else {
00608                 find_entry=find_entry->next;
00609             }
00610         } else {
00611             entry->next=find_entry->next;
00612             find_entry->next=entry;
00613             break;
00614         }
00615     }
00616     Bits cycles=PIC_MakeCycles(pic_queue.next_entry->index-PIC_TickIndex());
00617     if (cycles<CPU_Cycles) {
00618         CPU_CycleLeft+=CPU_Cycles;
00619         CPU_Cycles=0;
00620     }
00621 }
00622 
00623 static bool InEventService = false;
00624 static pic_tickindex_t srv_lag = 0;
00625 
00626 pic_tickindex_t PIC_GetCurrentEventTime(void) {
00627     if (InEventService)
00628         return (pic_tickindex_t)PIC_Ticks + srv_lag;
00629     else
00630         return PIC_FullIndex();
00631 }
00632 
00633 void PIC_AddEvent(PIC_EventHandler handler,pic_tickindex_t delay,Bitu val) {
00634     if (GCC_UNLIKELY(!pic_queue.free_entry)) {
00635         LOG(LOG_PIC,LOG_ERROR)("Event queue full");
00636         return;
00637     }
00638     PICEntry * entry=pic_queue.free_entry;
00639     if(InEventService) entry->index = delay + srv_lag;
00640     else entry->index = delay + PIC_TickIndex();
00641 
00642     entry->pic_event=handler;
00643     entry->value=val;
00644     pic_queue.free_entry=pic_queue.free_entry->next;
00645     AddEntry(entry);
00646 }
00647 
00648 void PIC_RemoveSpecificEvents(PIC_EventHandler handler, Bitu val) {
00649     PICEntry * entry=pic_queue.next_entry;
00650     PICEntry * prev_entry;
00651     prev_entry = 0;
00652     while (entry) {
00653         if (GCC_UNLIKELY((entry->pic_event == handler)) && (entry->value == val)) {
00654             if (prev_entry) {
00655                 prev_entry->next=entry->next;
00656                 entry->next=pic_queue.free_entry;
00657                 pic_queue.free_entry=entry;
00658                 entry=prev_entry->next;
00659                 continue;
00660             } else {
00661                 pic_queue.next_entry=entry->next;
00662                 entry->next=pic_queue.free_entry;
00663                 pic_queue.free_entry=entry;
00664                 entry=pic_queue.next_entry;
00665                 continue;
00666             }
00667         }
00668         prev_entry=entry;
00669         entry=entry->next;
00670     }   
00671 }
00672 
00673 void PIC_RemoveEvents(PIC_EventHandler handler) {
00674     PICEntry * entry=pic_queue.next_entry;
00675     PICEntry * prev_entry;
00676     prev_entry=0;
00677     while (entry) {
00678         if (GCC_UNLIKELY(entry->pic_event==handler)) {
00679             if (prev_entry) {
00680                 prev_entry->next=entry->next;
00681                 entry->next=pic_queue.free_entry;
00682                 pic_queue.free_entry=entry;
00683                 entry=prev_entry->next;
00684                 continue;
00685             } else {
00686                 pic_queue.next_entry=entry->next;
00687                 entry->next=pic_queue.free_entry;
00688                 pic_queue.free_entry=entry;
00689                 entry=pic_queue.next_entry;
00690                 continue;
00691             }
00692         }
00693         prev_entry=entry;
00694         entry=entry->next;
00695     }   
00696 }
00697 
00698 extern ClockDomain clockdom_DOSBox_cycles;
00699 
00700 //#define DEBUG_CPU_CYCLE_OVERRUN
00701 //#define DEBUG_PIC_IRQCHECK_VS_IRR
00702 
00703 bool PIC_RunQueue(void) {
00704 #if C_DEBUG
00705     bool IsDebuggerActive(void);
00706     if (IsDebuggerActive())
00707         return false;
00708 #endif
00709 #ifdef DEBUG_CPU_CYCLE_OVERRUN
00710     /* I/O delay can cause negative CPU_Cycles and PIC event / audio rendering issues */
00711     cpu_cycles_count_t overrun = -std::min(CPU_Cycles,(cpu_cycles_count_t)0);
00712 
00713     if (overrun > (CPU_CycleMax/100))
00714         LOG_MSG("PIC_RunQueue: CPU cycles count overrun by %ld (%.3fms)\n",(signed long)overrun,(double)overrun / CPU_CycleMax);
00715 #endif
00716 
00717     /* Check to see if a new millisecond needs to be started */
00718     CPU_CycleLeft += CPU_Cycles;
00719     CPU_Cycles = 0;
00720 
00721 #ifdef DEBUG_PIC_IRQCHECK_VS_IRR
00722     // WARNING: If the problem is the cascade interrupt un-acknowledged, this will give a false positive
00723     if (!PIC_IRQCheck && !PIC_IRQCheckPending && ((master.irr&master.imrr) != 0 || (slave.irr&slave.imrr) != 0))
00724         LOG_MSG("PIC_IRQCheck not set and interrupts pending");
00725 #endif
00726 
00727     if (CPU_CycleLeft > 0) {
00728         if (PIC_IRQCheck)
00729             PIC_runIRQs();
00730 
00731         /* Check the queue for an entry */
00732         Bits index_nd=PIC_TickIndexND();
00733         InEventService = true;
00734         while (pic_queue.next_entry && (pic_queue.next_entry->index*CPU_CycleMax<=index_nd)) {
00735             PICEntry * entry=pic_queue.next_entry;
00736             pic_queue.next_entry=entry->next;
00737 
00738             srv_lag = entry->index;
00739             (entry->pic_event)(entry->value); // call the event handler
00740 
00741             /* Put the entry in the free list */
00742             entry->next=pic_queue.free_entry;
00743             pic_queue.free_entry=entry;
00744         }
00745         InEventService = false;
00746 
00747         /* Check when to set the new cycle end */
00748         if (pic_queue.next_entry) {
00749             Bits cycles=(Bits)(pic_queue.next_entry->index*CPU_CycleMax-index_nd);
00750             if (GCC_UNLIKELY(!cycles)) cycles=1;
00751             if (cycles<CPU_CycleLeft) {
00752                 CPU_Cycles=cycles;
00753             } else {
00754                 CPU_Cycles=CPU_CycleLeft;
00755             }
00756         } else CPU_Cycles=CPU_CycleLeft;
00757         CPU_CycleLeft-=CPU_Cycles;
00758 
00759         if (PIC_IRQCheck)
00760             PIC_runIRQs();
00761     }
00762 
00763     /* if we're out of cycles, then return false. don't execute any more instructions */
00764     if ((CPU_CycleLeft+CPU_Cycles) <= 0)
00765         return false;
00766 
00767     return true;
00768 }
00769 
00770 /* The TIMER Part */
00771 struct TickerBlock {
00772     /* TODO: carry const char * field for name! */
00773     TIMER_TickHandler handler;
00774     TickerBlock * next;
00775 };
00776 
00777 static TickerBlock * firstticker=0;
00778 
00779 void TIMER_ShutdownTickHandlers() {
00780     unsigned int leftovers = 0;
00781 
00782     /* pull in the singly linked list from the front, hand over hand */
00783     while (firstticker != NULL) {
00784         TickerBlock *n = firstticker->next;
00785         delete firstticker;
00786         firstticker = n;
00787         leftovers++;
00788     }
00789 
00790     if (leftovers != 0)
00791         LOG(LOG_MISC,LOG_DEBUG)("TIMER: %u leftover handlers (clean up!).",leftovers);
00792 }
00793 
00794 void TIMER_DelTickHandler(TIMER_TickHandler handler) {
00795     TickerBlock * ticker=firstticker;
00796     TickerBlock * * tick_where=&firstticker;
00797     while (ticker) {
00798         if (ticker->handler==handler) {
00799             *tick_where=ticker->next;
00800             delete ticker;
00801             return;
00802         }
00803         tick_where=&ticker->next;
00804         ticker=ticker->next;
00805     }
00806 }
00807 
00808 void TIMER_AddTickHandler(TIMER_TickHandler handler) {
00809     TickerBlock * newticker=new TickerBlock;
00810     newticker->next=firstticker;
00811     newticker->handler=handler;
00812     firstticker=newticker;
00813 }
00814 
00815 extern Bitu time_limit_ms;
00816 
00817 static unsigned long PIC_benchstart = 0;
00818 static unsigned long PIC_tickstart = 0;
00819 
00820 extern void GFX_SetTitle(Bit32s cycles, Bits frameskip, Bits timing, bool paused);
00821 void TIMER_AddTick(void) {
00822     /* Setup new amount of cycles for PIC */
00823     PIC_Ticks++;
00824     if ((PIC_Ticks&0x3fff) == 0) {
00825         unsigned long ticks = GetTicks();
00826         int delta = int((PIC_Ticks-PIC_tickstart)*10000/(ticks-PIC_benchstart)+5);
00827         GFX_SetTitle(-1,-1,delta,false);
00828         PIC_benchstart = ticks;
00829         PIC_tickstart = PIC_Ticks;
00830     }
00831     CPU_CycleLeft += CPU_CycleMax + CPU_Cycles;
00832     CPU_Cycles = 0;
00833 
00834     /* timeout */
00835     if (time_limit_ms != 0 && PIC_Ticks >= time_limit_ms)
00836         throw int(1);
00837 
00838     /* Go through the list of scheduled events and lower their index with 1000 */
00839     PICEntry * entry=pic_queue.next_entry;
00840     while (entry) {
00841         entry->index -= 1.0;
00842         entry=entry->next;
00843     }
00844 
00845     /* Call our list of ticker handlers */
00846     TickerBlock * ticker=firstticker;
00847     while (ticker) {
00848         TickerBlock * nextticker=ticker->next;
00849         ticker->handler();
00850         ticker=nextticker;
00851     }
00852 }
00853 
00854 static IO_WriteHandleObject PCXT_NMI_WriteHandler;
00855 
00856 static IO_ReadHandleObject ReadHandler[4];
00857 static IO_WriteHandleObject WriteHandler[4];
00858 
00859 void PIC_Reset(Section *sec) {
00860     (void)sec;//UNUSED
00861     Bitu i;
00862 
00863     ReadHandler[0].Uninstall();
00864     ReadHandler[1].Uninstall();
00865     WriteHandler[0].Uninstall();
00866     WriteHandler[1].Uninstall();
00867     ReadHandler[2].Uninstall();
00868     ReadHandler[3].Uninstall();
00869     WriteHandler[2].Uninstall();
00870     WriteHandler[3].Uninstall();
00871     PCXT_NMI_WriteHandler.Uninstall();
00872 
00873     /* NTS: Parsing this on reset allows PIC configuration changes on reboot instead of restarting the entire emulator */
00874     Section_prop * section=static_cast<Section_prop *>(control->GetSection("dosbox"));
00875     assert(section != NULL);
00876 
00877     enable_slave_pic = section->Get_bool("enable slave pic");
00878     enable_pc_xt_nmi_mask = section->Get_bool("enable pc nmi mask");
00879     never_mark_cascade_in_service = section->Get_bool("cascade interrupt never in service");
00880 
00881     {
00882         std::string x = section->Get_string("cascade interrupt ignore in service");
00883 
00884         if (x == "1" || x == "true")
00885             ignore_cascade_in_service = true;
00886         else if (x == "0" || x == "false")
00887             ignore_cascade_in_service = false;
00888         else {
00889             // auto
00890             if (IS_PC98_ARCH)
00891                 ignore_cascade_in_service = true;
00892             else
00893                 ignore_cascade_in_service = false;
00894         }
00895 
00896         LOG(LOG_MISC,LOG_DEBUG)("PIC: Ignore cascade in service=%u",ignore_cascade_in_service);
00897     }
00898 
00899     if (enable_slave_pic && machine == MCH_PCJR && enable_pc_xt_nmi_mask) {
00900         LOG(LOG_MISC,LOG_DEBUG)("PIC_Reset(): PCjr emulation with NMI mask register requires disabling slave PIC (IRQ 8-15)");
00901         enable_slave_pic = false;
00902     }
00903 
00904     if (!enable_slave_pic && IS_PC98_ARCH)
00905         LOG(LOG_MISC,LOG_DEBUG)("PIC_Reset(): PC-98 emulation without slave PIC (IRQ 8-15) is unusual");
00906 
00907     /* NTS: This is a good guess. But the 8259 is static circuitry and not driven by a clock.
00908      *      But the ability to respond to interrupts is limited by the CPU, too. */
00909     PIC_irq_delay_ns = 1000000000UL / (unsigned long)PIT_TICK_RATE;
00910     {
00911         int x = section->Get_int("irq delay ns");
00912         if (x >= 0) PIC_irq_delay_ns = (unsigned int)x;
00913     }
00914 
00915     if (enable_slave_pic)
00916         master_cascade_irq = IS_PC98_ARCH ? 7 : 2;
00917     else
00918         master_cascade_irq = -1;
00919 
00920     // LOG
00921     LOG(LOG_MISC,LOG_DEBUG)("PIC_Reset(): reinitializing PIC controller (cascade=%d)",master_cascade_irq);
00922 
00923     /* Setup pic0 and pic1 with initial values like DOS has normally */
00924     PIC_Ticks=0;
00925     PIC_IRQCheck=0;
00926     for (i=0;i<2;i++) {
00927         pics[i].auto_eoi=false;
00928         pics[i].rotate_on_auto_eoi=false;
00929         pics[i].request_issr=false;
00930         pics[i].special=false;
00931         pics[i].single=false;
00932         pics[i].icw_index=0;
00933         pics[i].icw_words=0;
00934         pics[i].irr = pics[i].isr = pics[i].imrr = 0;
00935         pics[i].isrr = pics[i].imr = 0xff;
00936         pics[i].isr_ignore = 0x00;
00937         pics[i].active_irq = 8;
00938     }
00939 
00940     /* PC-98: By default (but an option otherwise)
00941      *        initialize the PIC so that reading the command port
00942      *        produces the ISR status not the IRR status.
00943      *
00944      *        The reason the option is on by default, is that there
00945      *        is PC-98 programming literature that recommends reading
00946      *        ISR status before acknowledging interrupts (to avoid
00947      *        conflicts with other ISR handlers perhaps). So it's
00948      *        probably a common convention.
00949      *
00950      * Notes: "Blackbird" by Vivian needs this in order for the FM interrupt
00951      *        to continue working. A bug in the FM interrupt routine programs
00952      *        only the master PIC into this mode but then reads from the slave
00953      *        which is not necessarily initialized into this mode and may return
00954      *        the IRR register instead, causing the game to misinterpret
00955      *        incoming interrupts as in-service. */
00956     if (IS_PC98_ARCH && section->Get_bool("pc-98 pic init to read isr"))
00957         pics[0].request_issr = pics[1].request_issr = true;
00958 
00959     /* IBM: IRQ 0-15 is INT 0x08-0x0F, 0x70-0x7F
00960      * PC-98: IRQ 0-15 is INT 0x08-0x17 */
00961     master.vector_base = 0x08;
00962     slave.vector_base = IS_PC98_ARCH ? 0x10 : 0x70;
00963 
00964     for (Bitu i=0;i < 16;i++)
00965         PIC_SetIRQMask(i,true);
00966 
00967     PIC_SetIRQMask(0,false);                    /* Enable system timer */
00968     PIC_SetIRQMask(1,false);                    /* Enable keyboard interrupt */
00969     PIC_SetIRQMask(8,false);                    /* Enable RTC IRQ */
00970 
00971     if (master_cascade_irq >= 0) {
00972         PIC_SetIRQMask((unsigned int)master_cascade_irq,false);/* Enable second pic */
00973 
00974         if (ignore_cascade_in_service)
00975             pics[0].isr_ignore |= 1u << (unsigned char)master_cascade_irq;
00976     }
00977 
00978     /* I/O port map
00979      *
00980      * IBM PC/XT/AT     NEC PC-98        A0
00981      * ---------------------------------------
00982      * 0x20             0x00             0
00983      * 0x21             0x02             1
00984      * 0xA0             0x08             0
00985      * 0xA1             0x0A             1
00986      */
00987 
00988     ReadHandler[0].Install(IS_PC98_ARCH ? 0x00 : 0x20,read_command,IO_MB);
00989     ReadHandler[1].Install(IS_PC98_ARCH ? 0x02 : 0x21,read_data,IO_MB);
00990     WriteHandler[0].Install(IS_PC98_ARCH ? 0x00 : 0x20,write_command,IO_MB);
00991     WriteHandler[1].Install(IS_PC98_ARCH ? 0x02 : 0x21,write_data,IO_MB);
00992 
00993     /* the secondary slave PIC takes priority over PC/XT NMI mask emulation */
00994     if (enable_slave_pic) {
00995         ReadHandler[2].Install(IS_PC98_ARCH ? 0x08 : 0xa0,read_command,IO_MB);
00996         ReadHandler[3].Install(IS_PC98_ARCH ? 0x0A : 0xa1,read_data,IO_MB);
00997         WriteHandler[2].Install(IS_PC98_ARCH ? 0x08 : 0xa0,write_command,IO_MB);
00998         WriteHandler[3].Install(IS_PC98_ARCH ? 0x0A : 0xa1,write_data,IO_MB);
00999     }
01000     else if (!IS_PC98_ARCH && enable_pc_xt_nmi_mask) {
01001         PCXT_NMI_WriteHandler.Install(0xa0,pc_xt_nmi_write,IO_MB);
01002     }
01003 }
01004 
01005 void PIC_Destroy(Section* sec) {
01006     (void)sec;//UNUSED
01007 }
01008 
01009 void Init_PIC() {
01010     Bitu i;
01011 
01012     LOG(LOG_MISC,LOG_DEBUG)("Init_PIC()");
01013 
01014     /* Initialize the pic queue */
01015     for (i=0;i<PIC_QUEUESIZE-1;i++) {
01016         pic_queue.entries[i].next=&pic_queue.entries[i+1];
01017 
01018         // savestate compatibility
01019         pic_queue.entries[i].pic_event = 0;
01020     }
01021     pic_queue.entries[PIC_QUEUESIZE-1].next=0;
01022     pic_queue.free_entry=&pic_queue.entries[0];
01023     pic_queue.next_entry=0;
01024 
01025     AddExitFunction(AddExitFunctionFuncPair(PIC_Destroy));
01026     AddVMEventFunction(VM_EVENT_RESET,AddVMEventFunctionFuncPair(PIC_Reset));
01027 }
01028 
01029 #if C_DEBUG
01030 void DEBUG_LogPIC_C(PIC_Controller &pic) {
01031     LOG_MSG("%s interrupt controller state",&pic == &master ? "Master" : "Slave");
01032     LOG_MSG("ICW %u/%u special=%u auto-eoi=%u rotate-eoi=%u single=%u request_issr=%u vectorbase=0x%02x active_irq=%u isr=%02x isrr=%02x isrignore=%02x",
01033         (unsigned int)pic.icw_index,
01034         (unsigned int)pic.icw_words,
01035         pic.special?1:0,
01036         pic.auto_eoi?1:0,
01037         pic.rotate_on_auto_eoi?1:0,
01038         pic.single?1:0,
01039         pic.request_issr?1:0,
01040         pic.vector_base,
01041         pic.active_irq,
01042         pic.isr,
01043         pic.isrr,
01044         pic.isr_ignore);
01045 
01046     LOG_MSG("IRQ INT#  Req /Mask/Serv");
01047     for (unsigned int si=0;si < 8;si++) {
01048         unsigned int IRQ = si + (&pic == &slave ? 8 : 0);
01049         unsigned int CPUINT = pic.vector_base + si;
01050 
01051         LOG_MSG("%3u 0x%02X   %c    %c    %c   %s",
01052             IRQ,
01053             CPUINT,
01054             (pic.irr & (1U << si))?'R':' ',
01055             (pic.imr & (1U << si))?'M':' ',
01056             (pic.isr & (1U << si))?'S':' ',
01057             ((unsigned int)IRQ == (unsigned int)master_cascade_irq) ? "CASCADE" : "");
01058     }
01059 }
01060 
01061 void DEBUG_LogPIC(void) {
01062     DEBUG_LogPIC_C(master);
01063     if (enable_slave_pic) DEBUG_LogPIC_C(slave);
01064 }
01065 #endif
01066 
01067