DOSBox-X
|
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