DOSBox-X
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Defines
src/hardware/pic.cpp
00001 /*
00002  *  Copyright (C) 2002-2012  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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, 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 }
00368 
00369 /* FIXME: This should be called something else that's true to the ISA bus, like PIC_PulseIRQ, not Activate IRQ.
00370  *        ISA interrupts are edge triggered, not level triggered. */
00371 void PIC_ActivateIRQ(Bitu irq) {
00372     /* Remember what was once IRQ 2 on PC/XT is IRQ 9 on PC/AT */
00373     if (IS_PC98_ARCH) {
00374         if (irq == 7) {
00375             LOG(LOG_PIC,LOG_ERROR)("Attempted to raise IRQ %u, which is cascade IRQ",(int)irq);
00376             return; /* don't raise cascade IRQ */
00377         }
00378     }
00379     else if (enable_slave_pic) { /* PC/AT emulation with slave PIC cascade to master */
00380         if (irq == 2) irq = 9;
00381     }
00382     else { /* PC/XT emulation with only master PIC */
00383         if (irq == 9) irq = 2;
00384         if (irq >= 8) {
00385             LOG(LOG_PIC,LOG_ERROR)("Attempted to raise IRQ %u when slave PIC does not exist",(int)irq);
00386             return;
00387         }
00388     }
00389 
00390     Bitu t = irq>7 ? (irq - 8): irq;
00391     PIC_Controller * pic=&pics[irq>7 ? 1 : 0];
00392 
00393     pic->raise_irq(t);
00394 }
00395 
00396 void PIC_DeActivateIRQ(Bitu irq) {
00397     /* Remember what was once IRQ 2 on PC/XT is IRQ 9 on PC/AT */
00398     if (IS_PC98_ARCH) {
00399         if (irq == 7) return;
00400     }
00401     else if (enable_slave_pic) { /* PC/AT emulation with slave PIC cascade to master */
00402         if (irq == 2) irq = 9;
00403     }
00404     else { /* PC/XT emulation with only master PIC */
00405         if (irq == 9) irq = 2;
00406         if (irq >= 8) {
00407             LOG(LOG_PIC,LOG_ERROR)("Attempted to lower IRQ %u when slave PIC does not exist",(int)irq);
00408             return;
00409         }
00410     }
00411 
00412     Bitu t = irq>7 ? (irq - 8): irq;
00413     PIC_Controller * pic=&pics[irq>7 ? 1 : 0];
00414     pic->lower_irq(t);
00415 }
00416 
00417 unsigned int PIC_IRQ_hax[16] = { PIC_irq_hack_none };
00418 
00419 void PIC_Set_IRQ_hack(int IRQ,unsigned int hack) {
00420     if (IRQ < 0 || IRQ >= 16) return;
00421     PIC_IRQ_hax[IRQ] = hack;
00422 }
00423 
00424 unsigned int PIC_parse_IRQ_hack_string(const char *str) {
00425     unsigned int res = PIC_irq_hack_none;
00426     std::string what;
00427 
00428     while (*str != 0) {
00429         while (*str == ' ') str++;
00430         if (*str == 0) break;
00431 
00432         what.clear();
00433         while (*str != 0 && *str != ' ')
00434             what += *str++;
00435 
00436         while (*str == ' ') str++;
00437 
00438         LOG_MSG("IRQ HACK: '%s'",what.c_str());
00439 
00440         if (what == "none")
00441             res  = PIC_irq_hack_none;
00442         else if (what == "cs_equ_ds")
00443             res |= PIC_irq_hack_cs_equ_ds;
00444     }
00445 
00446     return res;
00447 }
00448 
00449 static bool IRQ_hack_check_cs_equ_ds(const int IRQ) {
00450     uint16_t s_cs = SegValue(cs);
00451     uint16_t s_ds = SegValue(ds);
00452 
00453     if (s_cs >= 0xA000)
00454         return true; // don't complain about the BIOS ISR
00455 
00456     if (s_cs != s_ds) {
00457 #if 0
00458         LOG(LOG_PIC,LOG_DEBUG)("Not dispatching IRQ %d according to IRQ hack. CS != DS",IRQ);
00459 #endif
00460         return false;
00461     }
00462 
00463     return true;
00464 }
00465 
00466 static void slave_startIRQ(){
00467     Bit8u pic1_irq = 8;
00468     bool skipped_irq = false;
00469     const Bit8u p = (slave.irr & slave.imrr)&slave.isrr;
00470     const Bit8u max = slave.special?8:slave.active_irq;
00471     for(Bit8u i = 0,s = 1;i < max;i++, s<<=1) {
00472         if (p&s) {
00473             if (PIC_IRQ_hax[i+8] & PIC_irq_hack_cs_equ_ds) {
00474                 if (!IRQ_hack_check_cs_equ_ds(i+8)) {
00475                     skipped_irq = true;
00476                     continue; // skip IRQ
00477                 }
00478             }
00479 
00480             pic1_irq = i;
00481             break;
00482         }
00483     }
00484 
00485     if (GCC_UNLIKELY(pic1_irq == 8)) {
00486         if (!skipped_irq) {
00487             /* we have an IRQ routing problem. this code is supposed to emulate the fact that
00488              * what was once IRQ 2 on PC/XT is routed to IRQ 9 on AT systems, because IRQ 8-15
00489              * cascade to IRQ 2 on such systems. but it's nothing to E_Exit() over. */
00490             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);
00491             slave.lower_irq(master_cascade_irq); /* clear it */
00492         }
00493 
00494         return;
00495     }
00496 
00497     slave.start_irq(pic1_irq);
00498     master.start_irq(master_cascade_irq);
00499     CPU_HW_Interrupt((unsigned int)slave.vector_base + (unsigned int)pic1_irq);
00500 }
00501 
00502 static void inline master_startIRQ(Bitu i){
00503     master.start_irq(i);
00504     CPU_HW_Interrupt(master.vector_base + i);
00505 }
00506 
00507 void PIC_runIRQs(void) {
00508     if (!GETFLAG(IF)) return;
00509     if (GCC_UNLIKELY(!PIC_IRQCheck)) return;
00510     if (GCC_UNLIKELY(cpudecoder==CPU_Core_Normal_Trap_Run)) return; // FIXME: Why?
00511     if (GCC_UNLIKELY(CPU_NMI_active) || GCC_UNLIKELY(CPU_NMI_pending)) return; /* NMI has higher priority than PIC */
00512 
00513     const Bit8u p = (master.irr & master.imrr)&master.isrr;
00514     Bit8u max = master.special?8:master.active_irq;
00515     Bit8u i,s;
00516 
00517     if (ignore_cascade_in_service && max == (unsigned char)master_cascade_irq)
00518         max++;
00519 
00520     for (i = 0,s = 1;i < max;i++, s<<=1){
00521         if (p&s) {
00522             if (PIC_IRQ_hax[i] & PIC_irq_hack_cs_equ_ds)
00523                 if (!IRQ_hack_check_cs_equ_ds(i))
00524                     continue; // skip IRQ
00525 
00526             if ((int)i == master_cascade_irq) { //second pic, or will not match if master_cascade_irq == -1
00527                 slave_startIRQ();
00528             } else {
00529                 master_startIRQ(i);
00530             }
00531             break;
00532         }
00533     }
00534 
00535     if (slave.auto_eoi)
00536         slave.check_for_irq();
00537     if (master.auto_eoi)
00538         master.check_for_irq();
00539 
00540     /* continue (delayed) processing if more interrupts to handle */
00541     PIC_IRQCheck = 0;
00542     if (i != max) {
00543         if (!PIC_IRQCheckPending) {
00544             PIC_IRQCheckPending = 1;
00545             PIC_AddEvent(PIC_IRQCheckDelayed,(double)PIC_irq_delay_ns / 1000000,0);
00546         }
00547     }
00548 }
00549 
00550 void PIC_SetIRQMask(Bitu irq, bool masked) {
00551     Bitu t = irq>7 ? (irq - 8): irq;
00552     PIC_Controller * pic=&pics[irq>7 ? 1 : 0];
00553     //clear bit
00554     Bit8u bit = 1 <<(t);
00555     Bit8u newmask = pic->imr;
00556     newmask &= ~bit;
00557     if (masked) newmask |= bit;
00558     pic->set_imr(newmask);
00559 }
00560 
00561 void DEBUG_PICSignal(int irq,bool raise) {
00562     if (irq >= 0 && irq <= 15) {
00563         if (raise)
00564             PIC_ActivateIRQ((unsigned int)irq);
00565         else
00566             PIC_DeActivateIRQ((unsigned int)irq);
00567     }
00568 }
00569 
00570 void DEBUG_PICAck(int irq) {
00571     if (irq >= 0 && irq <= 15) {
00572         PIC_Controller * pic=&pics[irq>7 ? 1 : 0];
00573 
00574         pic->isr &= ~(1u << ((unsigned int)irq & 7U));
00575         pic->isrr = (~pic->isr) | pic->isr_ignore;
00576         pic->check_after_EOI();
00577     }
00578 }
00579 
00580 void DEBUG_PICMask(int irq,bool mask) {
00581     if (irq >= 0 && irq <= 15)
00582         PIC_SetIRQMask((unsigned int)irq,mask);
00583 }
00584 
00585 static void AddEntry(PICEntry * entry) {
00586     PICEntry * find_entry=pic_queue.next_entry;
00587     if (GCC_UNLIKELY(find_entry ==0)) {
00588         entry->next=0;
00589         pic_queue.next_entry=entry;
00590     } else if (find_entry->index>entry->index) {
00591         pic_queue.next_entry=entry;
00592         entry->next=find_entry;
00593     } else while (find_entry) {
00594         if (find_entry->next) {
00595             /* See if the next index comes later than this one */
00596             if (find_entry->next->index > entry->index) {
00597                 entry->next=find_entry->next;
00598                 find_entry->next=entry;
00599                 break;
00600             } else {
00601                 find_entry=find_entry->next;
00602             }
00603         } else {
00604             entry->next=find_entry->next;
00605             find_entry->next=entry;
00606             break;
00607         }
00608     }
00609     Bits cycles=PIC_MakeCycles(pic_queue.next_entry->index-PIC_TickIndex());
00610     if (cycles<CPU_Cycles) {
00611         CPU_CycleLeft+=CPU_Cycles;
00612         CPU_Cycles=0;
00613     }
00614 }
00615 
00616 static bool InEventService = false;
00617 static pic_tickindex_t srv_lag = 0;
00618 
00619 void PIC_AddEvent(PIC_EventHandler handler,pic_tickindex_t delay,Bitu val) {
00620     if (GCC_UNLIKELY(!pic_queue.free_entry)) {
00621         LOG(LOG_PIC,LOG_ERROR)("Event queue full");
00622         return;
00623     }
00624     PICEntry * entry=pic_queue.free_entry;
00625     if(InEventService) entry->index = delay + srv_lag;
00626     else entry->index = delay + PIC_TickIndex();
00627 
00628     entry->pic_event=handler;
00629     entry->value=val;
00630     pic_queue.free_entry=pic_queue.free_entry->next;
00631     AddEntry(entry);
00632 }
00633 
00634 void PIC_RemoveSpecificEvents(PIC_EventHandler handler, Bitu val) {
00635     PICEntry * entry=pic_queue.next_entry;
00636     PICEntry * prev_entry;
00637     prev_entry = 0;
00638     while (entry) {
00639         if (GCC_UNLIKELY((entry->pic_event == handler)) && (entry->value == val)) {
00640             if (prev_entry) {
00641                 prev_entry->next=entry->next;
00642                 entry->next=pic_queue.free_entry;
00643                 pic_queue.free_entry=entry;
00644                 entry=prev_entry->next;
00645                 continue;
00646             } else {
00647                 pic_queue.next_entry=entry->next;
00648                 entry->next=pic_queue.free_entry;
00649                 pic_queue.free_entry=entry;
00650                 entry=pic_queue.next_entry;
00651                 continue;
00652             }
00653         }
00654         prev_entry=entry;
00655         entry=entry->next;
00656     }   
00657 }
00658 
00659 void PIC_RemoveEvents(PIC_EventHandler handler) {
00660     PICEntry * entry=pic_queue.next_entry;
00661     PICEntry * prev_entry;
00662     prev_entry=0;
00663     while (entry) {
00664         if (GCC_UNLIKELY(entry->pic_event==handler)) {
00665             if (prev_entry) {
00666                 prev_entry->next=entry->next;
00667                 entry->next=pic_queue.free_entry;
00668                 pic_queue.free_entry=entry;
00669                 entry=prev_entry->next;
00670                 continue;
00671             } else {
00672                 pic_queue.next_entry=entry->next;
00673                 entry->next=pic_queue.free_entry;
00674                 pic_queue.free_entry=entry;
00675                 entry=pic_queue.next_entry;
00676                 continue;
00677             }
00678         }
00679         prev_entry=entry;
00680         entry=entry->next;
00681     }   
00682 }
00683 
00684 extern ClockDomain clockdom_DOSBox_cycles;
00685 
00686 //#define DEBUG_CPU_CYCLE_OVERRUN
00687 //#define DEBUG_PIC_IRQCHECK_VS_IRR
00688 
00689 bool PIC_RunQueue(void) {
00690 #if C_DEBUG
00691     bool IsDebuggerActive(void);
00692     if (IsDebuggerActive())
00693         return false;
00694 #endif
00695 #ifdef DEBUG_CPU_CYCLE_OVERRUN
00696     /* I/O delay can cause negative CPU_Cycles and PIC event / audio rendering issues */
00697     cpu_cycles_count_t overrun = -std::min(CPU_Cycles,(cpu_cycles_count_t)0);
00698 
00699     if (overrun > (CPU_CycleMax/100))
00700         LOG_MSG("PIC_RunQueue: CPU cycles count overrun by %ld (%.3fms)\n",(signed long)overrun,(double)overrun / CPU_CycleMax);
00701 #endif
00702 
00703     /* Check to see if a new millisecond needs to be started */
00704     CPU_CycleLeft += CPU_Cycles;
00705     CPU_Cycles = 0;
00706 
00707 #ifdef DEBUG_PIC_IRQCHECK_VS_IRR
00708     // WARNING: If the problem is the cascade interrupt un-acknowledged, this will give a false positive
00709     if (!PIC_IRQCheck && !PIC_IRQCheckPending && ((master.irr&master.imrr) != 0 || (slave.irr&slave.imrr) != 0))
00710         LOG_MSG("PIC_IRQCheck not set and interrupts pending");
00711 #endif
00712 
00713     if (CPU_CycleLeft > 0) {
00714         if (PIC_IRQCheck)
00715             PIC_runIRQs();
00716 
00717         /* Check the queue for an entry */
00718         Bits index_nd=PIC_TickIndexND();
00719         InEventService = true;
00720         while (pic_queue.next_entry && (pic_queue.next_entry->index*CPU_CycleMax<=index_nd)) {
00721             PICEntry * entry=pic_queue.next_entry;
00722             pic_queue.next_entry=entry->next;
00723 
00724             srv_lag = entry->index;
00725             (entry->pic_event)(entry->value); // call the event handler
00726 
00727             /* Put the entry in the free list */
00728             entry->next=pic_queue.free_entry;
00729             pic_queue.free_entry=entry;
00730         }
00731         InEventService = false;
00732 
00733         /* Check when to set the new cycle end */
00734         if (pic_queue.next_entry) {
00735             Bits cycles=(Bits)(pic_queue.next_entry->index*CPU_CycleMax-index_nd);
00736             if (GCC_UNLIKELY(!cycles)) cycles=1;
00737             if (cycles<CPU_CycleLeft) {
00738                 CPU_Cycles=cycles;
00739             } else {
00740                 CPU_Cycles=CPU_CycleLeft;
00741             }
00742         } else CPU_Cycles=CPU_CycleLeft;
00743         CPU_CycleLeft-=CPU_Cycles;
00744 
00745         if (PIC_IRQCheck)
00746             PIC_runIRQs();
00747     }
00748 
00749     /* if we're out of cycles, then return false. don't execute any more instructions */
00750     if ((CPU_CycleLeft+CPU_Cycles) <= 0)
00751         return false;
00752 
00753     return true;
00754 }
00755 
00756 /* The TIMER Part */
00757 struct TickerBlock {
00758     /* TODO: carry const char * field for name! */
00759     TIMER_TickHandler handler;
00760     TickerBlock * next;
00761 };
00762 
00763 static TickerBlock * firstticker=0;
00764 
00765 void TIMER_ShutdownTickHandlers() {
00766     unsigned int leftovers = 0;
00767 
00768     /* pull in the singly linked list from the front, hand over hand */
00769     while (firstticker != NULL) {
00770         TickerBlock *n = firstticker->next;
00771         delete firstticker;
00772         firstticker = n;
00773         leftovers++;
00774     }
00775 
00776     if (leftovers != 0)
00777         LOG(LOG_MISC,LOG_DEBUG)("TIMER: %u leftover handlers (clean up!).",leftovers);
00778 }
00779 
00780 void TIMER_DelTickHandler(TIMER_TickHandler handler) {
00781     TickerBlock * ticker=firstticker;
00782     TickerBlock * * tick_where=&firstticker;
00783     while (ticker) {
00784         if (ticker->handler==handler) {
00785             *tick_where=ticker->next;
00786             delete ticker;
00787             return;
00788         }
00789         tick_where=&ticker->next;
00790         ticker=ticker->next;
00791     }
00792 }
00793 
00794 void TIMER_AddTickHandler(TIMER_TickHandler handler) {
00795     TickerBlock * newticker=new TickerBlock;
00796     newticker->next=firstticker;
00797     newticker->handler=handler;
00798     firstticker=newticker;
00799 }
00800 
00801 extern Bitu time_limit_ms;
00802 
00803 static unsigned long PIC_benchstart = 0;
00804 static unsigned long PIC_tickstart = 0;
00805 
00806 extern void GFX_SetTitle(Bit32s cycles, Bits frameskip, Bits timing, bool paused);
00807 void TIMER_AddTick(void) {
00808     /* Setup new amount of cycles for PIC */
00809     PIC_Ticks++;
00810     if ((PIC_Ticks&0x3fff) == 0) {
00811         unsigned long ticks = GetTicks();
00812         int delta = (PIC_Ticks-PIC_tickstart)*10000/(ticks-PIC_benchstart)+5;
00813         GFX_SetTitle(-1,-1,delta,false);
00814         PIC_benchstart = ticks;
00815         PIC_tickstart = PIC_Ticks;
00816     }
00817     CPU_CycleLeft += CPU_CycleMax + CPU_Cycles;
00818     CPU_Cycles = 0;
00819 
00820     /* timeout */
00821     if (time_limit_ms != 0 && PIC_Ticks >= time_limit_ms)
00822         throw int(1);
00823 
00824     /* Go through the list of scheduled events and lower their index with 1000 */
00825     PICEntry * entry=pic_queue.next_entry;
00826     while (entry) {
00827         entry->index -= 1.0;
00828         entry=entry->next;
00829     }
00830 
00831     /* Call our list of ticker handlers */
00832     TickerBlock * ticker=firstticker;
00833     while (ticker) {
00834         TickerBlock * nextticker=ticker->next;
00835         ticker->handler();
00836         ticker=nextticker;
00837     }
00838 }
00839 
00840 static IO_WriteHandleObject PCXT_NMI_WriteHandler;
00841 
00842 static IO_ReadHandleObject ReadHandler[4];
00843 static IO_WriteHandleObject WriteHandler[4];
00844 
00845 void PIC_Reset(Section *sec) {
00846     (void)sec;//UNUSED
00847     Bitu i;
00848 
00849     ReadHandler[0].Uninstall();
00850     ReadHandler[1].Uninstall();
00851     WriteHandler[0].Uninstall();
00852     WriteHandler[1].Uninstall();
00853     ReadHandler[2].Uninstall();
00854     ReadHandler[3].Uninstall();
00855     WriteHandler[2].Uninstall();
00856     WriteHandler[3].Uninstall();
00857     PCXT_NMI_WriteHandler.Uninstall();
00858 
00859     /* NTS: Parsing this on reset allows PIC configuration changes on reboot instead of restarting the entire emulator */
00860     Section_prop * section=static_cast<Section_prop *>(control->GetSection("dosbox"));
00861     assert(section != NULL);
00862 
00863     enable_slave_pic = section->Get_bool("enable slave pic");
00864     enable_pc_xt_nmi_mask = section->Get_bool("enable pc nmi mask");
00865     never_mark_cascade_in_service = section->Get_bool("cascade interrupt never in service");
00866     ignore_cascade_in_service = section->Get_bool("cascade interrupt ignore in service");
00867 
00868     if (enable_slave_pic && machine == MCH_PCJR && enable_pc_xt_nmi_mask) {
00869         LOG(LOG_MISC,LOG_DEBUG)("PIC_Reset(): PCjr emulation with NMI mask register requires disabling slave PIC (IRQ 8-15)");
00870         enable_slave_pic = false;
00871     }
00872 
00873     if (!enable_slave_pic && IS_PC98_ARCH)
00874         LOG(LOG_MISC,LOG_DEBUG)("PIC_Reset(): PC-98 emulation without slave PIC (IRQ 8-15) is unusual");
00875 
00876     /* NTS: This is a good guess. But the 8259 is static circuitry and not driven by a clock.
00877      *      But the ability to respond to interrupts is limited by the CPU, too. */
00878     PIC_irq_delay_ns = 1000000000UL / (unsigned long)PIT_TICK_RATE;
00879     {
00880         int x = section->Get_int("irq delay ns");
00881         if (x >= 0) PIC_irq_delay_ns = (unsigned int)x;
00882     }
00883 
00884     if (enable_slave_pic)
00885         master_cascade_irq = IS_PC98_ARCH ? 7 : 2;
00886     else
00887         master_cascade_irq = -1;
00888 
00889     // LOG
00890     LOG(LOG_MISC,LOG_DEBUG)("PIC_Reset(): reinitializing PIC controller (cascade=%d)",master_cascade_irq);
00891 
00892     /* Setup pic0 and pic1 with initial values like DOS has normally */
00893     PIC_Ticks=0;
00894     PIC_IRQCheck=0;
00895     for (i=0;i<2;i++) {
00896         pics[i].auto_eoi=false;
00897         pics[i].rotate_on_auto_eoi=false;
00898         pics[i].request_issr=false;
00899         pics[i].special=false;
00900         pics[i].single=false;
00901         pics[i].icw_index=0;
00902         pics[i].icw_words=0;
00903         pics[i].irr = pics[i].isr = pics[i].imrr = 0;
00904         pics[i].isrr = pics[i].imr = 0xff;
00905         pics[i].isr_ignore = 0x00;
00906         pics[i].active_irq = 8;
00907     }
00908 
00909     /* PC-98: By default (but an option otherwise)
00910      *        initialize the PIC so that reading the command port
00911      *        produces the ISR status not the IRR status.
00912      *
00913      *        The reason the option is on by default, is that there
00914      *        is PC-98 programming literature that recommends reading
00915      *        ISR status before acknowledging interrupts (to avoid
00916      *        conflicts with other ISR handlers perhaps). So it's
00917      *        probably a common convention.
00918      *
00919      * Notes: "Blackbird" by Vivian needs this in order for the FM interrupt
00920      *        to continue working. A bug in the FM interrupt routine programs
00921      *        only the master PIC into this mode but then reads from the slave
00922      *        which is not necessarily initialized into this mode and may return
00923      *        the IRR register instead, causing the game to misinterpret
00924      *        incoming interrupts as in-service. */
00925     if (IS_PC98_ARCH && section->Get_bool("pc-98 pic init to read isr"))
00926         pics[0].request_issr = pics[1].request_issr = true;
00927 
00928     /* IBM: IRQ 0-15 is INT 0x08-0x0F, 0x70-0x7F
00929      * PC-98: IRQ 0-15 is INT 0x08-0x17 */
00930     master.vector_base = 0x08;
00931     slave.vector_base = IS_PC98_ARCH ? 0x10 : 0x70;
00932 
00933     for (Bitu i=0;i < 16;i++)
00934         PIC_SetIRQMask(i,true);
00935 
00936     PIC_SetIRQMask(0,false);                    /* Enable system timer */
00937     PIC_SetIRQMask(1,false);                    /* Enable system timer */
00938     PIC_SetIRQMask(8,false);                    /* Enable RTC IRQ */
00939 
00940     if (master_cascade_irq >= 0) {
00941         PIC_SetIRQMask((unsigned int)master_cascade_irq,false);/* Enable second pic */
00942 
00943         if (ignore_cascade_in_service)
00944             pics[0].isr_ignore |= 1u << (unsigned char)master_cascade_irq;
00945     }
00946 
00947     /* I/O port map
00948      *
00949      * IBM PC/XT/AT     NEC PC-98        A0
00950      * ---------------------------------------
00951      * 0x20             0x00             0
00952      * 0x21             0x02             1
00953      * 0xA0             0x08             0
00954      * 0xA1             0x0A             1
00955      */
00956 
00957     ReadHandler[0].Install(IS_PC98_ARCH ? 0x00 : 0x20,read_command,IO_MB);
00958     ReadHandler[1].Install(IS_PC98_ARCH ? 0x02 : 0x21,read_data,IO_MB);
00959     WriteHandler[0].Install(IS_PC98_ARCH ? 0x00 : 0x20,write_command,IO_MB);
00960     WriteHandler[1].Install(IS_PC98_ARCH ? 0x02 : 0x21,write_data,IO_MB);
00961 
00962     /* the secondary slave PIC takes priority over PC/XT NMI mask emulation */
00963     if (enable_slave_pic) {
00964         ReadHandler[2].Install(IS_PC98_ARCH ? 0x08 : 0xa0,read_command,IO_MB);
00965         ReadHandler[3].Install(IS_PC98_ARCH ? 0x0A : 0xa1,read_data,IO_MB);
00966         WriteHandler[2].Install(IS_PC98_ARCH ? 0x08 : 0xa0,write_command,IO_MB);
00967         WriteHandler[3].Install(IS_PC98_ARCH ? 0x0A : 0xa1,write_data,IO_MB);
00968     }
00969     else if (!IS_PC98_ARCH && enable_pc_xt_nmi_mask) {
00970         PCXT_NMI_WriteHandler.Install(0xa0,pc_xt_nmi_write,IO_MB);
00971     }
00972 }
00973 
00974 void PIC_Destroy(Section* sec) {
00975     (void)sec;//UNUSED
00976 }
00977 
00978 void Init_PIC() {
00979     Bitu i;
00980 
00981     LOG(LOG_MISC,LOG_DEBUG)("Init_PIC()");
00982 
00983     /* Initialize the pic queue */
00984     for (i=0;i<PIC_QUEUESIZE-1;i++) {
00985         pic_queue.entries[i].next=&pic_queue.entries[i+1];
00986 
00987         // savestate compatibility
00988         pic_queue.entries[i].pic_event = 0;
00989     }
00990     pic_queue.entries[PIC_QUEUESIZE-1].next=0;
00991     pic_queue.free_entry=&pic_queue.entries[0];
00992     pic_queue.next_entry=0;
00993 
00994     AddExitFunction(AddExitFunctionFuncPair(PIC_Destroy));
00995     AddVMEventFunction(VM_EVENT_RESET,AddVMEventFunctionFuncPair(PIC_Reset));
00996 }
00997 
00998 #if C_DEBUG
00999 void DEBUG_LogPIC_C(PIC_Controller &pic) {
01000     LOG_MSG("%s interrupt controller state",&pic == &master ? "Master" : "Slave");
01001     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",
01002         (unsigned int)pic.icw_index,
01003         (unsigned int)pic.icw_words,
01004         pic.special?1:0,
01005         pic.auto_eoi?1:0,
01006         pic.rotate_on_auto_eoi?1:0,
01007         pic.single?1:0,
01008         pic.request_issr?1:0,
01009         pic.vector_base,
01010         pic.active_irq,
01011         pic.isr,
01012         pic.isrr,
01013         pic.isr_ignore);
01014 
01015     LOG_MSG("IRQ INT#  Req /Mask/Serv");
01016     for (unsigned int si=0;si < 8;si++) {
01017         unsigned int IRQ = si + (&pic == &slave ? 8 : 0);
01018         unsigned int CPUINT = pic.vector_base + si;
01019 
01020         LOG_MSG("%3u 0x%02X   %c    %c    %c   %s",
01021             IRQ,
01022             CPUINT,
01023             (pic.irr & (1U << si))?'R':' ',
01024             (pic.imr & (1U << si))?'M':' ',
01025             (pic.isr & (1U << si))?'S':' ',
01026             ((unsigned int)IRQ == (unsigned int)master_cascade_irq) ? "CASCADE" : "");
01027     }
01028 }
01029 
01030 void DEBUG_LogPIC(void) {
01031     DEBUG_LogPIC_C(master);
01032     if (enable_slave_pic) DEBUG_LogPIC_C(slave);
01033 }
01034 #endif
01035 
01036