Arduino IDE program to control 8253 Programmable interval timer

Here's an attempt at a parallel version. It uses all of port 'C' for the data bus and uses direct port manipulation for simplicity.

Because the use of a parallel I/O port allows reading I added that functionality too (untested!)

/*
 * Sketch: sketch_oct13c.ino
 * Target:  Teensy 2.0++
 *
 *  Uses parallel data for the 8253 to
 *  (hopefully!) demonstrate loading timers 
 *  to produce square waves.
 */

typedef enum
{
    COUNTER0 = 0,   //00
    COUNTER1,       //01
    COUNTER2,       //10
    WR_MODE_WORD    //11
    
}e8253_ADDRS_t;

//parallel data to 8253
const uint8_t pinD0 = 10;   //PORTC0    declarations for clarity only
const uint8_t pinD1 = 11;   //...
const uint8_t pinD2 = 12;
const uint8_t pinD3 = 13;
const uint8_t pinD4 = 14;
const uint8_t pinD5 = 15;
const uint8_t pinD6 = 16;
const uint8_t pinD7 = 17;   //PORTC7

const uint8_t pinCS = 2;        //to 8253 chip-select
const uint8_t pinA0 = 3;        //address A0
const uint8_t pinA1 = 4;        //address A1
const uint8_t pinRD = 5;        //!read
const uint8_t pinWR = 6;        //!write

uint8_t timerData;
uint16_t timerValue;

void setup() 
{
    //set data direction to input initially
    DDRC = 0x00;
    PORTC = 0x00;            
    pinMode( pinCS, OUTPUT );
    pinMode( pinA0, OUTPUT );
    pinMode( pinA1, OUTPUT );
    pinMode( pinRD, OUTPUT );
    pinMode( pinWR, OUTPUT );

    digitalWrite( pinCS, HIGH );
    digitalWrite( pinWR, HIGH );
    digitalWrite( pinRD, HIGH );

    //counter 0
    timerData = 0b00110110;     //counter 0, load least then most, Mode 3, binary count
    timerValue = 0x7fff;
    Write8253( WR_MODE_WORD, timerData );
    Write8253( COUNTER0, (uint8_t)(timerValue & 0x00ff) );
    Write8253( COUNTER0, (uint8_t)((timerValue >> 8) & 0x00ff) );

    //counter 1
    timerData = 0b01110110;     //counter 1, load least then most, Mode 3, binary count
    timerValue = 0xfff5;
    Write8253( WR_MODE_WORD, timerData );
    Write8253( COUNTER1, (uint8_t)(timerValue & 0x00ff) );
    Write8253( COUNTER1, (uint8_t)((timerValue >> 8) & 0x00ff) );

    //counter 2
    timerData = 0b10110110;     //counter 2, load least then most, Mode 3, binary count
    timerValue = 0x1000;
    Write8253( WR_MODE_WORD, timerData );
    Write8253( COUNTER2, (uint8_t)(timerValue & 0x00ff) );
    Write8253( COUNTER2, (uint8_t)((timerValue >> 8) & 0x00ff) );
    
}//setup

void loop() 
{
    //nothing to do in loop
    //for this proof of concept
    
}//loop

void Write8253( uint8_t address, uint8_t dataval )
{
    //set databus
    PORTC = dataval;    //set databus value
    DDRC = 0xff;        //and drive to pins (make pins outputs)
        
    //set 8253 address pins
    digitalWrite( pinA0, (address & 0x01) ? HIGH:LOW );
    digitalWrite( pinA1, (address & 0x02) ? HIGH:LOW );

    //wiggle CS and WR to send to 8253
    digitalWrite( pinCS, LOW );     //set chip select
    delayMicroseconds( 10 );        //wait >> Taw
    digitalWrite( pinWR, LOW );     //assert WR low
    delayMicroseconds( 10 );        //wait >> Tww
    digitalWrite( pinWR, HIGH );    //de-assert WR
    delayMicroseconds( 10 );        //wait >> Twa
    digitalWrite( pinCS, HIGH );    //de-assert CS

    //set databus back to default HiZ input state
    DDRC = 0x00;
        
}//Write8253

uint16_t Read8253( uint8_t counter )
{
    //we can only read counters, not the mode register
    if( counter > 2 )
        return 0x0000;
        
    //write a mode word that latches the count for the desired counter
    uint8_t mask = (counter & 0x03) << 6;    //set SC1:0 to counter address, RL1:0 low
    Write8253( WR_MODE_WORD, mask );        //write latches counter value for read

    //issue two reads to get the latched counter value (LSB read first)
    return( ReadByte(counter) + (ReadByte(counter) << 8) );
        
}//Read8253

uint8_t ReadByte( uint8_t address )
{
    //set the address pins
    digitalWrite( pinA0, (address & 0x01) ? HIGH:LOW );
    digitalWrite( pinA1, (address & 0x02) ? HIGH:LOW );
    digitalWrite( pinCS, LOW );     //assert chip select low
    delayMicroseconds( 10 );        //wait >> Tar
    digitalWrite( pinRD, LOW );     //assert RD low
    delayMicroseconds( 10 );        //wait >> Trr
    digitalWrite( pinRD, HIGH );    //de-assert RD and read the byte from the bus
    uint8_t mask = PINC;
    delayMicroseconds( 10 );        //wait >> Twa
    digitalWrite( pinCS, HIGH );    //de-assert CS

    return mask;
    
}//ReadByte