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