diff options
Diffstat (limited to 'tools/ioemu/iodev/pit.cc')
-rw-r--r-- | tools/ioemu/iodev/pit.cc | 856 |
1 files changed, 856 insertions, 0 deletions
diff --git a/tools/ioemu/iodev/pit.cc b/tools/ioemu/iodev/pit.cc new file mode 100644 index 0000000000..cf4777a759 --- /dev/null +++ b/tools/ioemu/iodev/pit.cc @@ -0,0 +1,856 @@ +///////////////////////////////////////////////////////////////////////// +// $Id: pit.cc,v 1.15 2003/07/31 12:04:48 vruppert Exp $ +///////////////////////////////////////////////////////////////////////// +// +// Copyright (C) 2001 MandrakeSoft S.A. +// +// MandrakeSoft S.A. +// 43, rue d'Aboukir +// 75002 Paris - France +// http://www.linux-mandrake.com/ +// http://www.mandrakesoft.com/ +// +// This library is free software; you can redistribute it and/or +// modify it under the terms of the GNU Lesser General Public +// License as published by the Free Software Foundation; either +// version 2 of the License, or (at your option) any later version. +// +// This library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public +// License along with this library; if not, write to the Free Software +// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + + +#include "bochs.h" + +#if (BX_USE_NEW_PIT==0) + +#define LOG_THIS bx_pit. + + +// NOTES ON THE 8253/8254 PIT MODES + +// MODE 0: Interrupt on Terminal Count +// =================================== +// Writing new count action: +// loaded upon next CLK pulse. counting doesn't start until GATE=1 +// GATE 0..1 transition: +// ??? +// GATE 1..0 transition: +// counter expiration action: +// wraps to FFFF +// * OUT rises until new count val or new control word for mode 0 written + +// MODE 1: Programmable Monoflop +// ============================= +// Writing new count action: +// not effective for current process +// GATE 0..1 transition: +// loads counter +// counter expiration action: +// wraps to FFFF +// NOTES: +// OUT rises until new count val or new control word for mode 0 written + +// MODE 2: Rate Generator +// ====================== +// Writing new count action: +// ??? +// GATE 0..1 transition: +// loads initial count val and starts counting +// counter expiration action: +// reloads after count expires +// NOTES: +// * after control word & initial count val N loaded, PIT starts +// counting upon next CLK pulse. +// * when counter reaches 1, OUT drops to a low level, for one +// CLK cycle. (short peak pulse generated) +// * afterwards, the initial count val is automatically reloaded +// and the PIT restarts the same counting operation again. +// * distance of two OUT pulses is N CLK cycles long. +// * GATE=1 enables, GATE=0 disables counter. +// * if GATE drops to low level during counting operation and rises +// to high level later, PIT loads initial count value at the +// rise and starts counting. +// * PIT starts counting after last data byte written if GATE=1 +// * if the output is low when the gate goes low, the output is +// immediately set high. + +// MODE 3: Square Wave Generator +// ============================= +// Writing new count action: +// ??? +// GATE 0..1 transition: +// ??? +// counter expiration action: +// reloads after count expires +// NOTES: +// * initially OUT at a high level +// * drop of GATE to a low level while OUT low, raises OUT to a high level +// * a rise from a low to a high level at GATE (trigger pulse), +// loads the counter with the initial count value and starts +// counting operation +// * a new count value supplied during the course of an active +// counting operation doesn't affect the current process. +// At the end of the current half cycle, the PIT loads the new value +// * if the GATE line goes low, count is temporarily halted until GATE +// returns high +// * if the OUT line is high when GATE goes low, OUT is forced low. +// ??? different for odd/even counts + +// MODE 4: Software Triggered Pulse +// ================================ +// Writing new count action: +// ??? +// GATE 0..1 transition: +// ??? +// counter expiration action: +// wraps to FFFF +// NOTES: + +// MODE 5: Hardware Triggered Pulse +// ================================ +// Writing new count action: +// ??? +// GATE 0..1 transition: +// ??? +// counter expiration action: +// wraps to FFFF +// NOTES: + + + +#define BX_PIT_LATCH_MODE_LSB 10 +#define BX_PIT_LATCH_MODE_MSB 11 +#define BX_PIT_LATCH_MODE_16BIT 12 + + +bx_pit_c bx_pit; +#if BX_USE_PIT_SMF +#define this (&bx_pit) +#endif + +#ifdef OUT +# undef OUT +#endif + + +bx_pit_c::bx_pit_c( void ) +{ + put("PIT"); + settype(PITLOG); + memset(&s, 0, sizeof(s)); + + /* 8254 PIT (Programmable Interval Timer) */ + + BX_PIT_THIS s.timer_handle[1] = BX_NULL_TIMER_HANDLE; + BX_PIT_THIS s.timer_handle[2] = BX_NULL_TIMER_HANDLE; +} + +bx_pit_c::~bx_pit_c( void ) +{ +} + + + int +bx_pit_c::init( void ) +{ + DEV_register_irq(0, "8254 PIT"); + DEV_register_ioread_handler(this, read_handler, 0x0040, "8254 PIT", 1); + DEV_register_ioread_handler(this, read_handler, 0x0041, "8254 PIT", 1); + DEV_register_ioread_handler(this, read_handler, 0x0042, "8254 PIT", 1); + DEV_register_ioread_handler(this, read_handler, 0x0043, "8254 PIT", 1); + DEV_register_ioread_handler(this, read_handler, 0x0061, "8254 PIT", 1); + + DEV_register_iowrite_handler(this, write_handler, 0x0040, "8254 PIT", 1); + DEV_register_iowrite_handler(this, write_handler, 0x0041, "8254 PIT", 1); + DEV_register_iowrite_handler(this, write_handler, 0x0042, "8254 PIT", 1); + DEV_register_iowrite_handler(this, write_handler, 0x0043, "8254 PIT", 1); + DEV_register_iowrite_handler(this, write_handler, 0x0061, "8254 PIT", 1); + + BX_PIT_THIS s.speaker_data_on = 0; + BX_PIT_THIS s.refresh_clock_div2 = 0; + + BX_PIT_THIS s.timer[0].mode = 3; /* periodic rate generator */ + BX_PIT_THIS s.timer[0].latch_mode = BX_PIT_LATCH_MODE_16BIT; + BX_PIT_THIS s.timer[0].input_latch_value = 0; + BX_PIT_THIS s.timer[0].input_latch_toggle = 0; + BX_PIT_THIS s.timer[0].output_latch_value = 0; + BX_PIT_THIS s.timer[0].output_latch_toggle = 0; + BX_PIT_THIS s.timer[0].output_latch_full = 0; + BX_PIT_THIS s.timer[0].counter_max = 0; /* 0xFFFF + 1 : (1193182 / 65535 = 18.2Hz) */ + BX_PIT_THIS s.timer[0].counter = 0; /* 0xFFFF + 1 : (1193182 / 65535 = 18.2Hz) */ + BX_PIT_THIS s.timer[0].bcd_mode = 0; /* binary counting mode */ + BX_PIT_THIS s.timer[0].GATE = 1; /* GATE tied to + logic */ + BX_PIT_THIS s.timer[0].OUT = 1; + BX_PIT_THIS s.timer[0].active = 0; + + BX_PIT_THIS s.timer[1].mode = 3; /* periodic rate generator */ + BX_PIT_THIS s.timer[1].latch_mode = BX_PIT_LATCH_MODE_16BIT; + BX_PIT_THIS s.timer[1].input_latch_value = 0; + BX_PIT_THIS s.timer[1].input_latch_toggle = 0; + BX_PIT_THIS s.timer[1].output_latch_value = 0; + BX_PIT_THIS s.timer[1].output_latch_toggle = 0; + BX_PIT_THIS s.timer[1].output_latch_full = 0; + BX_PIT_THIS s.timer[1].counter_max = 0; /* 0xFFFF + 1 : (1193182 / 65535 = 18.2Hz) */ + BX_PIT_THIS s.timer[1].counter = 0; /* 0xFFFF + 1 : (1193182 / 65535 = 18.2Hz) */ + BX_PIT_THIS s.timer[1].bcd_mode = 0; /* binary counting mode */ + BX_PIT_THIS s.timer[1].GATE = 1; /* GATE tied to + logic */ + BX_PIT_THIS s.timer[1].OUT = 1; + BX_PIT_THIS s.timer[1].active = 0; + + BX_PIT_THIS s.timer[2].mode = 3; /* periodic rate generator */ + BX_PIT_THIS s.timer[2].latch_mode = BX_PIT_LATCH_MODE_16BIT; + BX_PIT_THIS s.timer[2].input_latch_value = 0; + BX_PIT_THIS s.timer[2].input_latch_toggle = 0; + BX_PIT_THIS s.timer[2].output_latch_value = 0; + BX_PIT_THIS s.timer[2].output_latch_toggle = 0; + BX_PIT_THIS s.timer[2].output_latch_full = 0; + BX_PIT_THIS s.timer[2].counter_max = 0; /* 0xFFFF + 1 : (1193182 / 65535 = 18.2Hz) */ + BX_PIT_THIS s.timer[2].counter = 0; /* 0xFFFF + 1 : (1193182 / 65535 = 18.2Hz) */ + BX_PIT_THIS s.timer[2].bcd_mode = 0; /* binary counting mode */ + BX_PIT_THIS s.timer[2].GATE = 0; /* timer2 gate controlled by port 61h bit 0 */ + BX_PIT_THIS s.timer[2].OUT = 1; + BX_PIT_THIS s.timer[2].active = 0; + + return(1); +} + +void bx_pit_c::reset(unsigned type) { +} + + // static IO port read callback handler + // redirects to non-static class handler to avoid virtual functions + + Bit32u +bx_pit_c::read_handler(void *this_ptr, Bit32u address, unsigned io_len) +{ +#if !BX_USE_PIT_SMF + bx_pit_c *class_ptr = (bx_pit_c *) this_ptr; + + return( class_ptr->read(address, io_len) ); +} + + + Bit32u +bx_pit_c::read( Bit32u address, unsigned int io_len ) +{ +#else + UNUSED(this_ptr); +#endif // !BX_USE_PIT_SMF + if (bx_dbg.pit) + BX_INFO(("pit: io read from port %04x", (unsigned) address)); + + switch (address) { + case 0x40: /* timer 0 - system ticks */ + return( read_counter(0) ); + break; + + case 0x42: /* timer 2 read */ + return( read_counter(2) ); + break; + + case 0x61: + /* AT, port 61h */ + BX_PIT_THIS s.refresh_clock_div2 = !BX_PIT_THIS s.refresh_clock_div2; + return( (BX_PIT_THIS s.timer[2].OUT<<5) | + (BX_PIT_THIS s.refresh_clock_div2<<4) | + (BX_PIT_THIS s.speaker_data_on<<1) | + (BX_PIT_THIS s.timer[2].GATE) ); + break; + + default: + BX_PANIC(("pit: unsupported io read from port %04x", address)); + } + return(0); /* keep compiler happy */ +} + + + // static IO port write callback handler + // redirects to non-static class handler to avoid virtual functions + + void +bx_pit_c::write_handler(void *this_ptr, Bit32u address, Bit32u dvalue, unsigned io_len) +{ +#if !BX_USE_PIT_SMF + bx_pit_c *class_ptr = (bx_pit_c *) this_ptr; + + class_ptr->write(address, dvalue, io_len); +} + + void +bx_pit_c::write( Bit32u address, Bit32u dvalue, + unsigned int io_len ) +{ +#else + UNUSED(this_ptr); +#endif // !BX_USE_PIT_SMF + Bit8u command, mode, bcd_mode; + Bit8u value; + + value = (Bit8u ) dvalue; + + if (bx_dbg.pit) + BX_INFO(("pit: write to port %04x = %02x", + (unsigned) address, (unsigned) value)); + + switch (address) { + case 0x40: /* timer 0: write count register */ + write_count_reg( value, 0 ); + break; + + case 0x41: /* timer 1: write count register */ + write_count_reg( value, 1 ); + break; + + case 0x42: /* timer 2: write count register */ + write_count_reg( value, 2 ); + break; + + case 0x43: /* timer 0-2 mode control */ + /* |7 6 5 4|3 2 1|0| + * |-------|-----|-| + * |command|mode |bcd/binary| + */ + command = value >> 4; + mode = (value >> 1) & 0x07; + bcd_mode = value & 0x01; +#if 0 +BX_INFO(("timer 0-2 mode control: comm:%02x mode:%02x bcd_mode:%u", + (unsigned) command, (unsigned) mode, (unsigned) bcd_mode)); +#endif + + if ( (mode > 5) || (command > 0x0e) ) + BX_PANIC(("pit: outp(43h)=%02xh out of range", (unsigned) value)); + if (bcd_mode) + BX_PANIC(("pit: outp(43h)=%02xh: bcd mode unhandled", + (unsigned) bcd_mode)); + + switch (command) { + case 0x0: /* timer 0: counter latch */ + latch( 0 ); + break; + + case 0x1: /* timer 0: LSB mode */ + case 0x2: /* timer 0: MSB mode */ + BX_PANIC(("pit: outp(43h): command %02xh unhandled", + (unsigned) command)); + break; + case 0x3: /* timer 0: 16-bit mode */ + BX_PIT_THIS s.timer[0].mode = mode; + BX_PIT_THIS s.timer[0].latch_mode = BX_PIT_LATCH_MODE_16BIT; + BX_PIT_THIS s.timer[0].input_latch_value = 0; + BX_PIT_THIS s.timer[0].input_latch_toggle = 0; + BX_PIT_THIS s.timer[0].bcd_mode = bcd_mode; + if ( (mode!=3 && mode!=2 && mode!=0) || bcd_mode!=0 ) + BX_PANIC(("pit: outp(43h): comm 3, mode %02x, bcd %02x unhandled", + (unsigned) mode, bcd_mode)); + break; + case 0x4: /* timer 1: counter latch */ + latch( 1 ); + break; + + case 0x5: /* timer 1: LSB mode */ + case 0x6: /* timer 1: MSB mode */ + BX_INFO(("pit: outp(43h): command %02xh unhandled (ignored)", + (unsigned) command)); + break; + case 0x7: /* timer 1: 16-bit mode */ + BX_PIT_THIS s.timer[1].mode = mode; + BX_PIT_THIS s.timer[1].latch_mode = BX_PIT_LATCH_MODE_16BIT; + BX_PIT_THIS s.timer[1].input_latch_value = 0; + BX_PIT_THIS s.timer[1].input_latch_toggle = 0; + BX_PIT_THIS s.timer[1].bcd_mode = bcd_mode; + if ( (mode!=3 && mode!=2 && mode!=0) || bcd_mode!=0 ) + BX_PANIC(("pit: outp(43h): comm 7, mode %02x, bcd %02x unhandled", + (unsigned) mode, bcd_mode)); + break; + case 0x8: /* timer 2: counter latch */ + latch( 2 ); + break; + + case 0x9: /* timer 2: LSB mode */ + case 0xa: /* timer 2: MSB mode */ + BX_PANIC(("pit: outp(43h): command %02xh unhandled", + (unsigned) command)); + break; + case 0xb: /* timer 2: 16-bit mode */ + BX_PIT_THIS s.timer[2].mode = mode; + BX_PIT_THIS s.timer[2].latch_mode = BX_PIT_LATCH_MODE_16BIT; + BX_PIT_THIS s.timer[2].input_latch_value = 0; + BX_PIT_THIS s.timer[2].input_latch_toggle = 0; + BX_PIT_THIS s.timer[2].bcd_mode = bcd_mode; + if ( (mode!=3 && mode!=2 && mode!=0) || bcd_mode!=0 ) + BX_PANIC(("pit: outp(43h): comm Bh, mode %02x, bcd %02x unhandled", + (unsigned) mode, bcd_mode)); + break; +#if 0 + case 0xd: /* general counter latch */ + if (value & 0x08) /* select counter 2 */ + latch( 2 ); + if (value & 0x04) /* select counter 1 */ + latch( 1 ); + if (value & 0x02) /* select counter 0 */ + latch( 0 ); + break; + + case 0xe: /* latch status of timers */ + BX_PANIC(("pit: outp(43h): command %02xh unhandled", + (unsigned) command); + break; +#endif + case 0x0c: case 0x0d: case 0x0e: case 0x0f: + BX_INFO(("pit: ignoring 8254 command %u", (unsigned) command)); + break; + + default: /* 0xc & 0xf */ + BX_PANIC(("pit: outp(43h) command %1xh unhandled", + (unsigned) command)); + break; + } + break; + + case 0x61: + BX_PIT_THIS s.speaker_data_on = (value >> 1) & 0x01; +/*??? only on AT+ */ + set_GATE(2, value & 0x01); +#if BX_CPU_LEVEL < 2 + /* ??? XT: */ + bx_kbd_port61h_write(value); +#endif + break; + + default: + BX_PANIC(("pit: unsupported io write to port %04x = %02x", + (unsigned) address, (unsigned) value)); + } +} + + + + + void +bx_pit_c::write_count_reg( Bit8u value, unsigned timerid ) +{ + bx_bool xfer_complete; + + switch ( BX_PIT_THIS s.timer[timerid].latch_mode ) { + case BX_PIT_LATCH_MODE_16BIT: /* write1=LSB, write2=MSB */ + if (BX_PIT_THIS s.timer[timerid].input_latch_toggle==0) { + BX_PIT_THIS s.timer[timerid].input_latch_value = value; + BX_PIT_THIS s.timer[timerid].input_latch_toggle = 1; + xfer_complete = 0; + if (bx_dbg.pit) + BX_INFO(("pit: BX_PIT_THIS s.timer[timerid] write L = %02x", (unsigned) value)); + } + else { + BX_PIT_THIS s.timer[timerid].input_latch_value |= (value << 8); + BX_PIT_THIS s.timer[timerid].input_latch_toggle = 0; + xfer_complete = 1; + if (bx_dbg.pit) + BX_INFO(("pit: BX_PIT_THIS s.timer[timerid] write H = %02x", (unsigned) value)); + } + break; + + case BX_PIT_LATCH_MODE_MSB: /* write1=MSB, LSB=0 */ + BX_PIT_THIS s.timer[timerid].input_latch_value = (value << 8); + xfer_complete = 1; + if (bx_dbg.pit) + BX_INFO(("pit: BX_PIT_THIS s.timer[timerid] write H = %02x", (unsigned) value)); + break; + + case BX_PIT_LATCH_MODE_LSB: /* write1=LSB, MSB=0 */ + BX_PIT_THIS s.timer[timerid].input_latch_value = value; + xfer_complete = 1; + if (bx_dbg.pit) + BX_INFO(("pit: BX_PIT_THIS s.timer[timerid] write L = %02x", (unsigned) value)); + break; + + default: + BX_PANIC(("write_count_reg: latch_mode unknown")); + xfer_complete = 0; + } + + if (xfer_complete) { + BX_PIT_THIS s.timer[timerid].counter_max = BX_PIT_THIS s.timer[timerid].input_latch_value; + + // reprogramming counter clears latch + BX_PIT_THIS s.timer[timerid].output_latch_full = 0; + + // counter bounds + // mode minimum maximum + // 0 1 0 + // 1 1 0 + // 2 2 0 + // 3 2 0 + // 4 1 0 + // 5 1 0 + switch (BX_PIT_THIS s.timer[timerid].mode) { + case 0: + BX_PIT_THIS s.timer[timerid].counter = BX_PIT_THIS s.timer[timerid].counter_max; + BX_PIT_THIS s.timer[timerid].active = 1; + if (BX_PIT_THIS s.timer[timerid].GATE) { + BX_PIT_THIS s.timer[timerid].OUT = 0; // OUT pin starts low + start( timerid ); + } + break; + case 1: + BX_PANIC(("pit:write_count_reg(%u): mode1 unsupported", + timerid)); + break; + case 2: + if ( BX_PIT_THIS s.timer[timerid].counter_max == 1 ) + BX_PANIC(("pit:write_count_reg(%u): mode %u counter_max=1", + timerid, (unsigned) BX_PIT_THIS s.timer[timerid].mode)); + if ( BX_PIT_THIS s.timer[timerid].GATE && !BX_PIT_THIS s.timer[timerid].active ) { + // software triggered + BX_PIT_THIS s.timer[timerid].counter = BX_PIT_THIS s.timer[timerid].counter_max; + BX_PIT_THIS s.timer[timerid].active = 1; + BX_PIT_THIS s.timer[timerid].OUT = 1; // initially set high + start( timerid ); + } + break; + case 3: + if ( BX_PIT_THIS s.timer[timerid].counter_max == 1 ) + BX_PANIC(("pit:write_count_reg(%u): mode %u counter_max=1", + timerid, (unsigned) BX_PIT_THIS s.timer[timerid].mode)); + BX_PIT_THIS s.timer[timerid].counter_max = BX_PIT_THIS s.timer[timerid].counter_max & 0xfffe; + if ( BX_PIT_THIS s.timer[timerid].GATE && !BX_PIT_THIS s.timer[timerid].active ) { + // software triggered + BX_PIT_THIS s.timer[timerid].counter = BX_PIT_THIS s.timer[timerid].counter_max; + BX_PIT_THIS s.timer[timerid].active = 1; + BX_PIT_THIS s.timer[timerid].OUT = 1; // initially set high + start( timerid ); + } + break; + case 4: + BX_PANIC(("pit:write_count_reg(%u): mode4 unsupported", + timerid)); + break; + case 5: + BX_PANIC(("pit:write_count_reg(%u): mode5 unsupported", + timerid)); + break; + } + } +} + + + Bit8u +bx_pit_c::read_counter( unsigned timerid ) +{ + Bit16u counter_value; + Bit8u retval; + + if (BX_PIT_THIS s.timer[timerid].output_latch_full) { /* latched read */ + counter_value = BX_PIT_THIS s.timer[timerid].output_latch_value; + } + else { /* direct unlatched read */ + counter_value = BX_PIT_THIS s.timer[timerid].counter; +BX_INFO(("CV=%04x", (unsigned) BX_PIT_THIS s.timer[timerid].counter)); + } + + switch (BX_PIT_THIS s.timer[timerid].latch_mode) { + case BX_PIT_LATCH_MODE_LSB: + retval = (Bit8u ) counter_value; + BX_PIT_THIS s.timer[timerid].output_latch_full = 0; + break; + case BX_PIT_LATCH_MODE_MSB: + retval = (Bit8u ) ( counter_value >> 8 ); + BX_PIT_THIS s.timer[timerid].output_latch_full = 0; + break; + case BX_PIT_LATCH_MODE_16BIT: + if (BX_PIT_THIS s.timer[timerid].output_latch_toggle==0) { /* LSB 1st */ + retval = (Bit8u ) counter_value; + } + else { /* MSB 2nd */ + retval = (Bit8u ) ( counter_value >> 8 ); + } + BX_PIT_THIS s.timer[timerid].output_latch_toggle = !BX_PIT_THIS s.timer[timerid].output_latch_toggle; + if (BX_PIT_THIS s.timer[timerid].output_latch_toggle == 0) + BX_PIT_THIS s.timer[timerid].output_latch_full = 0; + break; + default: + BX_PANIC(("pit: io read from port 40h: unknown latch mode")); + retval = 0; /* keep compiler happy */ + } + return( retval ); +} + + + void +bx_pit_c::latch( unsigned timerid ) +{ + /* subsequent counter latch commands are ignored until value read out */ + if (BX_PIT_THIS s.timer[timerid].output_latch_full) { + BX_INFO(("pit: pit(%u) latch: output latch full, ignoring", + timerid)); + return; + } + + BX_PIT_THIS s.timer[timerid].output_latch_value = BX_PIT_THIS s.timer[timerid].counter; + + if (bx_dbg.pit) + BX_INFO(("pit: latch_value = %u", (unsigned) BX_PIT_THIS s.timer[timerid].output_latch_value)); + BX_PIT_THIS s.timer[timerid].output_latch_toggle = 0; + BX_PIT_THIS s.timer[timerid].output_latch_full = 1; +} + + void +bx_pit_c::set_GATE(unsigned pit_id, unsigned value) +{ + // GATE's for Timer 0 & Timer 1 are tied high. + if (pit_id != 2) + BX_PANIC(("pit:set_GATE: pit_id != 2")); + + value = (value > 0); + + /* if no transition of GATE input line, then nothing to do */ + if (value == BX_PIT_THIS s.timer[2].GATE) + return; + + if (value) { /* PIT2: GATE transition from 0 to 1 */ + BX_PIT_THIS s.timer[2].GATE = 1; + switch ( BX_PIT_THIS s.timer[2].mode ) { + case 0: + BX_PIT_THIS s.timer[2].counter = BX_PIT_THIS s.timer[2].counter_max; + if (BX_PIT_THIS s.timer[2].active) { + BX_PIT_THIS s.timer[2].OUT = 0; + } + start( 2 ); + break; + case 2: + // begin counting, reload counter + BX_PIT_THIS s.timer[2].active = 1; + BX_PIT_THIS s.timer[2].OUT = 1; + BX_PIT_THIS s.timer[2].counter = BX_PIT_THIS s.timer[2].counter_max; + start( 2 ); + break; + case 3: + // begin counting, reload counter + BX_PIT_THIS s.timer[2].active = 1; + BX_PIT_THIS s.timer[2].OUT = 1; + BX_PIT_THIS s.timer[2].counter = BX_PIT_THIS s.timer[2].counter_max; + start( 2 ); + break; + case 1: + case 4: + case 5: + default: + BX_PANIC(("bx_pit_c::set_GATE: unhandled timer2 mode %u", + (unsigned) BX_PIT_THIS s.timer[2].mode)); + } + } + else { // PIT2: GATE transition from 1 to 0, deactivate + BX_PIT_THIS s.timer[2].GATE = 0; + switch ( BX_PIT_THIS s.timer[2].mode ) { + case 0: + break; + case 2: + // 1) stops count, 2) OUT goes immediately high + BX_PIT_THIS s.timer[2].active = 0; + BX_PIT_THIS s.timer[2].OUT = 1; + break; + case 3: + // 1) stops count, 2) OUT goes immediately high + BX_PIT_THIS s.timer[2].active = 0; + BX_PIT_THIS s.timer[2].OUT = 1; + break; + case 1: + case 4: + case 5: + default: + BX_PANIC(("bx_pit_c::set_GATE: unhandled timer2 mode %u", + (unsigned) BX_PIT_THIS s.timer[2].mode)); + } + } +} + + + void +bx_pit_c::start(unsigned timerid) +{ + unsigned long period_hz; + + if (BX_PIT_THIS s.timer[timerid].counter_max == 0x0000) { + period_hz = 1193182 / 65536; + } + else { + period_hz = 1193182 / BX_PIT_THIS s.timer[timerid].counter_max; + } + BX_INFO(("timer%u period set to %lu hz", timerid, period_hz)); + + + switch (BX_PIT_THIS s.timer[timerid].mode) { + case 0: /* single timeout */ + break; + case 1: /* retriggerable one-shot */ + BX_PANIC(("start: mode %u unhandled", + (unsigned) BX_PIT_THIS s.timer[timerid].mode)); + break; + case 2: /* rate generator */ + break; + case 3: /* square wave mode */ + break; + case 4: /* software triggered strobe */ + BX_PANIC(("start: mode %u unhandled", + (unsigned) BX_PIT_THIS s.timer[timerid].mode)); + break; + case 5: /* hardware retriggerable strobe */ + BX_PANIC(("start: mode %u unhandled", + (unsigned) BX_PIT_THIS s.timer[timerid].mode)); + break; + default: + BX_PANIC(("start: timer%u has bad mode", + (unsigned) BX_PIT_THIS s.timer[timerid].mode)); + } +} + + + + + int +bx_pit_c::SaveState( class state_file *fd ) +{ + fd->write_check ("8254 start"); + fd->write (&BX_PIT_THIS s, sizeof (BX_PIT_THIS s)); + fd->write_check ("8254 end"); + return(0); +} + + + int +bx_pit_c::LoadState( class state_file *fd ) +{ + fd->read_check ("8254 start"); + fd->read (&BX_PIT_THIS s, sizeof (BX_PIT_THIS s)); + fd->read_check ("8254 end"); + return(0); +} + + +#if 0 + void +bx_kbd_port61h_write(Bit8u value) +{ +// PcError("KBD_PORT61H_WRITE(): not implemented yet"); + UNUSED( value ); +} +#endif + + + bx_bool +bx_pit_c::periodic( Bit32u usec_delta ) +{ + bx_bool prev_timer0_out; + + prev_timer0_out = BX_PIT_THIS s.timer[0].OUT; + + for (unsigned i = 0; i < 3; i++) { + // is timer enabled and active? + if ( BX_PIT_THIS s.timer[i].GATE && BX_PIT_THIS s.timer[i].active ) { + switch ( BX_PIT_THIS s.timer[i].mode ) { + case 0: // Mode 0: Single Timeout + // wraps after count expires + if ( BX_PIT_THIS s.timer[i].counter == 0 ) { + // counter previously expired, wrap counter + BX_PIT_THIS s.timer[i].counter = 0xffff; + } + else if ( usec_delta >= BX_PIT_THIS s.timer[i].counter ) { + // counter expired + BX_PIT_THIS s.timer[i].counter = 0; + BX_PIT_THIS s.timer[i].OUT = 1; + } + else { + // decrement counter by elapsed useconds + BX_PIT_THIS s.timer[i].counter -= (Bit16u ) usec_delta; + } + break; + + case 1: // Mode 1: Retriggerable One-Shot + // wraps after count expires + BX_PANIC(("bx_pit_c::periodic: bad mode: timer[%u], mode %u", + i, (unsigned) BX_PIT_THIS s.timer[i].mode)); + break; + + case 2: // Mode 2: Rate Generator + // reloads after count expires + // OUT is low when counter=1, high otherwise + // min count=2, max count=0 + if ( BX_PIT_THIS s.timer[i].counter == 0 ) { + // max counter val, just wrap + BX_PIT_THIS s.timer[i].counter = 0xffff; + BX_PIT_THIS s.timer[i].OUT = 1; + } + else if ( BX_PIT_THIS s.timer[i].counter == 1 ) { + // counter previously expired, reload + BX_PIT_THIS s.timer[i].counter = BX_PIT_THIS s.timer[i].counter_max; + BX_PIT_THIS s.timer[i].OUT = 1; + } + else if ( (BX_PIT_THIS s.timer[i].counter == 2) || + (usec_delta >= (Bit32u(BX_PIT_THIS s.timer[i].counter) - 1)) ) { + // in either case, counter will reach 1 + BX_PIT_THIS s.timer[i].counter = 1; + BX_PIT_THIS s.timer[i].OUT = 0; + } + else { + // decrement counter by elapsed useconds + BX_PIT_THIS s.timer[i].counter -= (Bit16u ) usec_delta; + } + break; + + case 3: // Mode 3: Square Wave Mode + // reloads after count expires + // min count=2, max count=0 + if ( BX_PIT_THIS s.timer[i].counter == 0 ) { + // max count, dec by 2 + BX_PIT_THIS s.timer[i].counter = 0xfffe; + } + else if ( (BX_PIT_THIS s.timer[i].counter <= 2) || + ( (usec_delta*2) >= BX_PIT_THIS s.timer[i].counter ) ) { + // counter expired, reload + BX_PIT_THIS s.timer[i].counter = BX_PIT_THIS s.timer[i].counter_max; + BX_PIT_THIS s.timer[i].OUT = !BX_PIT_THIS s.timer[i].OUT; + //BX_INFO(("CV: reload t%u to %04x", (unsigned) i, (unsigned) + // BX_PIT_THIS s.timer[i].counter)); + } + else { + // decrement counter by elapsed useconds + BX_PIT_THIS s.timer[i].counter -= (Bit16u ) ( 2*usec_delta ); + //BX_INFO(("CV: dec count to %04x", + // (unsigned) BX_PIT_THIS s.timer[i].counter)); + } + break; + + case 4: // Mode 4: Software Triggered Strobe + // wraps after count expires + BX_PANIC(("bx_pit_c::periodic: bad mode: timer[%u], mode %u", + i, (unsigned) BX_PIT_THIS s.timer[i].mode)); + break; + + case 5: // Mode 5: Hardware Retriggerable Strobe + // wraps after count expires + BX_PANIC(("bx_pit_c::periodic: bad mode: timer[%u], mode %u", + i, (unsigned) BX_PIT_THIS s.timer[i].mode)); + break; + default: + BX_PANIC(("bx_pit_c::periodic: bad mode: timer[%u], mode %u", + i, (unsigned) BX_PIT_THIS s.timer[i].mode)); + break; + } // switch ( BX_PIT_THIS s.tim... + } // if ( BX_PIT_THIS s.timer[i]... + } // for (unsigned i... + + // see if there's a rising edge on timer0's output to trigger an IRQ0. + if ( (prev_timer0_out==0) && (BX_PIT_THIS s.timer[0].OUT==1) ) + return(1); // request IRQ 0 + else + return(0); +} + +#endif // #if (BX_USE_NEW_PIT==0) |