2001-04-10 06:20:02 +04:00
|
|
|
// Copyright (C) 2001 MandrakeSoft S.A.
|
2001-04-10 05:04:59 +04:00
|
|
|
//
|
|
|
|
// 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"
|
|
|
|
|
|
|
|
|
|
|
|
// 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
|
|
|
|
|
|
|
|
|
|
|
|
#if BX_USE_PIT_SMF
|
|
|
|
bx_pit_c bx_pit;
|
|
|
|
#define this (&bx_pit)
|
|
|
|
#endif
|
|
|
|
|
|
|
|
#ifdef OUT
|
|
|
|
# undef OUT
|
|
|
|
#endif
|
|
|
|
|
|
|
|
|
|
|
|
bx_pit_c::bx_pit_c( void )
|
|
|
|
{
|
|
|
|
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( bx_devices_c *d )
|
|
|
|
{
|
|
|
|
BX_PIT_THIS devices = d;
|
|
|
|
|
|
|
|
BX_PIT_THIS devices->register_irq(0, "8254 PIT");
|
|
|
|
BX_PIT_THIS devices->register_io_read_handler(this, read_handler, 0x0040, "8254 PIT");
|
|
|
|
BX_PIT_THIS devices->register_io_read_handler(this, read_handler, 0x0041, "8254 PIT");
|
|
|
|
BX_PIT_THIS devices->register_io_read_handler(this, read_handler, 0x0042, "8254 PIT");
|
|
|
|
BX_PIT_THIS devices->register_io_read_handler(this, read_handler, 0x0043, "8254 PIT");
|
|
|
|
BX_PIT_THIS devices->register_io_read_handler(this, read_handler, 0x0061, "8254 PIT");
|
|
|
|
|
|
|
|
BX_PIT_THIS devices->register_io_write_handler(this, write_handler, 0x0040, "8254 PIT");
|
|
|
|
BX_PIT_THIS devices->register_io_write_handler(this, write_handler, 0x0041, "8254 PIT");
|
|
|
|
BX_PIT_THIS devices->register_io_write_handler(this, write_handler, 0x0042, "8254 PIT");
|
|
|
|
BX_PIT_THIS devices->register_io_write_handler(this, write_handler, 0x0043, "8254 PIT");
|
|
|
|
BX_PIT_THIS devices->register_io_write_handler(this, write_handler, 0x0061, "8254 PIT");
|
|
|
|
|
|
|
|
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);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// 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 (io_len > 1)
|
|
|
|
bx_panic("pit: io read from port %04x, len=%u\n", (unsigned) address,
|
|
|
|
(unsigned) io_len);
|
|
|
|
|
|
|
|
if (bx_dbg.pit)
|
|
|
|
bx_printf("pit: io read from port %04x\n", (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\n", 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, value, 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 (io_len > 1)
|
|
|
|
bx_panic("pit: io write to port %04x, len=%u\n", (unsigned) address,
|
|
|
|
(unsigned) io_len);
|
|
|
|
|
|
|
|
if (bx_dbg.pit)
|
|
|
|
bx_printf("pit: write to port %04x = %02x\n",
|
|
|
|
(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_printf("timer 0-2 mode control: comm:%02x mode:%02x bcd_mode:%u\n",
|
|
|
|
(unsigned) command, (unsigned) mode, (unsigned) bcd_mode);
|
|
|
|
#endif
|
|
|
|
|
|
|
|
if ( (mode > 5) || (command > 0x0e) )
|
|
|
|
bx_panic("pit: outp(43h)=%02xh out of range\n", (unsigned) value);
|
|
|
|
if (bcd_mode)
|
|
|
|
bx_panic("pit: outp(43h)=%02xh: bcd mode unhandled\n",
|
|
|
|
(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\n",
|
|
|
|
(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\n",
|
|
|
|
(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_printf("pit: outp(43h): command %02xh unhandled (ignored)\n",
|
|
|
|
(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;
|
2001-04-10 06:14:37 +04:00
|
|
|
if ( (mode!=3 && mode!=2 && mode!=0) || bcd_mode!=0 )
|
2001-04-10 05:04:59 +04:00
|
|
|
bx_panic("pit: outp(43h): comm 7, mode %02x, bcd %02x unhandled\n",
|
|
|
|
(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\n",
|
|
|
|
(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;
|
2001-04-10 06:14:37 +04:00
|
|
|
if ( (mode!=3 && mode!=2 && mode!=0) || bcd_mode!=0 )
|
2001-04-10 05:04:59 +04:00
|
|
|
bx_panic("pit: outp(43h): comm Bh, mode %02x, bcd %02x unhandled\n",
|
|
|
|
(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\n",
|
|
|
|
(unsigned) command);
|
|
|
|
break;
|
|
|
|
#endif
|
|
|
|
case 0x0c: case 0x0d: case 0x0e: case 0x0f:
|
|
|
|
bx_printf("pit: ignoring 8254 command %u\n", (unsigned) command);
|
|
|
|
break;
|
|
|
|
|
|
|
|
default: /* 0xc & 0xf */
|
|
|
|
bx_panic("pit: outp(43h) command %1xh unhandled\n",
|
|
|
|
(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\n",
|
|
|
|
(unsigned) address, (unsigned) value);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
bx_pit_c::write_count_reg( Bit8u value, unsigned timerid )
|
|
|
|
{
|
|
|
|
Boolean 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_printf("pit: BX_PIT_THIS s.timer[timerid] write L = %02x\n", (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_printf("pit: BX_PIT_THIS s.timer[timerid] write H = %02x\n", (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_printf("pit: BX_PIT_THIS s.timer[timerid] write H = %02x\n", (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_printf("pit: BX_PIT_THIS s.timer[timerid] write L = %02x\n", (unsigned) value);
|
|
|
|
break;
|
|
|
|
|
|
|
|
default:
|
|
|
|
bx_panic("write_count_reg: latch_mode unknown\n");
|
|
|
|
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\n",
|
|
|
|
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\n",
|
|
|
|
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\n",
|
|
|
|
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\n",
|
|
|
|
timerid);
|
|
|
|
break;
|
|
|
|
case 5:
|
|
|
|
bx_panic("pit:write_count_reg(%u): mode5 unsupported\n",
|
|
|
|
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_printf("CV=%04x\n", (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\n");
|
|
|
|
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_printf("pit: pit(%u) latch: output latch full, ignoring\n",
|
|
|
|
timerid);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
BX_PIT_THIS s.timer[timerid].output_latch_value = BX_PIT_THIS s.timer[timerid].counter;
|
|
|
|
|
|
|
|
if (bx_dbg.pit)
|
|
|
|
bx_printf("pit: latch_value = %lu\n", 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\n");
|
|
|
|
|
|
|
|
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\n",
|
|
|
|
(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\n",
|
|
|
|
(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_printf("timer%u period set to %lu hz\n", 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\n",
|
|
|
|
(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\n",
|
|
|
|
(unsigned) BX_PIT_THIS s.timer[timerid].mode);
|
|
|
|
break;
|
|
|
|
case 5: /* hardware retriggerable strobe */
|
|
|
|
bx_panic("start: mode %u unhandled\n",
|
|
|
|
(unsigned) BX_PIT_THIS s.timer[timerid].mode);
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
bx_panic("start: timer%u has bad mode\n",
|
|
|
|
(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
|
|
|
|
|
|
|
|
|
|
|
|
Boolean
|
|
|
|
bx_pit_c::periodic( Bit32u usec_delta )
|
|
|
|
{
|
|
|
|
Boolean 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\n",
|
|
|
|
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_printf("CV: reload t%u to %04x\n", (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_printf("CV: dec count to %04x\n",
|
|
|
|
// (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\n",
|
|
|
|
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\n",
|
|
|
|
i, (unsigned) BX_PIT_THIS s.timer[i].mode);
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
bx_panic("bx_pit_c::periodic: bad mode: timer[%u], mode %u\n",
|
|
|
|
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);
|
|
|
|
}
|