i8253.cpp
1 #include <stdint.h> 2 #include <string.h> 3 #include <Arduino.h> 4 5 #ifdef MEGA 6 #include <TimerOne.h> 7 #else 8 IntervalTimer myTimer; 9 #endif 10 11 #define PIT_MODE_LATCHCOUNT 0 12 #define PIT_MODE_LOBYTE 1 13 #define PIT_MODE_HIBYTE 2 14 #define PIT_MODE_TOGGLE 3 15 16 17 struct i8253_s { 18 uint16_t chandata[3]; 19 uint8_t accessmode[3]; 20 uint8_t bytetoggle[3]; 21 uint32_t effectivedata[3]; 22 float chanfreq[3]; 23 uint8_t active[3]; 24 uint16_t counter[3]; 25 } i8253; 26 27 volatile uint8_t timerTick = 0; 28 29 void timer_isr() { 30 timerTick = 1; 31 } 32 33 void out8253 (uint16_t portnum, uint8_t value) { 34 uint8_t curbyte; 35 portnum &= 3; 36 switch (portnum) { 37 case 0: 38 case 1: 39 case 2: //channel data 40 if ( (i8253.accessmode[portnum] == PIT_MODE_LOBYTE) || ( (i8253.accessmode[portnum] == PIT_MODE_TOGGLE) && (i8253.bytetoggle[portnum] == 0) ) ) curbyte = 0; 41 else if ( (i8253.accessmode[portnum] == PIT_MODE_HIBYTE) || ( (i8253.accessmode[portnum] == PIT_MODE_TOGGLE) && (i8253.bytetoggle[portnum] == 1) ) ) curbyte = 1; 42 if (curbyte == 0) { //low byte 43 i8253.chandata[portnum] = (i8253.chandata[portnum] & 0xFF00) | value; 44 } 45 else { //high byte 46 i8253.chandata[portnum] = (i8253.chandata[portnum] & 0x00FF) | ( (uint16_t) value << 8); 47 } 48 if (i8253.chandata[portnum] == 0) i8253.effectivedata[portnum] = 65536; 49 else i8253.effectivedata[portnum] = i8253.chandata[portnum]; 50 i8253.active[portnum] = 1; 51 if (i8253.accessmode[portnum] == PIT_MODE_TOGGLE) i8253.bytetoggle[portnum] = (~i8253.bytetoggle[portnum]) & 1; 52 //i8253.chanfreq[portnum] = (float) ( (uint32_t) ( ( (float) 1193182.0 / (float) i8253.effectivedata[portnum]) * (float) 1000.0) ); 53 //Serial.print("period "); Serial.println((uint32_t) ((float)1000000.0 / ( ( (float) 1193182.0 / (float) i8253.effectivedata[portnum])))); 54 if (portnum == 0) { 55 uint32_t period; 56 period = (uint32_t) ((float)1000000.0 / ( ( (float) 1193182.0 / (float) i8253.effectivedata[portnum]))); 57 #ifdef MEGA 58 if (period < 4000) period = 4000; //limit to 250 Hz, or the emulator just can't keep up on a Mega 59 //Serial.println((float)1000000.0 / (float)period); 60 Timer1.attachInterrupt(timer_isr, period); 61 #else 62 myTimer.begin(timer_isr, period); 63 #endif 64 } 65 break; 66 case 3: //mode/command 67 i8253.accessmode[value>>6] = (value >> 4) & 3; 68 if (i8253.accessmode[value>>6] == PIT_MODE_TOGGLE) i8253.bytetoggle[value>>6] = 0; 69 break; 70 } 71 } 72 73 uint8_t in8253 (uint16_t portnum) { 74 uint8_t curbyte; 75 portnum &= 3; 76 switch (portnum) { 77 case 0: 78 case 1: 79 case 2: //channel data 80 if ( (i8253.accessmode[portnum] == 0) || (i8253.accessmode[portnum] == PIT_MODE_LOBYTE) || ( (i8253.accessmode[portnum] == PIT_MODE_TOGGLE) && (i8253.bytetoggle[portnum] == 0) ) ) curbyte = 0; 81 else if ( (i8253.accessmode[portnum] == PIT_MODE_HIBYTE) || ( (i8253.accessmode[portnum] == PIT_MODE_TOGGLE) && (i8253.bytetoggle[portnum] == 1) ) ) curbyte = 1; 82 if ( (i8253.accessmode[portnum] == 0) || (i8253.accessmode[portnum] == PIT_MODE_LOBYTE) || ( (i8253.accessmode[portnum] == PIT_MODE_TOGGLE) && (i8253.bytetoggle[portnum] == 0) ) ) curbyte = 0; 83 else if ( (i8253.accessmode[portnum] == PIT_MODE_HIBYTE) || ( (i8253.accessmode[portnum] == PIT_MODE_TOGGLE) && (i8253.bytetoggle[portnum] == 1) ) ) curbyte = 1; 84 if ( (i8253.accessmode[portnum] == 0) || (i8253.accessmode[portnum] == PIT_MODE_TOGGLE) ) i8253.bytetoggle[portnum] = (~i8253.bytetoggle[portnum]) & 1; 85 if (curbyte == 0) { //low byte 86 if (i8253.counter[portnum] < 10) i8253.counter[portnum] = i8253.chandata[portnum]; 87 i8253.counter[portnum] -= 10; 88 return ( (uint8_t) i8253.counter[portnum]); 89 } 90 else { //high byte 91 return ( (uint8_t) (i8253.counter[portnum] >> 8) ); 92 } 93 break; 94 } 95 return (0); 96 } 97 98 void init8253() { 99 memset (&i8253, 0, sizeof (i8253) ); 100 #ifdef MEGA 101 Timer1.initialize(54925); 102 Timer1.attachInterrupt(timer_isr, 54925); 103 #else 104 myTimer.begin(timer_isr, 54925); 105 #endif 106 //set_port_write_redirector (0x40, 0x43, &out8253); 107 //set_port_read_redirector (0x40, 0x43, &in8253); 108 } 109