DOSBox-X
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Defines
src/hardware/pic.cpp
00001 /*
00002  *  Copyright (C) 2002-2020  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 along
00015  *  with this program; if not, write to the Free Software Foundation, Inc.,
00016  *  51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, 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     bool IsDebuggerRunwatch(void);
00707     if (IsDebuggerActive() && !IsDebuggerRunwatch())
00708         return false;
00709 #endif
00710 #ifdef DEBUG_CPU_CYCLE_OVERRUN
00711     /* I/O delay can cause negative CPU_Cycles and PIC event / audio rendering issues */
00712     cpu_cycles_count_t overrun = -std::min(CPU_Cycles,(cpu_cycles_count_t)0);
00713 
00714     if (overrun > (CPU_CycleMax/100))
00715         LOG_MSG("PIC_RunQueue: CPU cycles count overrun by %ld (%.3fms)\n",(signed long)overrun,(double)overrun / CPU_CycleMax);
00716 #endif
00717 
00718     /* Check to see if a new millisecond needs to be started */
00719     CPU_CycleLeft += CPU_Cycles;
00720     CPU_Cycles = 0;
00721 
00722 #ifdef DEBUG_PIC_IRQCHECK_VS_IRR
00723     // WARNING: If the problem is the cascade interrupt un-acknowledged, this will give a false positive
00724     if (!PIC_IRQCheck && !PIC_IRQCheckPending && ((master.irr&master.imrr) != 0 || (slave.irr&slave.imrr) != 0))
00725         LOG_MSG("PIC_IRQCheck not set and interrupts pending");
00726 #endif
00727 
00728     if (CPU_CycleLeft > 0) {
00729         if (PIC_IRQCheck)
00730             PIC_runIRQs();
00731 
00732         /* Check the queue for an entry */
00733         Bits index_nd=PIC_TickIndexND();
00734         InEventService = true;
00735         while (pic_queue.next_entry && (pic_queue.next_entry->index*CPU_CycleMax<=index_nd)) {
00736             PICEntry * entry=pic_queue.next_entry;
00737             pic_queue.next_entry=entry->next;
00738             srv_lag = entry->index;
00739 
00740             if (entry->pic_event != NULL)
00741                 (entry->pic_event)(entry->value); // call the event handler
00742             else
00743                 LOG(LOG_MISC,LOG_WARN)("PIC: Event in queue with NULL handler"); // This can happen after save state / load state
00744 
00745             /* Put the entry in the free list */
00746             entry->next=pic_queue.free_entry;
00747             pic_queue.free_entry=entry;
00748         }
00749         InEventService = false;
00750 
00751         /* Check when to set the new cycle end */
00752         if (pic_queue.next_entry) {
00753             Bits cycles=(Bits)(pic_queue.next_entry->index*CPU_CycleMax-index_nd);
00754             if (GCC_UNLIKELY(!cycles)) cycles=1;
00755             if (cycles<CPU_CycleLeft) {
00756                 CPU_Cycles=cycles;
00757             } else {
00758                 CPU_Cycles=CPU_CycleLeft;
00759             }
00760         } else CPU_Cycles=CPU_CycleLeft;
00761         CPU_CycleLeft-=CPU_Cycles;
00762 
00763         if (PIC_IRQCheck)
00764             PIC_runIRQs();
00765     }
00766 
00767     /* if we're out of cycles, then return false. don't execute any more instructions */
00768     if ((CPU_CycleLeft+CPU_Cycles) <= 0)
00769         return false;
00770 
00771     return true;
00772 }
00773 
00774 /* The TIMER Part */
00775 struct TickerBlock {
00776     /* TODO: carry const char * field for name! */
00777     TIMER_TickHandler handler;
00778     TickerBlock * next;
00779 };
00780 
00781 static TickerBlock * firstticker=0;
00782 
00783 void TIMER_ShutdownTickHandlers() {
00784     unsigned int leftovers = 0;
00785 
00786     /* pull in the singly linked list from the front, hand over hand */
00787     while (firstticker != NULL) {
00788         TickerBlock *n = firstticker->next;
00789         delete firstticker;
00790         firstticker = n;
00791         leftovers++;
00792     }
00793 
00794     if (leftovers != 0)
00795         LOG(LOG_MISC,LOG_DEBUG)("TIMER: %u leftover handlers (clean up!).",leftovers);
00796 }
00797 
00798 void TIMER_DelTickHandler(TIMER_TickHandler handler) {
00799     TickerBlock * ticker=firstticker;
00800     TickerBlock * * tick_where=&firstticker;
00801     while (ticker) {
00802         if (ticker->handler==handler) {
00803             *tick_where=ticker->next;
00804             delete ticker;
00805             return;
00806         }
00807         tick_where=&ticker->next;
00808         ticker=ticker->next;
00809     }
00810 }
00811 
00812 void TIMER_AddTickHandler(TIMER_TickHandler handler) {
00813     TickerBlock * newticker=new TickerBlock;
00814     newticker->next=firstticker;
00815     newticker->handler=handler;
00816     firstticker=newticker;
00817 }
00818 
00819 extern Bitu time_limit_ms;
00820 
00821 static unsigned long PIC_benchstart = 0;
00822 static unsigned long PIC_tickstart = 0;
00823 
00824 extern void GFX_SetTitle(Bit32s cycles, Bits frameskip, Bits timing, bool paused);
00825 void TIMER_AddTick(void) {
00826     /* Setup new amount of cycles for PIC */
00827     PIC_Ticks++;
00828     if ((PIC_Ticks&0x3fff) == 0) {
00829         unsigned long ticks = GetTicks();
00830         int delta = int((PIC_Ticks-PIC_tickstart)*10000/(ticks-PIC_benchstart)+5);
00831         GFX_SetTitle(-1,-1,delta,false);
00832         PIC_benchstart = ticks;
00833         PIC_tickstart = PIC_Ticks;
00834     }
00835     CPU_CycleLeft += CPU_CycleMax + CPU_Cycles;
00836     CPU_Cycles = 0;
00837 
00838     /* timeout */
00839     if (time_limit_ms != 0 && PIC_Ticks >= time_limit_ms)
00840         throw int(1);
00841 
00842     /* Go through the list of scheduled events and lower their index with 1000 */
00843     PICEntry * entry=pic_queue.next_entry;
00844     while (entry) {
00845         entry->index -= 1.0;
00846         entry=entry->next;
00847     }
00848 
00849     /* Call our list of ticker handlers */
00850     TickerBlock * ticker=firstticker;
00851     while (ticker) {
00852         TickerBlock * nextticker=ticker->next;
00853         ticker->handler();
00854         ticker=nextticker;
00855     }
00856 }
00857 
00858 static IO_WriteHandleObject PCXT_NMI_WriteHandler;
00859 
00860 static IO_ReadHandleObject ReadHandler[4];
00861 static IO_WriteHandleObject WriteHandler[4];
00862 
00863 void PIC_Reset(Section *sec) {
00864     (void)sec;//UNUSED
00865 
00866     ReadHandler[0].Uninstall();
00867     ReadHandler[1].Uninstall();
00868     WriteHandler[0].Uninstall();
00869     WriteHandler[1].Uninstall();
00870     ReadHandler[2].Uninstall();
00871     ReadHandler[3].Uninstall();
00872     WriteHandler[2].Uninstall();
00873     WriteHandler[3].Uninstall();
00874     PCXT_NMI_WriteHandler.Uninstall();
00875 
00876     /* NTS: Parsing this on reset allows PIC configuration changes on reboot instead of restarting the entire emulator */
00877     Section_prop * section=static_cast<Section_prop *>(control->GetSection("dosbox"));
00878     assert(section != NULL);
00879 
00880     enable_slave_pic = section->Get_bool("enable slave pic");
00881     enable_pc_xt_nmi_mask = section->Get_bool("enable pc nmi mask");
00882     never_mark_cascade_in_service = section->Get_bool("cascade interrupt never in service");
00883 
00884     {
00885         std::string x = section->Get_string("cascade interrupt ignore in service");
00886 
00887         if (x == "1" || x == "true")
00888             ignore_cascade_in_service = true;
00889         else if (x == "0" || x == "false")
00890             ignore_cascade_in_service = false;
00891         else {
00892             // auto
00893             if (IS_PC98_ARCH)
00894                 ignore_cascade_in_service = true;
00895             else
00896                 ignore_cascade_in_service = false;
00897         }
00898 
00899         LOG(LOG_MISC,LOG_DEBUG)("PIC: Ignore cascade in service=%u",ignore_cascade_in_service);
00900     }
00901 
00902     if (enable_slave_pic && machine == MCH_PCJR && enable_pc_xt_nmi_mask) {
00903         LOG(LOG_MISC,LOG_DEBUG)("PIC_Reset(): PCjr emulation with NMI mask register requires disabling slave PIC (IRQ 8-15)");
00904         enable_slave_pic = false;
00905     }
00906 
00907     if (!enable_slave_pic && IS_PC98_ARCH)
00908         LOG(LOG_MISC,LOG_DEBUG)("PIC_Reset(): PC-98 emulation without slave PIC (IRQ 8-15) is unusual");
00909 
00910     /* NTS: This is a good guess. But the 8259 is static circuitry and not driven by a clock.
00911      *      But the ability to respond to interrupts is limited by the CPU, too. */
00912     PIC_irq_delay_ns = 1000000000UL / (unsigned long)PIT_TICK_RATE;
00913     {
00914         int x = section->Get_int("irq delay ns");
00915         if (x >= 0) PIC_irq_delay_ns = (unsigned int)x;
00916     }
00917 
00918     if (enable_slave_pic)
00919         master_cascade_irq = IS_PC98_ARCH ? 7 : 2;
00920     else
00921         master_cascade_irq = -1;
00922 
00923     // LOG
00924     LOG(LOG_MISC,LOG_DEBUG)("PIC_Reset(): reinitializing PIC controller (cascade=%d)",master_cascade_irq);
00925 
00926     /* Setup pic0 and pic1 with initial values like DOS has normally */
00927     PIC_Ticks=0;
00928     PIC_IRQCheck=0;
00929     for (Bitu i=0;i<2;i++) {
00930         pics[i].auto_eoi=false;
00931         pics[i].rotate_on_auto_eoi=false;
00932         pics[i].request_issr=false;
00933         pics[i].special=false;
00934         pics[i].single=false;
00935         pics[i].icw_index=0;
00936         pics[i].icw_words=0;
00937         pics[i].irr = pics[i].isr = pics[i].imrr = 0;
00938         pics[i].isrr = pics[i].imr = 0xff;
00939         pics[i].isr_ignore = 0x00;
00940         pics[i].active_irq = 8;
00941     }
00942 
00943     /* PC-98: By default (but an option otherwise)
00944      *        initialize the PIC so that reading the command port
00945      *        produces the ISR status not the IRR status.
00946      *
00947      *        The reason the option is on by default, is that there
00948      *        is PC-98 programming literature that recommends reading
00949      *        ISR status before acknowledging interrupts (to avoid
00950      *        conflicts with other ISR handlers perhaps). So it's
00951      *        probably a common convention.
00952      *
00953      * Notes: "Blackbird" by Vivian needs this in order for the FM interrupt
00954      *        to continue working. A bug in the FM interrupt routine programs
00955      *        only the master PIC into this mode but then reads from the slave
00956      *        which is not necessarily initialized into this mode and may return
00957      *        the IRR register instead, causing the game to misinterpret
00958      *        incoming interrupts as in-service. */
00959     if (IS_PC98_ARCH) {
00960                 Section_prop * pc98_section = static_cast<Section_prop *>(control->GetSection("pc98"));
00961                 if (pc98_section != NULL && pc98_section->Get_bool("pc-98 pic init to read isr"))
00962                         pics[0].request_issr = pics[1].request_issr = true;
00963         }
00964 
00965     /* IBM: IRQ 0-15 is INT 0x08-0x0F, 0x70-0x7F
00966      * PC-98: IRQ 0-15 is INT 0x08-0x17 */
00967     master.vector_base = 0x08;
00968     slave.vector_base = IS_PC98_ARCH ? 0x10 : 0x70;
00969 
00970     for (Bitu i=0;i < 16;i++)
00971         PIC_SetIRQMask(i,true);
00972 
00973     PIC_SetIRQMask(0,false);                    /* Enable system timer */
00974     PIC_SetIRQMask(1,false);                    /* Enable keyboard interrupt */
00975     PIC_SetIRQMask(8,false);                    /* Enable RTC IRQ */
00976 
00977     if (master_cascade_irq >= 0) {
00978         PIC_SetIRQMask((unsigned int)master_cascade_irq,false);/* Enable second pic */
00979 
00980         if (ignore_cascade_in_service)
00981             pics[0].isr_ignore |= 1u << (unsigned char)master_cascade_irq;
00982     }
00983 
00984     /* I/O port map
00985      *
00986      * IBM PC/XT/AT     NEC PC-98        A0
00987      * ---------------------------------------
00988      * 0x20             0x00             0
00989      * 0x21             0x02             1
00990      * 0xA0             0x08             0
00991      * 0xA1             0x0A             1
00992      */
00993 
00994     ReadHandler[0].Install(IS_PC98_ARCH ? 0x00 : 0x20,read_command,IO_MB);
00995     ReadHandler[1].Install(IS_PC98_ARCH ? 0x02 : 0x21,read_data,IO_MB);
00996     WriteHandler[0].Install(IS_PC98_ARCH ? 0x00 : 0x20,write_command,IO_MB);
00997     WriteHandler[1].Install(IS_PC98_ARCH ? 0x02 : 0x21,write_data,IO_MB);
00998 
00999     /* the secondary slave PIC takes priority over PC/XT NMI mask emulation */
01000     if (enable_slave_pic) {
01001         ReadHandler[2].Install(IS_PC98_ARCH ? 0x08 : 0xa0,read_command,IO_MB);
01002         ReadHandler[3].Install(IS_PC98_ARCH ? 0x0A : 0xa1,read_data,IO_MB);
01003         WriteHandler[2].Install(IS_PC98_ARCH ? 0x08 : 0xa0,write_command,IO_MB);
01004         WriteHandler[3].Install(IS_PC98_ARCH ? 0x0A : 0xa1,write_data,IO_MB);
01005     }
01006     else if (!IS_PC98_ARCH && enable_pc_xt_nmi_mask) {
01007         PCXT_NMI_WriteHandler.Install(0xa0,pc_xt_nmi_write,IO_MB);
01008     }
01009 }
01010 
01011 void PIC_Destroy(Section* sec) {
01012     (void)sec;//UNUSED
01013 }
01014 
01015 void Init_PIC() {
01016     Bitu i;
01017 
01018     LOG(LOG_MISC,LOG_DEBUG)("Init_PIC()");
01019 
01020     /* Initialize the pic queue */
01021     for (i=0;i<PIC_QUEUESIZE-1;i++) {
01022         pic_queue.entries[i].next=&pic_queue.entries[i+1];
01023 
01024         // savestate compatibility
01025         pic_queue.entries[i].pic_event = 0;
01026     }
01027     pic_queue.entries[PIC_QUEUESIZE-1].next=0;
01028     pic_queue.free_entry=&pic_queue.entries[0];
01029     pic_queue.next_entry=0;
01030 
01031     AddExitFunction(AddExitFunctionFuncPair(PIC_Destroy));
01032     AddVMEventFunction(VM_EVENT_RESET,AddVMEventFunctionFuncPair(PIC_Reset));
01033 }
01034 
01035 #if C_DEBUG
01036 void DEBUG_LogPIC_C(PIC_Controller &pic) {
01037     LOG_MSG("%s interrupt controller state",&pic == &master ? "Master" : "Slave");
01038     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",
01039         (unsigned int)pic.icw_index,
01040         (unsigned int)pic.icw_words,
01041         pic.special?1:0,
01042         pic.auto_eoi?1:0,
01043         pic.rotate_on_auto_eoi?1:0,
01044         pic.single?1:0,
01045         pic.request_issr?1:0,
01046         pic.vector_base,
01047         pic.active_irq,
01048         pic.isr,
01049         pic.isrr,
01050         pic.isr_ignore);
01051 
01052     LOG_MSG("IRQ INT#  Req /Mask/Serv");
01053     for (unsigned int si=0;si < 8;si++) {
01054         unsigned int IRQ = si + (&pic == &slave ? 8 : 0);
01055         unsigned int CPUINT = pic.vector_base + si;
01056 
01057         LOG_MSG("%3u 0x%02X   %c    %c    %c   %s",
01058             IRQ,
01059             CPUINT,
01060             (pic.irr & (1U << si))?'R':' ',
01061             (pic.imr & (1U << si))?'M':' ',
01062             (pic.isr & (1U << si))?'S':' ',
01063             ((unsigned int)IRQ == (unsigned int)master_cascade_irq) ? "CASCADE" : "");
01064     }
01065 }
01066 
01067 void DEBUG_LogPIC(void) {
01068     DEBUG_LogPIC_C(master);
01069     if (enable_slave_pic) DEBUG_LogPIC_C(slave);
01070 }
01071 #endif
01072 
01073 // PIC_EventHandlers
01074 extern void *cmos_timerevent_PIC_Event;                                         // Cmos.cpp
01075 extern void *DISNEY_disable_PIC_Event;                                          // Disney.cpp
01076 extern void *GUS_TimerEvent_PIC_Event;                                          // Gus.cpp
01077 #if C_IPX
01078 //extern void *IPX_AES_EventHandler_PIC_Event;                  // Ipx.cpp
01079 #endif
01080 extern void *KEYBOARD_TransferBuffer_PIC_Event;         // Keyboard.cpp
01081 extern void *MOUSE_Limit_Events_PIC_Event;                              // Mouse.cpp
01082 extern void *MPU401_Event_PIC_Event;                                                    // Mpu401.cpp
01083 extern void *DMA_Silent_Event_PIC_Event;                                        // Sblaster.cpp
01084 extern void *DMA_DAC_Event_PIC_Event;
01085 extern void *DSP_FinishReset_PIC_Event;
01086 extern void *DSP_RaiseIRQEvent_PIC_Event;
01087 extern void *END_DMA_Event_PIC_Event;
01088 extern void *Serial_EventHandler_PIC_Event;                             // Serialport.cpp
01089 extern void *PIT0_Event_PIC_Event;                                                              // Timer.cpp
01090 extern void *VGA_DisplayStartLatch_PIC_Event;                   // Vga.cpp
01091 extern void *VGA_DrawEGASingleLine_PIC_Event;
01092 //extern void *VGA_DrawPart_PIC_Event;
01093 extern void *VGA_DrawSingleLine_PIC_Event;
01094 extern void *VGA_Other_VertInterrupt_PIC_Event;
01095 extern void *VGA_PanningLatch_PIC_Event;
01096 extern void *VGA_SetupDrawing_PIC_Event;
01097 extern void *VGA_VertInterrupt_PIC_Event;
01098 extern void *VGA_VerticalTimer_PIC_Event;
01099 extern void *PIC_IRQCheckDelayed_PIC_Event;
01100 
01101 #if C_NE2000
01102 //extern void *NE2000_TX_Event_PIC_Event;                                               // Ne2000.cpp
01103 #endif
01104 
01105 
01106 // PIC_TimerHandlers
01107 #if C_IPX
01108 //extern void *IPX_ClientLoop_PIC_Timer;                                                // Ipx.cpp
01109 //extern void *IPX_ServerLoop_PIC_Timer;                                                // Ipxserver.cpp
01110 #endif
01111 extern void *KEYBOARD_TickHandler_PIC_Timer;                    // Keyboard.cpp
01112 extern void *KEYBOARD_TickHandler_PIC_Timer;                    // Keyboard.cpp
01113 //extern void *MIXER_Mix_NoSound_PIC_Timer;                                     // Mixer.cpp
01114 extern void *MIXER_Mix_PIC_Timer;
01115 
01116 #if C_NE2000
01117 //extern void *NE2000_Poller_PIC_Event;                                                 // Ne2000.cpp
01118 #endif
01119 
01120 extern void *fmport_a_pic_event_PIC_Event;
01121 extern void *fmport_b_pic_event_PIC_Event;
01122 
01123 const void *pic_state_event_table[] = {
01124         NULL,
01125         cmos_timerevent_PIC_Event,
01126         DISNEY_disable_PIC_Event,
01127         GUS_TimerEvent_PIC_Event,
01128 #if C_IPX       
01129 //      IPX_AES_EventHandler_PIC_Event,
01130 #endif  
01131         KEYBOARD_TransferBuffer_PIC_Event,
01132         MOUSE_Limit_Events_PIC_Event,
01133         MPU401_Event_PIC_Event,
01134         DMA_Silent_Event_PIC_Event,
01135         DSP_FinishReset_PIC_Event,
01136         DSP_RaiseIRQEvent_PIC_Event,
01137         DMA_DAC_Event_PIC_Event,
01138         END_DMA_Event_PIC_Event,
01139         END_DMA_Event_PIC_Event,
01140         Serial_EventHandler_PIC_Event,
01141         PIT0_Event_PIC_Event,
01142         VGA_DisplayStartLatch_PIC_Event,
01143         VGA_DrawEGASingleLine_PIC_Event,
01144         //VGA_DrawPart_PIC_Event,
01145         VGA_DrawSingleLine_PIC_Event,
01146         VGA_Other_VertInterrupt_PIC_Event,
01147         VGA_PanningLatch_PIC_Event,
01148         VGA_SetupDrawing_PIC_Event,
01149         VGA_VertInterrupt_PIC_Event,
01150         VGA_VerticalTimer_PIC_Event,
01151         fmport_a_pic_event_PIC_Event,
01152         fmport_b_pic_event_PIC_Event,
01153         PIC_IRQCheckDelayed_PIC_Event,
01154 
01155 #if C_NE2000
01156         //NE2000_TX_Event_PIC_Event,
01157 #endif
01158 };
01159 
01160 
01161 const void *pic_state_timer_table[] = {
01162         NULL,
01163 #if C_IPX       
01164 //      IPX_ClientLoop_PIC_Timer,
01165 //      IPX_ServerLoop_PIC_Timer,
01166 #endif  
01167         KEYBOARD_TickHandler_PIC_Timer,
01168         KEYBOARD_TickHandler_PIC_Timer,
01169         //MIXER_Mix_NoSound_PIC_Timer,
01170         MIXER_Mix_PIC_Timer,
01171 
01172 #if C_NE2000
01173         //NE2000_Poller_PIC_Event,
01174 #endif
01175 };
01176 
01177 
01178 
01179 //#include <windows.h>
01180 Bit16u PIC_State_FindEvent( Bitu addr ) {
01181         int size;
01182 
01183         size = sizeof(pic_state_event_table) / sizeof(void *);
01184         for( int lcv=0; lcv<size; lcv++ ) {
01185                 if( addr == (Bitu) (pic_state_event_table[lcv]) ) return lcv;
01186         }
01187 
01188 
01189         // ERROR!! (place debug breakpoint here)
01190         //MessageBox(0,"PIC - State FindEvent",0,0);
01191         return 0xffff;
01192 }
01193 
01194 
01195 Bit16u PIC_State_FindTimer( Bitu addr ) {
01196         int size;
01197 
01198         size = sizeof(pic_state_timer_table) / sizeof(void *);
01199         for( int lcv=0; lcv<size; lcv++ ) {
01200                 if( addr == (Bitu) (pic_state_timer_table[lcv]) ) return lcv;
01201         }
01202 
01203 
01204         // ERROR!! (place debug breakpoint here)
01205         //MessageBox(0,"PIC - State FindTimer",0,0);
01206         return 0xffff;
01207 }
01208 
01209 
01210 Bitu PIC_State_IndexEvent( Bit16u index ) {
01211         if( index == 0xffff ) return 0;
01212 
01213         return (Bitu) (pic_state_event_table[index]);
01214 }
01215 
01216 
01217 Bitu PIC_State_IndexTimer( Bit16u index ) {
01218         if( index == 0xffff ) return 0;
01219 
01220         return (Bitu) (pic_state_timer_table[index]);
01221 }
01222 
01223 //save state support
01224 void *PIC_IRQCheckDelayed_PIC_Event = (void*)((uintptr_t)PIC_IRQCheckDelayed);
01225 
01226 namespace
01227 {
01228 class SerializePic : public SerializeGlobalPOD
01229 {
01230 public:
01231                 SerializePic() : SerializeGlobalPOD("Pic")
01232     {}
01233 
01234 private:
01235     virtual void getBytes(std::ostream& stream)
01236     {
01237                                 Bit16u pic_free_idx, pic_next_idx;
01238                                 Bit16u pic_next_ptr[PIC_QUEUESIZE];
01239 
01240                                 TickerBlock *ticker_ptr;
01241                                 Bit16u ticker_size;
01242                                 Bit16u ticker_handler_idx;
01243 
01244 
01245                                 for( int lcv=0; lcv<PIC_QUEUESIZE; lcv++ ) {
01246                                         Bitu pic_addr;
01247 
01248                                         pic_addr = (Bitu) pic_queue.entries[lcv].next;
01249                                         pic_next_ptr[lcv] = 0xffff;
01250 
01251                                         for( int lcv2=0; lcv2<PIC_QUEUESIZE; lcv2++ ) {
01252                                                 if( pic_addr == (Bitu) &pic_queue.entries[lcv2] ) {
01253                                                         pic_next_ptr[lcv] = lcv2;
01254                                                         break;
01255                                                 }
01256                                         }
01257                                 }
01258 
01259 
01260                                 ticker_size = 0;
01261                                 ticker_ptr = firstticker;
01262                                 while( ticker_ptr != NULL ) {
01263                                         ticker_ptr = ticker_ptr->next;
01264                                         ticker_size++;
01265                                 }
01266 
01267                                 // ***************************************************
01268                                 // ***************************************************
01269                                 // ***************************************************
01270 
01271         SerializeGlobalPOD::getBytes(stream);
01272 
01273 
01274                                 // - data
01275         stream.write(reinterpret_cast<const char*>(&PIC_Ticks), sizeof(PIC_Ticks) );
01276         stream.write(reinterpret_cast<const char*>(&PIC_IRQCheck), sizeof(PIC_IRQCheck) );
01277         stream.write(reinterpret_cast<const char*>(&PIC_IRQCheckPending), sizeof(PIC_IRQCheckPending) );
01278 
01279                                 // - data structs
01280         stream.write(reinterpret_cast<const char*>(&pics), sizeof(pics) );
01281 
01282 
01283                                 pic_free_idx = 0xffff;
01284                                 pic_next_idx = 0xffff;
01285                                 for( int lcv=0; lcv<PIC_QUEUESIZE; lcv++ ) {
01286                                         Bit16u event_idx;
01287 
01288                                         // - data
01289                                         stream.write(reinterpret_cast<const char*>(&pic_queue.entries[lcv].index), sizeof(pic_queue.entries[lcv].index) );
01290                                         stream.write(reinterpret_cast<const char*>(&pic_queue.entries[lcv].value), sizeof(pic_queue.entries[lcv].value) );
01291 
01292                                         // - function ptr
01293                                         event_idx = PIC_State_FindEvent( (Bitu) (pic_queue.entries[lcv].pic_event) );
01294                                         stream.write(reinterpret_cast<const char*>(&event_idx), sizeof(event_idx) );
01295 
01296                                         // - reloc ptr
01297                                         stream.write(reinterpret_cast<const char*>(&pic_next_ptr[lcv]), sizeof(pic_next_ptr[lcv]) );
01298 
01299 
01300                                         if( &pic_queue.entries[lcv] == pic_queue.free_entry ) pic_free_idx = lcv;
01301                                         if( &pic_queue.entries[lcv] == pic_queue.next_entry ) pic_next_idx = lcv;
01302                                 }
01303 
01304                                 // - reloc ptrs
01305         stream.write(reinterpret_cast<const char*>(&pic_free_idx), sizeof(pic_free_idx) );
01306         stream.write(reinterpret_cast<const char*>(&pic_next_idx), sizeof(pic_next_idx) );
01307 
01308 
01309                                 // - data
01310         stream.write(reinterpret_cast<const char*>(&InEventService), sizeof(InEventService) );
01311         stream.write(reinterpret_cast<const char*>(&srv_lag), sizeof(srv_lag) );
01312 
01313 
01314                                 // - reloc ptr
01315         stream.write(reinterpret_cast<const char*>(&ticker_size), sizeof(ticker_size) );
01316 
01317                                 ticker_ptr = firstticker;
01318                                 for( int lcv=0; lcv<ticker_size; lcv++ ) {
01319                                         // - function ptr
01320                                         ticker_handler_idx = PIC_State_FindTimer( (Bitu) (ticker_ptr->handler) );
01321                                         stream.write(reinterpret_cast<const char*>(&ticker_handler_idx), sizeof(ticker_handler_idx) );
01322 
01323                                         // - reloc new ptr (leave alone)
01324                                         //stream.write(reinterpret_cast<const char*>(&ticker_ptr->next), sizeof(ticker_ptr->next) );
01325 
01326                                         ticker_ptr = ticker_ptr->next;
01327                                 }
01328 
01329 
01330                                 // - system (leave alone)
01331         //stream.write(reinterpret_cast<const char*>(&PIC_benchstart), sizeof(PIC_benchstart) );
01332         //stream.write(reinterpret_cast<const char*>(&PIC_tickstart), sizeof(PIC_tickstart) );
01333 
01334                                 // - static (leave alone)
01335                                 //test->saveState(stream);
01336                 }
01337 
01338     virtual void setBytes(std::istream& stream)
01339     {
01340                                 Bit16u free_idx, next_idx;
01341                                 Bit16u ticker_size;
01342 
01343 
01344         SerializeGlobalPOD::setBytes(stream);
01345 
01346 
01347                                 // - data
01348         stream.read(reinterpret_cast<char*>(&PIC_Ticks), sizeof(PIC_Ticks) );
01349         stream.read(reinterpret_cast<char*>(&PIC_IRQCheck), sizeof(PIC_IRQCheck) );
01350         stream.read(reinterpret_cast<char*>(&PIC_IRQCheckPending), sizeof(PIC_IRQCheckPending) );
01351 
01352                                 // - data structs
01353         stream.read(reinterpret_cast<char*>(&pics), sizeof(pics) );
01354 
01355 
01356                                 for( int lcv=0; lcv<PIC_QUEUESIZE; lcv++ ) {
01357                                         Bit16u event_idx, next_idx;
01358 
01359                                         // - data
01360                                         stream.read(reinterpret_cast<char*>(&pic_queue.entries[lcv].index), sizeof(pic_queue.entries[lcv].index) );
01361                                         stream.read(reinterpret_cast<char*>(&pic_queue.entries[lcv].value), sizeof(pic_queue.entries[lcv].value) );
01362 
01363 
01364                                         // - function ptr
01365                                         stream.read(reinterpret_cast<char*>(&event_idx), sizeof(event_idx) );
01366                                         pic_queue.entries[lcv].pic_event = (PIC_EventHandler) PIC_State_IndexEvent( event_idx );
01367 
01368 
01369                                         // - reloc ptr
01370                                         stream.read(reinterpret_cast<char*>(&next_idx), sizeof(next_idx) );
01371 
01372                                         pic_queue.entries[lcv].next = NULL;
01373                                         if( next_idx != 0xffff )
01374                                                 pic_queue.entries[lcv].next = &pic_queue.entries[next_idx];
01375                                 }
01376 
01377                                 // - reloc ptrs
01378         stream.read(reinterpret_cast<char*>(&free_idx), sizeof(free_idx) );
01379         stream.read(reinterpret_cast<char*>(&next_idx), sizeof(next_idx) );
01380 
01381                                 pic_queue.free_entry = NULL;
01382                                 if( free_idx != 0xffff )
01383                                         pic_queue.free_entry = &pic_queue.entries[free_idx];
01384 
01385                                 pic_queue.next_entry = NULL;
01386                                 if( next_idx != 0xffff )
01387                                         pic_queue.next_entry = &pic_queue.entries[next_idx];
01388 
01389 
01390                                 // - data
01391         stream.read(reinterpret_cast<char*>(&InEventService), sizeof(InEventService) );
01392         stream.read(reinterpret_cast<char*>(&srv_lag), sizeof(srv_lag) );
01393 
01394 
01395                                 // 1- wipe old data
01396                                 // 2- insert new data
01397                                 while( firstticker != NULL ) {
01398                                         TickerBlock *ticker_ptr;
01399 
01400                                         ticker_ptr = firstticker;
01401                                         firstticker = firstticker->next;
01402 
01403                                         delete ticker_ptr;
01404                                 }
01405 
01406 
01407                                 // - reloc ptr
01408         stream.read(reinterpret_cast<char*>(&ticker_size), sizeof(ticker_size) );
01409 
01410                                 firstticker = NULL;
01411                                 if( ticker_size ) {
01412                                         TickerBlock *ticker_ptr;
01413 
01414                                         for( int lcv = 0; lcv<ticker_size; lcv++ ) {
01415                                                 Bit16u ticker_idx;
01416 
01417                                                 if( lcv == 0 ) {
01418                                                         ticker_ptr = new TickerBlock;
01419                                                         firstticker = ticker_ptr;
01420                                                 }
01421                                                 else {
01422                                                         ticker_ptr->next = new TickerBlock;
01423                                                         ticker_ptr = ticker_ptr->next;
01424                                                 }
01425 
01426 
01427                                                 // - function ptr
01428                                                 stream.read(reinterpret_cast<char*>(&ticker_idx), sizeof(ticker_idx) );
01429                                                 ticker_ptr->handler = (TIMER_TickHandler) PIC_State_IndexTimer( ticker_idx );
01430 
01431                                                 // - reloc new ptr (linked list)
01432                                                 ticker_ptr->next = NULL;
01433                                         }
01434                                 }
01435 
01436 
01437                                 // - system (leave alone)
01438         //stream.read(reinterpret_cast<char*>(&PIC_benchstart), sizeof(PIC_benchstart) );
01439         //stream.read(reinterpret_cast<char*>(&PIC_tickstart), sizeof(PIC_tickstart) );
01440 
01441                                 // - static (leave alone)
01442                                 //test->loadState(stream);
01443     }
01444 } dummy;
01445 }
01446