DOSBox-X
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator
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 struct PIC_Controller {
00037     Bitu icw_words;
00038     Bitu icw_index;
00039     bool special;
00040     bool auto_eoi;
00041     bool rotate_on_auto_eoi;
00042     bool single;
00043     bool request_issr;
00044     Bit8u vector_base;
00045 
00046     Bit8u irr;        // request register
00047     Bit8u imr;        // mask register
00048     Bit8u imrr;       // mask register reversed (makes bit tests simpler)
00049     Bit8u isr;        // in service register
00050     Bit8u isrr;       // in service register reversed (makes bit tests simpler)
00051     Bit8u active_irq; //currently active irq
00052 
00053 
00054     void set_imr(Bit8u val);
00055 
00056     void check_after_EOI(){
00057         //Update the active_irq as an EOI is likely to change that.
00058         update_active_irq();
00059         if((irr&imrr)&isrr) check_for_irq();
00060     }
00061 
00062     void update_active_irq() {
00063         if(isr == 0) {active_irq = 8; return;}
00064         for(Bit8u i = 0, s = 1; i < 8;i++, s<<=1){
00065             if( isr & s){
00066                 active_irq = i;
00067                 return;
00068             }
00069         }
00070     }
00071 
00072     void check_for_irq(){
00073         const Bit8u possible_irq = (irr&imrr)&isrr;
00074         if (possible_irq) {
00075             const Bit8u a_irq = special?8:active_irq;
00076             for(Bit8u i = 0, s = 1; i < a_irq;i++, s<<=1){
00077                 if ( possible_irq & s ) {
00078                     //There is an irq ready to be served => signal master and/or cpu
00079                     activate();
00080                     return;
00081                 }
00082             }
00083         }
00084         deactivate(); //No irq, remove signal to master and/or cpu
00085     }
00086 
00087     //Signals master/cpu that there is an irq ready.
00088     void activate();
00089 
00090     //Removes signal to master/cpu that there is an irq ready.
00091     void deactivate();
00092 
00093     void raise_irq(Bit8u val){
00094         Bit8u bit = 1 << (val);
00095         if((irr & bit)==0) { //value changed (as it is currently not active)
00096             irr|=bit;
00097             if((bit&imrr)&isrr) { //not masked and not in service
00098                 if(special || val < active_irq) activate();
00099             }
00100         }
00101     }
00102 
00103     void lower_irq(Bit8u val){
00104         Bit8u bit = 1 << ( val);
00105         if(irr & bit) { //value will change (as it is currently active)
00106             irr&=~bit;
00107             if((bit&imrr)&isrr) { //not masked and not in service
00108                 //This irq might have toggled PIC_IRQCheck/caused irq 2 on master, when it was raised.
00109                 //If it is active, then recheck it, we can't just deactivate as there might be more IRQS raised.
00110                 if(special || val < active_irq) check_for_irq();
00111             }
00112         }
00113     }
00114 
00115     //handles all bits and logic related to starting this IRQ, it does NOT start the interrupt on the CPU.
00116     void start_irq(Bit8u val);
00117 };
00118 
00119 int master_cascade_irq = -1;
00120 static PIC_Controller pics[2];
00121 static PIC_Controller& master = pics[0];
00122 static PIC_Controller& slave  = pics[1];
00123 Bitu PIC_Ticks = 0;
00124 Bitu PIC_IRQCheck = 0; //Maybe make it a bool and/or ensure 32bit size (x86 dynamic core seems to assume 32 bit variable size)
00125 Bitu PIC_IRQCheckPending = 0; //Maybe make it a bool and/or ensure 32bit size (x86 dynamic core seems to assume 32 bit variable size)
00126 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) */
00127 bool enable_pc_xt_nmi_mask = false;
00128 
00129 void PIC_IRQCheckDelayed(Bitu val) {
00130     (void)val;//UNUSED
00131     PIC_IRQCheck = 1;
00132     PIC_IRQCheckPending = 0;
00133 }
00134 
00135 void PIC_Controller::set_imr(Bit8u val) {
00136     Bit8u change = (imr) ^ (val); //Bits that have changed become 1.
00137     imr  =  val;
00138     imrr = ~val;
00139 
00140     //Test if changed bits are set in irr and are not being served at the moment
00141     //Those bits have impact on whether the cpu emulation should be paused or not.
00142     if((irr & change)&isrr) check_for_irq();
00143 }
00144 
00145 void PIC_Controller::activate() { 
00146     //Stops CPU if master, signals master if slave
00147     if(this == &master) {
00148         //cycles 0, take care of the port IO stuff added in raise_irq base caller.
00149         if (!PIC_IRQCheckPending) {
00150             /* NTS: PIC_AddEvent by design caps CPU_Cycles to make the event happen on time */
00151             PIC_AddEvent(PIC_IRQCheckDelayed,(double)PIC_irq_delay_ns / 1000000,0);
00152             PIC_IRQCheckPending = 1;
00153         }
00154     } else {
00155         master.raise_irq(master_cascade_irq);
00156     }
00157 }
00158 
00159 void PIC_Controller::deactivate() { 
00160     //removes irq check value if master, signals master if slave
00161     if(this == &master) {
00162         /* NTS: DOSBox code used to set PIC_IRQCheck = 0 here.
00163          *
00164          *      That's actually not the way to handle it. Here's why:
00165          *
00166          *      What would happen if one device raised an IRQ (setting PIC_IRQCheck=1)
00167          *      then before dispatching IRQs, another device lowered an IRQ (setting PIC_IRQCheck=0).
00168          *
00169          *      Lowering the IRQ would reset the flag, and PIC_runIRQs() would never process
00170          *      any pending IRQs even though some are waiting.
00171          *
00172          *      It's better if we set the flag when raising an IRQ, and leave the flag set until
00173          *      PIC_runIRQs() determines there are no more IRQs to dispatch. Then and only then
00174          *      will PIC_runIRQs() clear the flag. */
00175     } else {
00176         /* just because ONE IRQ on the slave finished doesn't mean there aren't any others needing service! */
00177         if ((irr&imrr) == 0)
00178             master.lower_irq(master_cascade_irq);
00179         else
00180             LOG_MSG("Slave PIC: still to handle irr=%02x imrr=%02x isrr=%02x",irr,imrr,isrr);
00181     }
00182 }
00183 
00184 void PIC_Controller::start_irq(Bit8u val){
00185     irr&=~(1<<(val));
00186     if (!auto_eoi) {
00187         active_irq = val;
00188         isr |= 1<<(val);
00189         isrr = ~isr;
00190     } else if (GCC_UNLIKELY(rotate_on_auto_eoi)) {
00191         LOG_MSG("rotate on auto EOI not handled");
00192     }
00193 }
00194 
00195 struct PICEntry {
00196     pic_tickindex_t index;
00197     Bitu value;
00198     PIC_EventHandler pic_event;
00199     PICEntry * next;
00200 };
00201 
00202 static struct {
00203     PICEntry entries[PIC_QUEUESIZE];
00204     PICEntry * free_entry;
00205     PICEntry * next_entry;
00206 } pic_queue;
00207 
00208 static void write_command(Bitu port,Bitu val,Bitu iolen) {
00209     (void)iolen;//UNUSED
00210     PIC_Controller * pic=&pics[(port==0x20/*IBM*/ || port==0x00/*PC-98*/) ? 0 : 1];
00211 
00212     if (GCC_UNLIKELY(val&0x10)) {       // ICW1 issued
00213         if (val&0x04) LOG_MSG("PIC: 4 byte interval not handled");
00214         if (val&0x08) LOG_MSG("PIC: level triggered mode not handled");
00215         if (val&0xe0) LOG_MSG("PIC: 8080/8085 mode not handled");
00216         pic->single=(val&0x02)==0x02;
00217         pic->icw_index=1;           // next is ICW2
00218         pic->icw_words=2 + (val&0x01);  // =3 if ICW4 needed
00219     } else if (GCC_UNLIKELY(val&0x08)) {    // OCW3 issued
00220         if (val&0x04) LOG_MSG("PIC: poll command not handled");
00221         if (val&0x02) {     // function select
00222             if (val&0x01) pic->request_issr=true;   /* select read interrupt in-service register */
00223             else pic->request_issr=false;           /* select read interrupt request register */
00224         }
00225         if (val&0x40) {     // special mask select
00226             if (val&0x20) pic->special = true;
00227             else pic->special = false;
00228             //Check if there are irqs ready to run, as the priority system has possibly been changed.
00229             pic->check_for_irq();
00230             LOG(LOG_PIC,LOG_NORMAL)("port %X : special mask %s",(int)port,(pic->special)?"ON":"OFF");
00231         }
00232     } else {    // OCW2 issued
00233         if (val&0x20) {     // EOI commands
00234             if (GCC_UNLIKELY(val&0x80)) LOG_MSG("rotate mode not supported");
00235             if (val&0x40) {     // specific EOI
00236                 pic->isr &= ~(1<< ((val-0x60)));
00237                 pic->isrr = ~pic->isr;
00238                 pic->check_after_EOI();
00239 //              if (val&0x80);  // perform rotation
00240             } else {        // nonspecific EOI
00241                 if (pic->active_irq != 8) { 
00242                     //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).
00243                     pic->isr &= ~(1 << (pic->active_irq));
00244                     pic->isrr = ~pic->isr;
00245                     pic->check_after_EOI();
00246                 }
00247 //              if (val&0x80);  // perform rotation
00248             }
00249         } else {
00250             if ((val&0x40)==0) {        // rotate in auto EOI mode
00251                 if (val&0x80) pic->rotate_on_auto_eoi=true;
00252                 else pic->rotate_on_auto_eoi=false;
00253             } else if (val&0x80) {
00254                 LOG(LOG_PIC,LOG_NORMAL)("set priority command not handled");
00255             }   // else NOP command
00256         }
00257     }   // end OCW2
00258 }
00259 
00260 static void write_data(Bitu port,Bitu val,Bitu iolen) {
00261     (void)iolen;//UNUSED
00262     PIC_Controller * pic=&pics[(port==0x21/*IBM*/ || port==0x02/*PC-98*/) ? 0 : 1];
00263 
00264     switch(pic->icw_index) {
00265     case 0:                        /* mask register */
00266         pic->set_imr(val);
00267         break;
00268     case 1:                        /* icw2          */
00269         LOG(LOG_PIC,LOG_NORMAL)("%d:Base vector %X",port==0x21 ? 0 : 1,(int)val);
00270         pic->vector_base = val&0xf8;
00271         if(pic->icw_index++ >= pic->icw_words) pic->icw_index=0;
00272         else if(pic->single) pic->icw_index=3;      /* skip ICW3 in single mode */
00273         break;
00274     case 2:                         /* icw 3 */
00275         LOG(LOG_PIC,LOG_NORMAL)("%d:ICW 3 %X",port==0x21 ? 0 : 1,(int)val);
00276         if(pic->icw_index++ >= pic->icw_words) pic->icw_index=0;
00277         break;
00278     case 3:                         /* icw 4 */
00279         /*
00280             0       1 8086/8080  0 mcs-8085 mode
00281             1       1 Auto EOI   0 Normal EOI
00282             2-3    0x Non buffer Mode 
00283                    10 Buffer Mode Slave 
00284                    11 Buffer mode Master    
00285             4       Special/Not Special nested mode 
00286         */
00287         pic->auto_eoi=(val & 0x2)>0;
00288         
00289         LOG(LOG_PIC,LOG_NORMAL)("%d:ICW 4 %X",port==0x21 ? 0 : 1,(int)val);
00290 
00291         if ((val&0x01)==0) LOG_MSG("PIC:ICW4: %x, 8085 mode not handled",(int)val);
00292         if ((val&0x10)!=0) LOG_MSG("PIC:ICW4: %x, special fully-nested mode not handled",(int)val);
00293 
00294         if(pic->icw_index++ >= pic->icw_words) pic->icw_index=0;
00295         break;
00296     default:
00297         LOG(LOG_PIC,LOG_NORMAL)("ICW HUH? %X",(int)val);
00298         break;
00299     }
00300 }
00301 
00302 
00303 static Bitu read_command(Bitu port,Bitu iolen) {
00304     (void)iolen;//UNUSED
00305     PIC_Controller * pic=&pics[(port==0x20/*IBM*/ || port==0x00/*PC-98*/) ? 0 : 1];
00306     if (pic->request_issr){
00307         return pic->isr;
00308     } else { 
00309         return pic->irr;
00310     }
00311 }
00312 
00313 
00314 static Bitu read_data(Bitu port,Bitu iolen) {
00315     (void)iolen;//UNUSED
00316     PIC_Controller * pic=&pics[(port==0x21/*IBM*/ || port==0x02/*PC-98*/) ? 0 : 1];
00317     return pic->imr;
00318 }
00319 
00320 /* PC/XT NMI mask register 0xA0. Documentation on the other bits
00321  * is sparse and spread across the internet, but many seem to
00322  * agree that bit 7 is used to enable/disable the NMI (1=enable,
00323  * 0=disable) */
00324 static void pc_xt_nmi_write(Bitu port,Bitu val,Bitu iolen) {
00325     (void)iolen;//UNUSED
00326     (void)port;//UNUSED
00327     CPU_NMI_gate = (val & 0x80) ? true : false;
00328 }
00329 
00330 /* FIXME: This should be called something else that's true to the ISA bus, like PIC_PulseIRQ, not Activate IRQ.
00331  *        ISA interrupts are edge triggered, not level triggered. */
00332 void PIC_ActivateIRQ(Bitu irq) {
00333     /* Remember what was once IRQ 2 on PC/XT is IRQ 9 on PC/AT */
00334     if (IS_PC98_ARCH) {
00335         if (irq == 7) {
00336             LOG(LOG_PIC,LOG_ERROR)("Attempted to raise IRQ %u, which is cascade IRQ",(int)irq);
00337             return; /* don't raise cascade IRQ */
00338         }
00339     }
00340     else if (enable_slave_pic) { /* PC/AT emulation with slave PIC cascade to master */
00341         if (irq == 2) irq = 9;
00342     }
00343     else { /* PC/XT emulation with only master PIC */
00344         if (irq == 9) irq = 2;
00345         if (irq >= 8) {
00346             LOG(LOG_PIC,LOG_ERROR)("Attempted to raise IRQ %u when slave PIC does not exist",(int)irq);
00347             return;
00348         }
00349     }
00350 
00351     Bitu t = irq>7 ? (irq - 8): irq;
00352     PIC_Controller * pic=&pics[irq>7 ? 1 : 0];
00353 
00354     pic->raise_irq(t);
00355 }
00356 
00357 void PIC_DeActivateIRQ(Bitu irq) {
00358     /* Remember what was once IRQ 2 on PC/XT is IRQ 9 on PC/AT */
00359     if (IS_PC98_ARCH) {
00360         if (irq == 7) return;
00361     }
00362     else if (enable_slave_pic) { /* PC/AT emulation with slave PIC cascade to master */
00363         if (irq == 2) irq = 9;
00364     }
00365     else { /* PC/XT emulation with only master PIC */
00366         if (irq == 9) irq = 2;
00367         if (irq >= 8) {
00368             LOG(LOG_PIC,LOG_ERROR)("Attempted to lower IRQ %u when slave PIC does not exist",(int)irq);
00369             return;
00370         }
00371     }
00372 
00373     Bitu t = irq>7 ? (irq - 8): irq;
00374     PIC_Controller * pic=&pics[irq>7 ? 1 : 0];
00375     pic->lower_irq(t);
00376 }
00377 
00378 enum PIC_irq_hacks PIC_IRQ_hax[16] = { PIC_irq_hack_none };
00379 
00380 void PIC_Set_IRQ_hack(int IRQ,enum PIC_irq_hacks hack) {
00381     if (IRQ < 0 || IRQ >= 16) return;
00382     PIC_IRQ_hax[IRQ] = hack;
00383 }
00384 
00385 enum PIC_irq_hacks PIC_parse_IRQ_hack_string(const char *str) {
00386     if (!strcmp(str,"none"))
00387         return PIC_irq_hack_none;
00388     else if (!strcmp(str,"cs_equ_ds"))
00389         return PIC_irq_hack_cs_equ_ds;
00390 
00391     return PIC_irq_hack_none;
00392 }
00393 
00394 static bool IRQ_hack_check_cs_equ_ds(const int IRQ) {
00395     uint16_t s_cs = SegValue(cs);
00396     uint16_t s_ds = SegValue(ds);
00397 
00398     if (s_cs >= 0xA000)
00399         return true; // don't complain about the BIOS ISR
00400 
00401     if (s_cs != s_ds) {
00402         LOG(LOG_PIC,LOG_DEBUG)("Not dispatching IRQ %d according to IRQ hack. CS != DS",IRQ);
00403         return false;
00404     }
00405 
00406     return true;
00407 }
00408 
00409 static void slave_startIRQ(){
00410     Bit8u pic1_irq = 8;
00411     const Bit8u p = (slave.irr & slave.imrr)&slave.isrr;
00412     const Bit8u max = slave.special?8:slave.active_irq;
00413     for(Bit8u i = 0,s = 1;i < max;i++, s<<=1) {
00414         if (p&s) {
00415             if (PIC_IRQ_hax[i+8] == PIC_irq_hack_cs_equ_ds)
00416                 if (!IRQ_hack_check_cs_equ_ds(i+8))
00417                     continue; // skip IRQ
00418 
00419             pic1_irq = i;
00420             break;
00421         }
00422     }
00423 
00424     if (GCC_UNLIKELY(pic1_irq == 8)) {
00425         /* we have an IRQ routing problem. this code is supposed to emulate the fact that
00426          * what was once IRQ 2 on PC/XT is routed to IRQ 9 on AT systems, because IRQ 8-15
00427          * cascade to IRQ 2 on such systems. but it's nothing to E_Exit() over. */
00428         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);
00429         slave.lower_irq(master_cascade_irq); /* clear it */
00430         return;
00431     }
00432 
00433     slave.start_irq(pic1_irq);
00434     master.start_irq(master_cascade_irq);
00435     CPU_HW_Interrupt((unsigned int)slave.vector_base + (unsigned int)pic1_irq);
00436 }
00437 
00438 static void inline master_startIRQ(Bitu i){
00439     master.start_irq(i);
00440     CPU_HW_Interrupt(master.vector_base + i);
00441 }
00442 
00443 void PIC_runIRQs(void) {
00444     if (!GETFLAG(IF)) return;
00445     if (GCC_UNLIKELY(!PIC_IRQCheck)) return;
00446     if (GCC_UNLIKELY(cpudecoder==CPU_Core_Normal_Trap_Run)) return; // FIXME: Why?
00447     if (GCC_UNLIKELY(CPU_NMI_active) || GCC_UNLIKELY(CPU_NMI_pending)) return; /* NMI has higher priority than PIC */
00448 
00449     const Bit8u p = (master.irr & master.imrr)&master.isrr;
00450     const Bit8u max = master.special?8:master.active_irq;
00451     Bit8u i,s;
00452 
00453     for (i = 0,s = 1;i < max;i++, s<<=1){
00454         if (p&s) {
00455             if (PIC_IRQ_hax[i] == PIC_irq_hack_cs_equ_ds)
00456                 if (!IRQ_hack_check_cs_equ_ds(i))
00457                     continue; // skip IRQ
00458 
00459             if ((int)i == master_cascade_irq) { //second pic, or will not match if master_cascade_irq == -1
00460                 slave_startIRQ();
00461             } else {
00462                 master_startIRQ(i);
00463             }
00464             break;
00465         }
00466     }
00467 
00468     /* if we cleared all IRQs, then stop checking.
00469      * otherwise, keep the flag set for the next IRQ to process. */
00470     if (i == max && (master.irr&master.imrr) == 0) {
00471         PIC_IRQCheckPending = 0;
00472         PIC_IRQCheck = 0;
00473     }
00474     else if (PIC_IRQCheck) {
00475         PIC_AddEvent(PIC_IRQCheckDelayed,(double)PIC_irq_delay_ns / 1000000,0);
00476         PIC_IRQCheckPending = 1;
00477         PIC_IRQCheck = 0;
00478     }
00479 }
00480 
00481 void PIC_SetIRQMask(Bitu irq, bool masked) {
00482     Bitu t = irq>7 ? (irq - 8): irq;
00483     PIC_Controller * pic=&pics[irq>7 ? 1 : 0];
00484     //clear bit
00485     Bit8u bit = 1 <<(t);
00486     Bit8u newmask = pic->imr;
00487     newmask &= ~bit;
00488     if (masked) newmask |= bit;
00489     pic->set_imr(newmask);
00490 }
00491 
00492 void DEBUG_PICSignal(int irq,bool raise) {
00493     if (irq >= 0 && irq <= 15) {
00494         if (raise)
00495             PIC_ActivateIRQ((unsigned int)irq);
00496         else
00497             PIC_DeActivateIRQ((unsigned int)irq);
00498     }
00499 }
00500 
00501 void DEBUG_PICAck(int irq) {
00502     if (irq >= 0 && irq <= 15) {
00503         PIC_Controller * pic=&pics[irq>7 ? 1 : 0];
00504 
00505         pic->isr &= ~(1u << ((unsigned int)irq & 7U));
00506         pic->isrr = ~pic->isr;
00507         pic->check_after_EOI();
00508     }
00509 }
00510 
00511 void DEBUG_PICMask(int irq,bool mask) {
00512     if (irq >= 0 && irq <= 15)
00513         PIC_SetIRQMask((unsigned int)irq,mask);
00514 }
00515 
00516 static void AddEntry(PICEntry * entry) {
00517     PICEntry * find_entry=pic_queue.next_entry;
00518     if (GCC_UNLIKELY(find_entry ==0)) {
00519         entry->next=0;
00520         pic_queue.next_entry=entry;
00521     } else if (find_entry->index>entry->index) {
00522         pic_queue.next_entry=entry;
00523         entry->next=find_entry;
00524     } else while (find_entry) {
00525         if (find_entry->next) {
00526             /* See if the next index comes later than this one */
00527             if (find_entry->next->index > entry->index) {
00528                 entry->next=find_entry->next;
00529                 find_entry->next=entry;
00530                 break;
00531             } else {
00532                 find_entry=find_entry->next;
00533             }
00534         } else {
00535             entry->next=find_entry->next;
00536             find_entry->next=entry;
00537             break;
00538         }
00539     }
00540     Bits cycles=PIC_MakeCycles(pic_queue.next_entry->index-PIC_TickIndex());
00541     if (cycles<CPU_Cycles) {
00542         CPU_CycleLeft+=CPU_Cycles;
00543         CPU_Cycles=0;
00544     }
00545 }
00546 
00547 static bool InEventService = false;
00548 static pic_tickindex_t srv_lag = 0;
00549 
00550 void PIC_AddEvent(PIC_EventHandler handler,pic_tickindex_t delay,Bitu val) {
00551     if (GCC_UNLIKELY(!pic_queue.free_entry)) {
00552         LOG(LOG_PIC,LOG_ERROR)("Event queue full");
00553         return;
00554     }
00555     PICEntry * entry=pic_queue.free_entry;
00556     if(InEventService) entry->index = delay + srv_lag;
00557     else entry->index = delay + PIC_TickIndex();
00558 
00559     entry->pic_event=handler;
00560     entry->value=val;
00561     pic_queue.free_entry=pic_queue.free_entry->next;
00562     AddEntry(entry);
00563 }
00564 
00565 void PIC_RemoveSpecificEvents(PIC_EventHandler handler, Bitu val) {
00566     PICEntry * entry=pic_queue.next_entry;
00567     PICEntry * prev_entry;
00568     prev_entry = 0;
00569     while (entry) {
00570         if (GCC_UNLIKELY((entry->pic_event == handler)) && (entry->value == val)) {
00571             if (prev_entry) {
00572                 prev_entry->next=entry->next;
00573                 entry->next=pic_queue.free_entry;
00574                 pic_queue.free_entry=entry;
00575                 entry=prev_entry->next;
00576                 continue;
00577             } else {
00578                 pic_queue.next_entry=entry->next;
00579                 entry->next=pic_queue.free_entry;
00580                 pic_queue.free_entry=entry;
00581                 entry=pic_queue.next_entry;
00582                 continue;
00583             }
00584         }
00585         prev_entry=entry;
00586         entry=entry->next;
00587     }   
00588 }
00589 
00590 void PIC_RemoveEvents(PIC_EventHandler handler) {
00591     PICEntry * entry=pic_queue.next_entry;
00592     PICEntry * prev_entry;
00593     prev_entry=0;
00594     while (entry) {
00595         if (GCC_UNLIKELY(entry->pic_event==handler)) {
00596             if (prev_entry) {
00597                 prev_entry->next=entry->next;
00598                 entry->next=pic_queue.free_entry;
00599                 pic_queue.free_entry=entry;
00600                 entry=prev_entry->next;
00601                 continue;
00602             } else {
00603                 pic_queue.next_entry=entry->next;
00604                 entry->next=pic_queue.free_entry;
00605                 pic_queue.free_entry=entry;
00606                 entry=pic_queue.next_entry;
00607                 continue;
00608             }
00609         }
00610         prev_entry=entry;
00611         entry=entry->next;
00612     }   
00613 }
00614 
00615 extern ClockDomain clockdom_DOSBox_cycles;
00616 
00617 bool PIC_RunQueue(void) {
00618     /* Check to see if a new millisecond needs to be started */
00619     CPU_CycleLeft += CPU_Cycles;
00620     CPU_Cycles = 0;
00621 
00622     if (CPU_CycleLeft > 0) {
00623         if (PIC_IRQCheck)
00624             PIC_runIRQs();
00625 
00626         /* Check the queue for an entry */
00627         Bits index_nd=PIC_TickIndexND();
00628         InEventService = true;
00629         while (pic_queue.next_entry && (pic_queue.next_entry->index*CPU_CycleMax<=index_nd)) {
00630             PICEntry * entry=pic_queue.next_entry;
00631             pic_queue.next_entry=entry->next;
00632 
00633             srv_lag = entry->index;
00634             (entry->pic_event)(entry->value); // call the event handler
00635 
00636             /* Put the entry in the free list */
00637             entry->next=pic_queue.free_entry;
00638             pic_queue.free_entry=entry;
00639         }
00640         InEventService = false;
00641 
00642         /* Check when to set the new cycle end */
00643         if (pic_queue.next_entry) {
00644             Bits cycles=(Bits)(pic_queue.next_entry->index*CPU_CycleMax-index_nd);
00645             if (GCC_UNLIKELY(!cycles)) cycles=1;
00646             if (cycles<CPU_CycleLeft) {
00647                 CPU_Cycles=cycles;
00648             } else {
00649                 CPU_Cycles=CPU_CycleLeft;
00650             }
00651         } else CPU_Cycles=CPU_CycleLeft;
00652         CPU_CycleLeft-=CPU_Cycles;
00653 
00654         if (PIC_IRQCheck)
00655             PIC_runIRQs();
00656     }
00657 
00658     /* if we're out of cycles, then return false. don't execute any more instructions */
00659     if ((CPU_CycleLeft+CPU_Cycles) <= 0)
00660         return false;
00661 
00662     return true;
00663 }
00664 
00665 /* The TIMER Part */
00666 struct TickerBlock {
00667     /* TODO: carry const char * field for name! */
00668     TIMER_TickHandler handler;
00669     TickerBlock * next;
00670 };
00671 
00672 static TickerBlock * firstticker=0;
00673 
00674 void TIMER_ShutdownTickHandlers() {
00675     unsigned int leftovers = 0;
00676 
00677     /* pull in the singly linked list from the front, hand over hand */
00678     while (firstticker != NULL) {
00679         TickerBlock *n = firstticker->next;
00680         delete firstticker;
00681         firstticker = n;
00682         leftovers++;
00683     }
00684 
00685     if (leftovers != 0)
00686         LOG(LOG_MISC,LOG_DEBUG)("TIMER: %u leftover handlers (clean up!).",leftovers);
00687 }
00688 
00689 void TIMER_DelTickHandler(TIMER_TickHandler handler) {
00690     TickerBlock * ticker=firstticker;
00691     TickerBlock * * tick_where=&firstticker;
00692     while (ticker) {
00693         if (ticker->handler==handler) {
00694             *tick_where=ticker->next;
00695             delete ticker;
00696             return;
00697         }
00698         tick_where=&ticker->next;
00699         ticker=ticker->next;
00700     }
00701 }
00702 
00703 void TIMER_AddTickHandler(TIMER_TickHandler handler) {
00704     TickerBlock * newticker=new TickerBlock;
00705     newticker->next=firstticker;
00706     newticker->handler=handler;
00707     firstticker=newticker;
00708 }
00709 
00710 extern Bitu time_limit_ms;
00711 
00712 static unsigned long PIC_benchstart = 0;
00713 static unsigned long PIC_tickstart = 0;
00714 
00715 extern void GFX_SetTitle(Bit32s cycles, Bits frameskip, Bits timing, bool paused);
00716 void TIMER_AddTick(void) {
00717     /* Setup new amount of cycles for PIC */
00718     PIC_Ticks++;
00719     if ((PIC_Ticks&0x3fff) == 0) {
00720         unsigned long ticks = GetTicks();
00721         int delta = (PIC_Ticks-PIC_tickstart)*10000/(ticks-PIC_benchstart)+5;
00722         GFX_SetTitle(-1,-1,delta,false);
00723         PIC_benchstart = ticks;
00724         PIC_tickstart = PIC_Ticks;
00725     }
00726     CPU_CycleLeft += CPU_CycleMax + CPU_Cycles;
00727     CPU_Cycles = 0;
00728 
00729     /* timeout */
00730     if (time_limit_ms != 0 && PIC_Ticks >= time_limit_ms)
00731         throw int(1);
00732 
00733     /* Go through the list of scheduled events and lower their index with 1000 */
00734     PICEntry * entry=pic_queue.next_entry;
00735     while (entry) {
00736         entry->index -= 1.0;
00737         entry=entry->next;
00738     }
00739 
00740     /* Call our list of ticker handlers */
00741     TickerBlock * ticker=firstticker;
00742     while (ticker) {
00743         TickerBlock * nextticker=ticker->next;
00744         ticker->handler();
00745         ticker=nextticker;
00746     }
00747 }
00748 
00749 static IO_WriteHandleObject PCXT_NMI_WriteHandler;
00750 
00751 static IO_ReadHandleObject ReadHandler[4];
00752 static IO_WriteHandleObject WriteHandler[4];
00753 
00754 void PIC_Reset(Section *sec) {
00755     (void)sec;//UNUSED
00756     Bitu i;
00757 
00758     ReadHandler[0].Uninstall();
00759     ReadHandler[1].Uninstall();
00760     WriteHandler[0].Uninstall();
00761     WriteHandler[1].Uninstall();
00762     ReadHandler[2].Uninstall();
00763     ReadHandler[3].Uninstall();
00764     WriteHandler[2].Uninstall();
00765     WriteHandler[3].Uninstall();
00766     PCXT_NMI_WriteHandler.Uninstall();
00767 
00768     /* NTS: Parsing this on reset allows PIC configuration changes on reboot instead of restarting the entire emulator */
00769     Section_prop * section=static_cast<Section_prop *>(control->GetSection("dosbox"));
00770     assert(section != NULL);
00771 
00772     enable_slave_pic = section->Get_bool("enable slave pic");
00773     enable_pc_xt_nmi_mask = section->Get_bool("enable pc nmi mask");
00774 
00775     /* NTS: This is a good guess. But the 8259 is static circuitry and not driven by a clock.
00776      *      But the ability to respond to interrupts is limited by the CPU, too. */
00777     PIC_irq_delay_ns = 1000000000UL / (unsigned long)PIT_TICK_RATE;
00778     {
00779         int x = section->Get_int("irq delay ns");
00780         if (x >= 0) PIC_irq_delay_ns = (unsigned int)x;
00781     }
00782 
00783     if (enable_slave_pic)
00784         master_cascade_irq = IS_PC98_ARCH ? 7 : 2;
00785     else
00786         master_cascade_irq = -1;
00787 
00788     // LOG
00789     LOG(LOG_MISC,LOG_DEBUG)("PIC_Reset(): reinitializing PIC controller (cascade=%d)",master_cascade_irq);
00790 
00791     /* Setup pic0 and pic1 with initial values like DOS has normally */
00792     PIC_Ticks=0;
00793     PIC_IRQCheck=0;
00794     for (i=0;i<2;i++) {
00795         pics[i].auto_eoi=false;
00796         pics[i].rotate_on_auto_eoi=false;
00797         pics[i].request_issr=false;
00798         pics[i].special=false;
00799         pics[i].single=false;
00800         pics[i].icw_index=0;
00801         pics[i].icw_words=0;
00802         pics[i].irr = pics[i].isr = pics[i].imrr = 0;
00803         pics[i].isrr = pics[i].imr = 0xff;
00804         pics[i].active_irq = 8;
00805     }
00806 
00807     /* IBM: IRQ 0-15 is INT 0x08-0x0F, 0x70-0x7F
00808      * PC-98: IRQ 0-15 is INT 0x08-0x17 */
00809     master.vector_base = 0x08;
00810     slave.vector_base = IS_PC98_ARCH ? 0x10 : 0x70;
00811 
00812     for (Bitu i=0;i < 16;i++)
00813         PIC_SetIRQMask(i,true);
00814 
00815     PIC_SetIRQMask(0,false);                    /* Enable system timer */
00816     PIC_SetIRQMask(1,false);                    /* Enable system timer */
00817     PIC_SetIRQMask(8,false);                    /* Enable RTC IRQ */
00818 
00819     if (master_cascade_irq >= 0)
00820         PIC_SetIRQMask((unsigned int)master_cascade_irq,false);/* Enable second pic */
00821 
00822     /* I/O port map
00823      *
00824      * IBM PC/XT/AT     NEC PC-98        A0
00825      * ---------------------------------------
00826      * 0x20             0x00             0
00827      * 0x21             0x02             1
00828      * 0xA0             0x08             0
00829      * 0xA1             0x0A             1
00830      */
00831 
00832     ReadHandler[0].Install(IS_PC98_ARCH ? 0x00 : 0x20,read_command,IO_MB);
00833     ReadHandler[1].Install(IS_PC98_ARCH ? 0x02 : 0x21,read_data,IO_MB);
00834     WriteHandler[0].Install(IS_PC98_ARCH ? 0x00 : 0x20,write_command,IO_MB);
00835     WriteHandler[1].Install(IS_PC98_ARCH ? 0x02 : 0x21,write_data,IO_MB);
00836 
00837     /* the secondary slave PIC takes priority over PC/XT NMI mask emulation */
00838     if (enable_slave_pic) {
00839         ReadHandler[2].Install(IS_PC98_ARCH ? 0x08 : 0xa0,read_command,IO_MB);
00840         ReadHandler[3].Install(IS_PC98_ARCH ? 0x0A : 0xa1,read_data,IO_MB);
00841         WriteHandler[2].Install(IS_PC98_ARCH ? 0x08 : 0xa0,write_command,IO_MB);
00842         WriteHandler[3].Install(IS_PC98_ARCH ? 0x0A : 0xa1,write_data,IO_MB);
00843     }
00844     else if (!IS_PC98_ARCH && enable_pc_xt_nmi_mask) {
00845         PCXT_NMI_WriteHandler.Install(0xa0,pc_xt_nmi_write,IO_MB);
00846     }
00847 }
00848 
00849 void PIC_Destroy(Section* sec) {
00850     (void)sec;//UNUSED
00851 }
00852 
00853 void Init_PIC() {
00854     Bitu i;
00855 
00856     LOG(LOG_MISC,LOG_DEBUG)("Init_PIC()");
00857 
00858     /* Initialize the pic queue */
00859     for (i=0;i<PIC_QUEUESIZE-1;i++) {
00860         pic_queue.entries[i].next=&pic_queue.entries[i+1];
00861 
00862         // savestate compatibility
00863         pic_queue.entries[i].pic_event = 0;
00864     }
00865     pic_queue.entries[PIC_QUEUESIZE-1].next=0;
00866     pic_queue.free_entry=&pic_queue.entries[0];
00867     pic_queue.next_entry=0;
00868 
00869     AddExitFunction(AddExitFunctionFuncPair(PIC_Destroy));
00870     AddVMEventFunction(VM_EVENT_RESET,AddVMEventFunctionFuncPair(PIC_Reset));
00871 }
00872 
00873 #if C_DEBUG
00874 void DEBUG_LogPIC_C(PIC_Controller &pic) {
00875     LOG_MSG("%s interrupt controller state",&pic == &master ? "Master" : "Slave");
00876     LOG_MSG("ICW %u/%u special=%u auto-eoi=%u rotate-eoi=%u single=%u request_issr=%u vectorbase=0x%02x active_irq=%u",
00877         (unsigned int)pic.icw_index,
00878         (unsigned int)pic.icw_words,
00879         pic.special?1:0,
00880         pic.auto_eoi?1:0,
00881         pic.rotate_on_auto_eoi?1:0,
00882         pic.single?1:0,
00883         pic.request_issr?1:0,
00884         pic.vector_base,
00885         pic.active_irq);
00886 
00887     LOG_MSG("IRQ INT#  Req /Mask/Serv");
00888     for (unsigned int si=0;si < 8;si++) {
00889         unsigned int IRQ = si + (&pic == &slave ? 8 : 0);
00890         unsigned int CPUINT = pic.vector_base + si;
00891 
00892         LOG_MSG("%3u 0x%02X   %c    %c    %c   %s",
00893             IRQ,
00894             CPUINT,
00895             (pic.irr & (1U << si))?'R':' ',
00896             (pic.imr & (1U << si))?'M':' ',
00897             (pic.isr & (1U << si))?'S':' ',
00898             ((unsigned int)IRQ == (unsigned int)master_cascade_irq) ? "CASCADE" : "");
00899     }
00900 }
00901 
00902 void DEBUG_LogPIC(void) {
00903     DEBUG_LogPIC_C(master);
00904     if (enable_slave_pic) DEBUG_LogPIC_C(slave);
00905 }
00906 #endif
00907 
00908