DOSBox-X
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Defines
src/hardware/timer.cpp
00001 /*
00002  *  Copyright (C) 2002-2015  The DOSBox Team
00003  *
00004  *  This program is free software; you can redistribute it and/or modify
00005  *  it under the terms of the GNU General Public License as published by
00006  *  the Free Software Foundation; either version 2 of the License, or
00007  *  (at your option) any later version.
00008  *
00009  *  This program is distributed in the hope that it will be useful,
00010  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
00011  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00012  *  GNU General Public License for more details.
00013  *
00014  *  You should have received a copy of the GNU General Public License
00015  *  along with this program; if not, write to the Free Software
00016  *  Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
00017  */
00018 
00019 
00020 #include <math.h>
00021 #include "dosbox.h"
00022 #include "inout.h"
00023 #include "pic.h"
00024 #include "cpu.h"
00025 #include "mem.h"
00026 #include "mixer.h"
00027 #include "timer.h"
00028 #include "setup.h"
00029 #include "control.h"
00030 
00031 static INLINE void BIN2BCD(Bit16u& val) {
00032         Bit16u temp=val%10 + (((val/10)%10)<<4)+ (((val/100)%10)<<8) + (((val/1000)%10)<<12);
00033         val=temp;
00034 }
00035 
00036 static INLINE void BCD2BIN(Bit16u& val) {
00037         Bit16u temp= (val&0x0f) +((val>>4)&0x0f) *10 +((val>>8)&0x0f) *100 +((val>>12)&0x0f) *1000;
00038         val=temp;
00039 }
00040 
00041 struct PIT_Block {
00042         Bitu cntr;
00043         float delay;
00044         double start;
00045 
00046         Bit16u read_latch;
00047         Bit16u write_latch;
00048 
00049         Bit8u mode;
00050         Bit8u latch_mode;
00051         Bit8u read_state;
00052         Bit8u write_state;
00053 
00054         bool bcd;
00055         bool go_read_latch;
00056         bool new_mode;
00057         bool counterstatus_set;
00058         bool counting;
00059         bool update_count;
00060 };
00061 
00062 static PIT_Block pit[3];
00063 static bool gate2;
00064 
00065 static Bit8u latched_timerstatus;
00066 // the timer status can not be overwritten until it is read or the timer was 
00067 // reprogrammed.
00068 static bool latched_timerstatus_locked;
00069 
00070 unsigned long PIT_TICK_RATE = PIT_TICK_RATE_IBM;
00071 
00072 static void PIT0_Event(Bitu /*val*/) {
00073         PIC_ActivateIRQ(0);
00074         if (pit[0].mode != 0) {
00075                 pit[0].start += pit[0].delay;
00076 
00077                 if (GCC_UNLIKELY(pit[0].update_count)) {
00078                         pit[0].delay=(1000.0f/((float)PIT_TICK_RATE/(float)pit[0].cntr));
00079                         pit[0].update_count=false;
00080                 }
00081                 PIC_AddEvent(PIT0_Event,pit[0].delay);
00082         }
00083 }
00084 
00085 static bool counter_output(Bitu counter) {
00086         PIT_Block * p=&pit[counter];
00087         double index=PIC_FullIndex()-p->start;
00088         switch (p->mode) {
00089         case 0:
00090                 if (p->new_mode) return false;
00091                 if (index>p->delay) return true;
00092                 else return false;
00093                 break;
00094         case 2:
00095                 if (p->new_mode) return true;
00096                 index=fmod(index,(double)p->delay);
00097                 return index>0;
00098         case 3:
00099                 if (p->new_mode) return true;
00100                 index=fmod(index,(double)p->delay);
00101                 return index*2<p->delay;
00102         case 4:
00103                 //Only low on terminal count
00104                 // if(fmod(index,(double)p->delay) == 0) return false; //Maybe take one rate tick in consideration
00105                 //Easiest solution is to report always high (Space marines uses this mode)
00106                 return true;
00107         default:
00108                 LOG(LOG_PIT,LOG_ERROR)("Illegal Mode %d for reading output",p->mode);
00109                 return true;
00110         }
00111 }
00112 static void status_latch(Bitu counter) {
00113         // the timer status can not be overwritten until it is read or the timer was 
00114         // reprogrammed.
00115         if(!latched_timerstatus_locked) {
00116                 PIT_Block * p=&pit[counter];
00117                 latched_timerstatus=0;
00118                 // Timer Status Word
00119                 // 0: BCD 
00120                 // 1-3: Timer mode
00121                 // 4-5: read/load mode
00122                 // 6: "NULL" - this is 0 if "the counter value is in the counter" ;)
00123                 // should rarely be 1 (i.e. on exotic modes)
00124                 // 7: OUT - the logic level on the Timer output pin
00125                 if(p->bcd)latched_timerstatus|=0x1;
00126                 latched_timerstatus|=((p->mode&7)<<1);
00127                 if((p->read_state==0)||(p->read_state==3)) latched_timerstatus|=0x30;
00128                 else if(p->read_state==1) latched_timerstatus|=0x10;
00129                 else if(p->read_state==2) latched_timerstatus|=0x20;
00130                 if(counter_output(counter)) latched_timerstatus|=0x80;
00131                 if(p->new_mode) latched_timerstatus|=0x40;
00132                 // The first thing that is being read from this counter now is the
00133                 // counter status.
00134                 p->counterstatus_set=true;
00135                 latched_timerstatus_locked=true;
00136         }
00137 }
00138 static void counter_latch(Bitu counter,bool do_latch=true) {
00139         /* Fill the read_latch of the selected counter with current count */
00140         PIT_Block * p=&pit[counter];
00141         p->go_read_latch=false;
00142 
00143         //If gate2 is disabled don't update the read_latch
00144         if (counter == (IS_PC98_ARCH ? 1 : 2) && !gate2 && p->mode !=1) return;
00145 
00146         if (GCC_UNLIKELY(p->new_mode)) {
00147                 double passed_time = PIC_FullIndex() - p->start;
00148                 Bitu ticks_since_then = (Bitu)(passed_time / (1000.0/PIT_TICK_RATE));
00149                 //if (p->mode==3) ticks_since_then /= 2; // TODO figure this out on real hardware
00150                 p->read_latch -= ticks_since_then;
00151                 return;
00152         }
00153         double index=PIC_FullIndex()-p->start;
00154         switch (p->mode) {
00155         case 4:         /* Software Triggered Strobe */
00156         case 0:         /* Interrupt on Terminal Count */
00157                 {
00158                         /* Counter keeps on counting after passing terminal count */
00159                         if(p->bcd) {
00160                                 index = fmod(index,(1000.0/PIT_TICK_RATE)*10000.0);
00161                                 p->read_latch = (Bit16u)(((unsigned long)(p->cntr-index*(PIT_TICK_RATE/1000.0))) % 10000UL);
00162                         } else {
00163                                 index = fmod(index,(1000.0/PIT_TICK_RATE)*(double)0x10000);
00164                                 p->read_latch = (Bit16u)(p->cntr-index*(PIT_TICK_RATE/1000.0));
00165                         }
00166                 }
00167                 break;
00168         case 1: // countdown
00169                 if(p->counting) {
00170                         if (index>p->delay) { // has timed out
00171                                 p->read_latch = 0xffff; //unconfirmed
00172                         } else {
00173                                 p->read_latch=(Bit16u)(p->cntr-index*(PIT_TICK_RATE/1000.0));
00174                         }
00175                 }
00176                 break;
00177         case 2:         /* Rate Generator */
00178                 index=fmod(index,(double)p->delay);
00179                 p->read_latch=(Bit16u)(p->cntr - (index/p->delay)*p->cntr);
00180                 break;
00181         case 3:         /* Square Wave Rate Generator */
00182         {
00183             bool out = true;
00184 
00185             index=fmod(index,(double)p->delay);
00186             index*=2;
00187             if (index>p->delay) {
00188                 index-=p->delay;
00189                 out = false;
00190             }
00191 
00192             if (do_latch) {
00193                 p->read_latch=(Bit16u)(p->cntr - (index/p->delay)*p->cntr);
00194                 // In mode 3 it never returns odd numbers LSB (if odd number is written 1 will be
00195                 // subtracted on first clock and then always 2)
00196                 // fixes "Corncob 3D"
00197                 p->read_latch&=0xfffe;
00198             }
00199 
00200             // for the second half of the cycle, OUT goes low.
00201             // remember that counter #0 is tied to IRQ0 on both IBM PC and PC-98.
00202             // therefore, counter #0 output directly affects bit 0 of the interrupt request register on the PIC.
00203             // I don't know any IBM PC compatible game that uses this behavior, but I did find a PC-98 game
00204             // "Steel Gun Nyan" with delay loops that are dependent on polling the IRR like that.
00205             // Yes, instead of polling the timer directly, it polls the PIC's interrupt request register instead. Ick.
00206             if (counter == 0/*IRQ 0*/) {
00207                 if (!out)
00208                     PIC_ActivateIRQ(0);
00209                 else
00210                     PIC_DeActivateIRQ(0);
00211             }
00212         }
00213                 break;
00214         default:
00215                 LOG(LOG_PIT,LOG_ERROR)("Illegal Mode %d for reading counter %d",(int)p->mode,(int)counter);
00216                 p->read_latch=0xffff;
00217                 break;
00218         }
00219 }
00220 
00221 void TIMER_IRQ0Poll(void) {
00222     counter_latch(0,false/*do not latch*/);
00223 }
00224 
00225 static void write_latch(Bitu port,Bitu val,Bitu /*iolen*/) {
00226 //LOG(LOG_PIT,LOG_ERROR)("port %X write:%X state:%X",port,val,pit[port-0x40].write_state);
00227 
00228     // HACK: Port translation for this code PC-98 mode.
00229     //       0x71,0x73,0x75,0x77 => 0x40-0x43
00230     if (IS_PC98_ARCH) {
00231         if (port >= 0x3FD9)
00232             port = ((port - 0x3FD9) >> 1) + 0x40;
00233         else
00234             port = ((port - 0x71) >> 1) + 0x40;
00235     }
00236 
00237         Bitu counter=port-0x40;
00238         PIT_Block * p=&pit[counter];
00239         if(p->bcd == true) BIN2BCD(p->write_latch);
00240    
00241         switch (p->write_state) {
00242                 case 0:
00243                         p->write_latch = p->write_latch | ((val & 0xff) << 8);
00244                         p->write_state = 3;
00245                         break;
00246                 case 3:
00247                         p->write_latch = val & 0xff;
00248                         p->write_state = 0;
00249                         break;
00250                 case 1:
00251                         p->write_latch = val & 0xff;
00252                         break;
00253                 case 2:
00254                         p->write_latch = (val & 0xff) << 8;
00255                 break;
00256         }
00257         if (p->bcd==true) BCD2BIN(p->write_latch);
00258         if (p->write_state != 0) {
00259                 if (p->write_latch == 0) {
00260                         if (p->bcd == false) p->cntr = 0x10000;
00261                         else p->cntr=9999;
00262                 } else p->cntr = p->write_latch;
00263 
00264                 if ((!p->new_mode) && (p->mode == 2) && (counter == 0)) {
00265                         // In mode 2 writing another value has no direct effect on the count
00266                         // until the old one has run out. This might apply to other modes too.
00267                         // This is not fixed for PIT2 yet!!
00268                         p->update_count=true;
00269                         return;
00270                 }
00271                 p->start=PIC_FullIndex();
00272                 p->delay=(1000.0f/((float)PIT_TICK_RATE/(float)p->cntr));
00273 
00274                 switch (counter) {
00275                 case 0x00:                      /* Timer hooked to IRQ 0 */
00276                         if (p->new_mode || p->mode == 0 ) {
00277                                 if(p->mode==0) PIC_RemoveEvents(PIT0_Event); // DoWhackaDo demo
00278                                 PIC_AddEvent(PIT0_Event,p->delay);
00279                         } else LOG(LOG_PIT,LOG_NORMAL)("PIT 0 Timer set without new control word");
00280                         LOG(LOG_PIT,LOG_NORMAL)("PIT 0 Timer at %.4f Hz mode %d",1000.0/p->delay,p->mode);
00281                         break;
00282         case 0x01:          /* Timer hooked to PC-Speaker (NEC-PC98) */
00283             if (IS_PC98_ARCH)
00284                 PCSPEAKER_SetCounter(p->cntr,p->mode);
00285             break;
00286         case 0x02:                      /* Timer hooked to PC-Speaker (IBM PC) */
00287             if (!IS_PC98_ARCH)
00288                 PCSPEAKER_SetCounter(p->cntr,p->mode);
00289             break;
00290         default:
00291                         LOG(LOG_PIT,LOG_ERROR)("PIT:Illegal timer selected for writing");
00292                 }
00293                 p->new_mode=false;
00294     }
00295 }
00296 
00297 static Bitu read_latch(Bitu port,Bitu /*iolen*/) {
00298 //LOG(LOG_PIT,LOG_ERROR)("port read %X",port);
00299 
00300     // HACK: Port translation for this code PC-98 mode.
00301     //       0x71,0x73,0x75,0x77 => 0x40-0x43
00302     if (IS_PC98_ARCH) {
00303         if (port >= 0x3FD9)
00304             port = ((port - 0x3FD9) >> 1) + 0x40;
00305         else
00306             port = ((port - 0x71) >> 1) + 0x40;
00307     }
00308 
00309         Bit32u counter=port-0x40;
00310         Bit8u ret=0;
00311         if(GCC_UNLIKELY(pit[counter].counterstatus_set)){
00312                 pit[counter].counterstatus_set = false;
00313                 latched_timerstatus_locked = false;
00314                 ret = latched_timerstatus;
00315         } else {
00316                 if (pit[counter].go_read_latch == true) 
00317                         counter_latch(counter);
00318 
00319                 if( pit[counter].bcd == true) BIN2BCD(pit[counter].read_latch);
00320 
00321                 switch (pit[counter].read_state) {
00322                 case 0: /* read MSB & return to state 3 */
00323                         ret=(pit[counter].read_latch >> 8) & 0xff;
00324                         pit[counter].read_state = 3;
00325                         pit[counter].go_read_latch = true;
00326                         break;
00327                 case 3: /* read LSB followed by MSB */
00328                         ret = pit[counter].read_latch & 0xff;
00329                         pit[counter].read_state = 0;
00330                         break;
00331                 case 1: /* read LSB */
00332                         ret = pit[counter].read_latch & 0xff;
00333                         pit[counter].go_read_latch = true;
00334                         break;
00335                 case 2: /* read MSB */
00336                         ret = (pit[counter].read_latch >> 8) & 0xff;
00337                         pit[counter].go_read_latch = true;
00338                         break;
00339                 default:
00340                         E_Exit("Timer.cpp: error in readlatch");
00341                         break;
00342                 }
00343                 if( pit[counter].bcd == true) BCD2BIN(pit[counter].read_latch);
00344         }
00345         return ret;
00346 }
00347 
00348 static void write_p43(Bitu /*port*/,Bitu val,Bitu /*iolen*/) {
00349 //LOG(LOG_PIT,LOG_ERROR)("port 43 %X",val);
00350         Bitu latch=(val >> 6) & 0x03;
00351         switch (latch) {
00352         case 0:
00353         case 1:
00354         case 2:
00355                 if ((val & 0x30) == 0) {
00356                         /* Counter latch command */
00357                         counter_latch(latch);
00358                 } else {
00359                         // save output status to be used with timer 0 irq
00360                         bool old_output = counter_output(0);
00361                         // save the current count value to be re-used in undocumented newmode
00362                         counter_latch(latch);
00363                         pit[latch].bcd = (val&1)>0;   
00364                         if (val & 1) {
00365                                 if(pit[latch].cntr>=9999) pit[latch].cntr=9999;
00366                         }
00367 
00368                         // Timer is being reprogrammed, unlock the status
00369                         if(pit[latch].counterstatus_set) {
00370                                 pit[latch].counterstatus_set=false;
00371                                 latched_timerstatus_locked=false;
00372                         }
00373                         pit[latch].start = PIC_FullIndex(); // for undocumented newmode
00374                         pit[latch].go_read_latch = true;
00375                         pit[latch].update_count = false;
00376                         pit[latch].counting = false;
00377                         pit[latch].read_state  = (val >> 4) & 0x03;
00378                         pit[latch].write_state = (val >> 4) & 0x03;
00379                         Bit8u mode             = (val >> 1) & 0x07;
00380                         if (mode > 5)
00381                                 mode -= 4; //6,7 become 2 and 3
00382 
00383                         pit[latch].mode = mode;
00384 
00385                         /* If the line goes from low to up => generate irq. 
00386                          *      ( BUT needs to stay up until acknowlegded by the cpu!!! therefore: )
00387                          * If the line goes to low => disable irq.
00388                          * Mode 0 starts with a low line. (so always disable irq)
00389                          * Mode 2,3 start with a high line.
00390                          * counter_output tells if the current counter is high or low 
00391                          * So actually a mode 3 timer enables and disables irq al the time. (not handled) */
00392 
00393             /* Jon C: Oh yeah? Nobody abuses counter == 0 on IBM PC that way, but there is a PC-98
00394              *        game that relies on that behavior: Steel Gun Nyan! */
00395 
00396                         if (latch == 0) {
00397                                 PIC_RemoveEvents(PIT0_Event);
00398                                 if((mode != 0)&& !old_output) {
00399                                         PIC_ActivateIRQ(0);
00400                                 } else {
00401                                         PIC_DeActivateIRQ(0);
00402                                 }
00403                         }
00404                         pit[latch].new_mode = true;
00405                         if (latch == (IS_PC98_ARCH ? 1 : 2)) {
00406                                 // notify pc speaker code that the control word was written
00407                                 PCSPEAKER_SetPITControl(mode);
00408                         }
00409                 }
00410                 break;
00411     case 3:
00412                 if ((val & 0x20)==0) {  /* Latch multiple pit counters */
00413                         if (val & 0x02) counter_latch(0);
00414                         if (val & 0x04) counter_latch(1);
00415                         if (val & 0x08) counter_latch(2);
00416                 }
00417                 // status and values can be latched simultaneously
00418                 if ((val & 0x10)==0) {  /* Latch status words */
00419                         // but only 1 status can be latched simultaneously
00420                         if (val & 0x02) status_latch(0);
00421                         else if (val & 0x04) status_latch(1);
00422                         else if (val & 0x08) status_latch(2);
00423                 }
00424                 break;
00425         }
00426 }
00427 
00428 void TIMER_SetGate2(bool in) {
00429         //No changes if gate doesn't change
00430         if(gate2 == in) return;
00431         Bit8u & mode=pit[2].mode;
00432         switch(mode) {
00433         case 0:
00434                 if(in) pit[2].start = PIC_FullIndex();
00435                 else {
00436                         //Fill readlatch and store it.
00437                         counter_latch(2);
00438                         pit[2].cntr = pit[2].read_latch;
00439                 }
00440                 break;
00441         case 1:
00442                 // gate 1 on: reload counter; off: nothing
00443                 if(in) {
00444                         pit[2].counting = true;
00445                         pit[2].start = PIC_FullIndex();
00446                 }
00447                 break;
00448         case 2:
00449         case 3:
00450                 //If gate is enabled restart counting. If disable store the current read_latch
00451                 if(in) pit[2].start = PIC_FullIndex();
00452                 else counter_latch(2);
00453                 break;
00454         case 4:
00455         case 5:
00456                 LOG(LOG_MISC,LOG_WARN)("unsupported gate 2 mode %x",mode);
00457                 break;
00458         }
00459         gate2 = in; //Set it here so the counter_latch above works
00460 }
00461 
00462 bool TIMER_GetOutput2() {
00463         return counter_output(2);
00464 }
00465 
00466 #include "programs.h"
00467 
00468 static IO_ReadHandleObject ReadHandler[4];
00469 static IO_WriteHandleObject WriteHandler[4];
00470 
00471 /* PC-98 alias */
00472 static IO_ReadHandleObject ReadHandler2[4];
00473 static IO_WriteHandleObject WriteHandler2[4];
00474 
00475 void TIMER_BIOS_INIT_Configure() {
00476         PIC_RemoveEvents(PIT0_Event);
00477         PIC_DeActivateIRQ(0);
00478 
00479         /* Setup Timer 0 */
00480         pit[0].cntr = 0x10000;
00481         pit[0].write_state = 3;
00482         pit[0].read_state = 3;
00483         pit[0].read_latch = 0;
00484         pit[0].write_latch = 0;
00485         pit[0].mode = 3;
00486         pit[0].bcd = false;
00487         pit[0].go_read_latch = true;
00488         pit[0].counterstatus_set = false;
00489         pit[0].update_count = false;
00490         pit[0].start = PIC_FullIndex();
00491 
00492         pit[1].bcd = false;
00493         pit[1].write_state = 1;
00494         pit[1].read_state = 1;
00495         pit[1].go_read_latch = true;
00496         pit[1].cntr = 18;
00497         pit[1].mode = 2;
00498         pit[1].write_state = 3;
00499         pit[1].counterstatus_set = false;
00500         pit[1].start = PIC_FullIndex();
00501 
00502         pit[2].bcd = false;
00503         pit[2].write_state = 1;
00504         pit[2].read_state = 1;
00505         pit[2].go_read_latch = true;
00506         pit[2].cntr = 18;
00507         pit[2].mode = 2;
00508         pit[2].write_state = 3;
00509         pit[2].counterstatus_set = false;
00510         pit[2].start = PIC_FullIndex();
00511 
00512     /* TODO: I have observed that on real PC-98 hardware:
00513      * 
00514      *   Output 1 (speaker) does not cycle if inhibited by port 35h
00515      *
00516      *   Output 2 (RS232C) does not cycle until programmed to cycle
00517      *   to operate the 8251 for data transfer. It is configured by
00518      *   the BIOS to countdown and stop, thus the UART is not cycling
00519      *   until put into active use. */
00520 
00521     int pcspeaker_pit = IS_PC98_ARCH ? 1 : 2; /* IBM: PC speaker on output 2   PC-98: PC speaker on output 1 */
00522 
00523         {
00524                 Section_prop *pcsec = static_cast<Section_prop *>(control->GetSection("speaker"));
00525                 int freq = pcsec->Get_int("initial frequency"); /* original code: 1320 */
00526                 unsigned int div;
00527 
00528         /* IBM PC defaults to 903Hz.
00529          * NEC PC-98 defaults to 2KHz */
00530         if (freq < 0)
00531             freq = IS_PC98_ARCH ? 2000 : 903;
00532 
00533                 if (freq < 19) {
00534                         div = 1;
00535                 }
00536                 else {
00537                         div = (unsigned int)PIT_TICK_RATE / (unsigned int)freq;
00538                         if (div > 65535) div = 65535;
00539                 }
00540 
00541                 pit[pcspeaker_pit].cntr = div;
00542                 pit[pcspeaker_pit].read_latch = div;
00543                 pit[pcspeaker_pit].write_state = 3; /* Chuck Yeager */
00544                 pit[pcspeaker_pit].read_state = 3;
00545                 pit[pcspeaker_pit].mode = 3;
00546                 pit[pcspeaker_pit].bcd = false;
00547                 pit[pcspeaker_pit].go_read_latch = true;
00548                 pit[pcspeaker_pit].counterstatus_set = false;
00549                 pit[pcspeaker_pit].counting = false;
00550             pit[pcspeaker_pit].start = PIC_FullIndex();
00551         }
00552 
00553         pit[0].delay=(1000.0f/((float)PIT_TICK_RATE/(float)pit[0].cntr));
00554         pit[1].delay=(1000.0f/((float)PIT_TICK_RATE/(float)pit[1].cntr));
00555         pit[2].delay=(1000.0f/((float)PIT_TICK_RATE/(float)pit[2].cntr));
00556 
00557         PCSPEAKER_SetCounter(pit[pcspeaker_pit].cntr,pit[pcspeaker_pit].mode);
00558         PIC_AddEvent(PIT0_Event,pit[0].delay);
00559 
00560     if (IS_PC98_ARCH) {
00561     /* BIOS data area at 0x501 tells the DOS application which clock rate to use */
00562         phys_writeb(0x501,
00563             (phys_readb(0x501) & 0x7F) |
00564             ((PIT_TICK_RATE == PIT_TICK_RATE_PC98_8MHZ) ? 0x80 : 0x00)      /* bit 7: 1=8MHz  0=5MHz/10MHz */
00565             );
00566     }
00567 }
00568 
00569 void TIMER_OnPowerOn(Section*) {
00570         Section_prop * section=static_cast<Section_prop *>(control->GetSection("dosbox"));
00571         assert(section != NULL);
00572 
00573         // log
00574         LOG(LOG_MISC,LOG_DEBUG)("TIMER_OnPowerOn(): Reinitializing PIT timer emulation");
00575 
00576         PIC_RemoveEvents(PIT0_Event);
00577 
00578         WriteHandler[0].Uninstall();
00579         WriteHandler[1].Uninstall();
00580         WriteHandler[2].Uninstall();
00581         WriteHandler[3].Uninstall();
00582         ReadHandler[0].Uninstall();
00583         ReadHandler[1].Uninstall();
00584         ReadHandler[2].Uninstall();
00585         ReadHandler[3].Uninstall();
00586 
00587         WriteHandler2[0].Uninstall();
00588         WriteHandler2[1].Uninstall();
00589         WriteHandler2[2].Uninstall();
00590         WriteHandler2[3].Uninstall();
00591         ReadHandler2[0].Uninstall();
00592         ReadHandler2[1].Uninstall();
00593         ReadHandler2[2].Uninstall();
00594         ReadHandler2[3].Uninstall();
00595 
00596         WriteHandler[0].Install(0x40,write_latch,IO_MB);
00597 //      WriteHandler[1].Install(0x41,write_latch,IO_MB);
00598         WriteHandler[2].Install(0x42,write_latch,IO_MB);
00599         WriteHandler[3].Install(0x43,write_p43,IO_MB);
00600         ReadHandler[0].Install(0x40,read_latch,IO_MB);
00601         ReadHandler[1].Install(0x41,read_latch,IO_MB);
00602         ReadHandler[2].Install(0x42,read_latch,IO_MB);
00603 
00604         latched_timerstatus_locked=false;
00605         gate2 = false;
00606 
00607     if (IS_PC98_ARCH) {
00608         void TIMER_OnEnterPC98_Phase2(Section*);
00609         TIMER_OnEnterPC98_Phase2(NULL);
00610     }
00611 }
00612 
00613 void TIMER_OnEnterPC98_Phase2_UpdateBDA(void) {
00614         if (!cpu.pmode) {
00615                 /* BIOS data area at 0x501 tells the DOS application which clock rate to use */
00616                 phys_writeb(0x501,
00617             (phys_readb(0x501) & 0x7F) |
00618                         ((PIT_TICK_RATE == PIT_TICK_RATE_PC98_8MHZ) ? 0x80 : 0x00)      /* bit 7: 1=8MHz  0=5MHz/10MHz */
00619                 );
00620         }
00621         else {
00622                 LOG_MSG("PC-98 warning: PIT timer change cannot be reflected to BIOS data area in protected/vm86 mode");
00623         }
00624 }
00625 
00626 /* NTS: This comes in two phases because we're taking ports 0x71-0x77 which overlap
00627  *      with ports 0x70-0x71 from CMOS emulation.
00628  *
00629  *      Phase 1 is where we unregister our I/O ports. CMOS emulation will do so as
00630  *      well either before or after our callback procedure.
00631  *
00632  *      Phase 2 is where we can then claim the I/O ports without our claim getting
00633  *      overwritten by CMOS emulation unregistering the I/O port. */
00634 
00635 void TIMER_OnEnterPC98_Phase2(Section*) {
00636         Section_prop * section=static_cast<Section_prop *>(control->GetSection("dosbox"));
00637         assert(section != NULL);
00638     int pc98rate;
00639 
00640         PIC_RemoveEvents(PIT0_Event);
00641 
00642         WriteHandler[0].Uninstall();
00643         WriteHandler[1].Uninstall();
00644         WriteHandler[2].Uninstall();
00645         WriteHandler[3].Uninstall();
00646         ReadHandler[0].Uninstall();
00647         ReadHandler[1].Uninstall();
00648         ReadHandler[2].Uninstall();
00649         ReadHandler[3].Uninstall();
00650 
00651         WriteHandler2[0].Uninstall();
00652         WriteHandler2[1].Uninstall();
00653         WriteHandler2[2].Uninstall();
00654         WriteHandler2[3].Uninstall();
00655         ReadHandler2[0].Uninstall();
00656         ReadHandler2[1].Uninstall();
00657         ReadHandler2[2].Uninstall();
00658         ReadHandler2[3].Uninstall();
00659 
00660     /* PC-98 has two different rates: 5/10MHz base or 8MHz base. Let the user choose via dosbox.conf */
00661     pc98rate = section->Get_int("pc-98 timer master frequency");
00662         if (pc98rate > 6) pc98rate /= 2;
00663     if (pc98rate == 0) pc98rate = 5; /* Pick the most likely to work with DOS games (FIXME: This is a GUESS!! Is this correct?) */
00664     else if (pc98rate < 5) pc98rate = 4;
00665     else pc98rate = 5;
00666 
00667     if (pc98rate >= 5)
00668         PIT_TICK_RATE = PIT_TICK_RATE_PC98_10MHZ;
00669     else
00670         PIT_TICK_RATE = PIT_TICK_RATE_PC98_8MHZ;
00671 
00672     LOG_MSG("PC-98 PIT master clock rate %luHz",PIT_TICK_RATE);
00673 
00674     /* I/O port map (8254)
00675      *
00676      * IBM PC/XT/AT      NEC-PC98     A1-A0
00677      * -----------------------------------
00678      *  0x40              0x71        0
00679      *  0x41              0x73        1
00680      *  0x42              0x75        2
00681      *  0x43              0x77        3
00682      */
00683     /* Timer output connection
00684      * 
00685      * IBM PC/XT/AT      NEC-PC98     Timer
00686      * ------------------------------------
00687      * Timer int.        Timer int.   0
00688      * DRAM refresh      Speaker      1
00689      * Speaker           RS-232C clk  2
00690      *
00691      * If I read documentation correctly, PC-98 wires timer output 2
00692      * to the clock pin of the 8251 UART for COM1 as a way to set the
00693      * baud rate. */
00694 
00695     /* This code is written to eventually copy-paste out in general */
00696         WriteHandler[0].Install(IS_PC98_ARCH ? 0x71 : 0x40,write_latch,IO_MB);
00697         WriteHandler[1].Install(IS_PC98_ARCH ? 0x73 : 0x41,write_latch,IO_MB);
00698         WriteHandler[2].Install(IS_PC98_ARCH ? 0x75 : 0x42,write_latch,IO_MB);
00699         WriteHandler[3].Install(IS_PC98_ARCH ? 0x77 : 0x43,write_p43,IO_MB);
00700         ReadHandler[0].Install(IS_PC98_ARCH ? 0x71 : 0x40,read_latch,IO_MB);
00701         ReadHandler[1].Install(IS_PC98_ARCH ? 0x73 : 0x41,read_latch,IO_MB);
00702         ReadHandler[2].Install(IS_PC98_ARCH ? 0x75 : 0x42,read_latch,IO_MB);
00703 
00704     /* Apparently all but the first PC-9801 systems have an alias of these
00705      * ports at 0x3FD9-0x3FDF odd. This alias is required for games that
00706      * rely on this alias. */
00707     if (IS_PC98_ARCH) {
00708         WriteHandler2[0].Install(0x3FD9,write_latch,IO_MB);
00709         WriteHandler2[1].Install(0x3FDB,write_latch,IO_MB);
00710         WriteHandler2[2].Install(0x3FDD,write_latch,IO_MB);
00711         WriteHandler2[3].Install(0x3FDF,write_p43,IO_MB);
00712         ReadHandler2[0].Install(0x3FD9,read_latch,IO_MB);
00713         ReadHandler2[1].Install(0x3FDB,read_latch,IO_MB);
00714         ReadHandler2[2].Install(0x3FDD,read_latch,IO_MB);
00715     }
00716 
00717         latched_timerstatus_locked=false;
00718         gate2 = false;
00719 
00720     TIMER_BIOS_INIT_Configure();
00721 }
00722 
00723 void TIMER_Destroy(Section*) {
00724         PIC_RemoveEvents(PIT0_Event);
00725 }
00726 
00727 void TIMER_Init() {
00728         Bitu i;
00729 
00730         LOG(LOG_MISC,LOG_DEBUG)("TIMER_Init()");
00731 
00732     PIT_TICK_RATE = PIT_TICK_RATE_IBM;
00733 
00734         for (i=0;i < 3;i++) {
00735                 pit[i].cntr = 0x10000;
00736                 pit[i].write_state = 0;
00737                 pit[i].read_state = 0;
00738                 pit[i].read_latch = 0;
00739                 pit[i].write_latch = 0;
00740                 pit[i].mode = 0;
00741                 pit[i].bcd = false;
00742                 pit[i].go_read_latch = false;
00743                 pit[i].counterstatus_set = false;
00744                 pit[i].update_count = false;
00745                 pit[i].delay = (1000.0f/((float)PIT_TICK_RATE/(float)pit[i].cntr));
00746         }
00747 
00748         AddExitFunction(AddExitFunctionFuncPair(TIMER_Destroy));
00749         AddVMEventFunction(VM_EVENT_POWERON, AddVMEventFunctionFuncPair(TIMER_OnPowerOn));
00750 
00751     if (IS_PC98_ARCH) /* HACK! Clean this up! */
00752         AddVMEventFunction(VM_EVENT_RESET, AddVMEventFunctionFuncPair(TIMER_OnEnterPC98_Phase2));
00753 }
00754