#include #include #include #include #define BASE_CLK 1193180 #define F 2200 #define DIVISOR (BASE_CLK /F) #define PIT_MCR_CH2 (2UL << 6) #define PIT_MCR_AM_LOHI (3UL << 4) #define PIT_MCR_MODE0 (0UL << 1) #define PIT_MCR_MODE3 (3UL << 1) #define PIT_DATA_CH2 0x42 #define PIT_MCR 0x43 #define PORTB 0x61 #define PORTB_SP_GATE (1 << 1) #define PORTB_SP_CH2_GATE (1 << 0) #define PORTB_SP_STATUS (1 << 5) static inline uint64_t rdtsc (void) { uint32_t low, high; __asm__ __volatile__ ("rdtsc":"=a" (low), "=d" (high)); return ((uint64_t) high << 32) | low; } static inline void sti (void) { __asm__ __volatile__ ("sti"); } static inline void cli (void) { __asm__ __volatile__ ("cli"); } static inline void out_8 (uint16_t port, uint8_t value) { asm volatile ("outb %b0,%w1"::"a" (value), "Nd" (port)); } static inline uint8_t in_8 (uint16_t port) { uint8_t ret; asm volatile ("inb %w1,%b0":"=a" (ret):"Nd" (port)); return ret; } static uint64_t t_f, t_b; static int p = 0, f = 0; static uint64_t tsc_baud, tsc_2f; static unsigned baud; static const char *varicode_fsk[] = { "1010101011", /* 0 - */ "1011011011", /* 1 - */ "1011101101", /* 2 - */ "1101110111", /* 3 - */ "1011101011", /* 4 - */ "1101011111", /* 5 - */ "1011101111", /* 6 - */ "1011111101", /* 7 - */ "1011111111", /* 8 - */ "11101111", /* 9 - */ "11101", /* 10 - */ "1101101111", /* 11 - */ "1011011101", /* 12 - */ "11111", /* 13 - */ "1101110101", /* 14 - */ "1110101011", /* 15 - */ "1011110111", /* 16 - */ "1011110101", /* 17 - */ "1110101101", /* 18 - */ "1110101111", /* 19 - */ "1101011011", /* 20 - */ "1101101011", /* 21 - */ "1101101101", /* 22 - */ "1101010111", /* 23 - */ "1101111011", /* 24 - */ "1101111101", /* 25 - */ "1110110111", /* 26 - */ "1101010101", /* 27 - */ "1101011101", /* 28 - */ "1110111011", /* 29 - */ "1011111011", /* 30 - */ "1101111111", /* 31 - */ "1", /* 32 - */ "111111111", /* 33 - ! */ "101011111", /* 34 - '"' */ "111110101", /* 35 - # */ "111011011", /* 36 - $ */ "1011010101", /* 37 - % */ "1010111011", /* 38 - & */ "101111111", /* 39 - ' */ "11111011", /* 40 - ( */ "11110111", /* 41 - ) */ "101101111", /* 42 - * */ "111011111", /* 43 - + */ "1110101", /* 44 - , */ "110101", /* 45 - - */ "1010111", /* 46 - . */ "110101111", /* 47 - / */ "10110111", /* 48 - 0 */ "10111101", /* 49 - 1 */ "11101101", /* 50 - 2 */ "11111111", /* 51 - 3 */ "101110111", /* 52 - 4 */ "101011011", /* 53 - 5 */ "101101011", /* 54 - 6 */ "110101101", /* 55 - 7 */ "110101011", /* 56 - 8 */ "110110111", /* 57 - 9 */ "11110101", /* 58 - : */ "110111101", /* 59 - ; */ "111101101", /* 60 - < */ "1010101", /* 61 - = */ "111010111", /* 62 - > */ "1010101111", /* 63 - ? */ "1010111101", /* 64 - @ */ "1111101", /* 65 - A */ "11101011", /* 66 - B */ "10101101", /* 67 - C */ "10110101", /* 68 - D */ "1110111", /* 69 - E */ "11011011", /* 70 - F */ "11111101", /* 71 - G */ "101010101", /* 72 - H */ "1111111", /* 73 - I */ "111111101", /* 74 - J */ "101111101", /* 75 - K */ "11010111", /* 76 - L */ "10111011", /* 77 - M */ "11011101", /* 78 - N */ "10101011", /* 79 - O */ "11010101", /* 80 - P */ "111011101", /* 81 - Q */ "10101111", /* 82 - R */ "1101111", /* 83 - S */ "1101101", /* 84 - T */ "101010111", /* 85 - U */ "110110101", /* 86 - V */ "101011101", /* 87 - W */ "101110101", /* 88 - X */ "101111011", /* 89 - Y */ "1010101101", /* 90 - Z */ "111110111", /* 91 - [ */ "111101111", /* 92 - \ */ "111111011", /* 93 - ] */ "1010111111", /* 94 - ^ */ "101101101", /* 95 - _ */ "1011011111", /* 96 - ` */ "1011", /* 97 - a */ "1011111", /* 98 - b */ "101111", /* 99 - c */ "101101", /* 100 - d */ "11", /* 101 - e */ "111101", /* 102 - f */ "1011011", /* 103 - g */ "101011", /* 104 - h */ "1101", /* 105 - i */ "111101011", /* 106 - j */ "10111111", /* 107 - k */ "11011", /* 108 - l */ "111011", /* 109 - m */ "1111", /* 110 - n */ "111", /* 111 - o */ "111111", /* 112 - p */ "110111111", /* 113 - q */ "10101", /* 114 - r */ "10111", /* 115 - s */ "101", /* 116 - t */ "110111", /* 117 - u */ "1111011", /* 118 - v */ "1101011", /* 119 - w */ "11011111", /* 120 - x */ "1011101", /* 121 - y */ "111010101", /* 122 - z */ "1010110111", /* 123 - { */ "110111011", /* 124 - | */ "1010110101", /* 125 - } */ "1011010111", /* 126 - ~ */ "1110110101", /* 127 - */ "1110111101", /* 128 - */ "1110111111", /* 129 - */ "1111010101", /* 130 - */ "1111010111", /* 131 - */ "1111011011", /* 132 - */ "1111011101", /* 133 - */ "1111011111", /* 134 - */ "1111101011", /* 135 - */ "1111101101", /* 136 - */ "1111101111", /* 137 - */ "1111110101", /* 138 - */ "1111110111", /* 139 - */ "1111111011", /* 140 - */ "1111111101", /* 141 - */ "1111111111", /* 142 - */ "10101010101", /* 143 - */ "10101010111", /* 144 - */ "10101011011", /* 145 - */ "10101011101", /* 146 - */ "10101011111", /* 147 - */ "10101101011", /* 148 - */ "10101101101", /* 149 - */ "10101101111", /* 150 - */ "10101110101", /* 151 - */ "10101110111", /* 152 - */ "10101111011", /* 153 - */ "10101111101", /* 154 - */ "10101111111", /* 155 - */ "10110101011", /* 156 - */ "10110101101", /* 157 - */ "10110101111", /* 158 - */ "10110110101", /* 159 - */ "10110110111", /* 160 - */ "10110111011", /* 161 - */ "10110111101", /* 162 - */ "10110111111", /* 163 - */ "10111010101", /* 164 - */ "10111010111", /* 165 - */ "10111011011", /* 166 - */ "10111011101", /* 167 - */ "10111011111", /* 168 - */ "10111101011", /* 169 - */ "10111101101", /* 170 - */ "10111101111", /* 171 - */ "10111110101", /* 172 - */ "10111110111", /* 173 - */ "10111111011", /* 174 - */ "10111111101", /* 175 - */ "10111111111", /* 176 - */ "11010101011", /* 177 - */ "11010101101", /* 178 - */ "11010101111", /* 179 - */ "11010110101", /* 180 - */ "11010110111", /* 181 - */ "11010111011", /* 182 - */ "11010111101", /* 183 - */ "11010111111", /* 184 - */ "11011010101", /* 185 - */ "11011010111", /* 186 - */ "11011011011", /* 187 - */ "11011011101", /* 188 - */ "11011011111", /* 189 - */ "11011101011", /* 190 - */ "11011101101", /* 191 - */ "11011101111", /* 192 - */ "11011110101", /* 193 - */ "11011110111", /* 194 - */ "11011111011", /* 195 - */ "11011111101", /* 196 - */ "11011111111", /* 197 - */ "11101010101", /* 198 - */ "11101010111", /* 199 - */ "11101011011", /* 200 - */ "11101011101", /* 201 - */ "11101011111", /* 202 - */ "11101101011", /* 203 - */ "11101101101", /* 204 - */ "11101101111", /* 205 - */ "11101110101", /* 206 - */ "11101110111", /* 207 - */ "11101111011", /* 208 - */ "11101111101", /* 209 - */ "11101111111", /* 210 - */ "11110101011", /* 211 - */ "11110101101", /* 212 - */ "11110101111", /* 213 - */ "11110110101", /* 214 - */ "11110110111", /* 215 - */ "11110111011", /* 216 - */ "11110111101", /* 217 - */ "11110111111", /* 218 - */ "11111010101", /* 219 - */ "11111010111", /* 220 - */ "11111011011", /* 221 - */ "11111011101", /* 222 - */ "11111011111", /* 223 - */ "11111101011", /* 224 - */ "11111101101", /* 225 - */ "11111101111", /* 226 - */ "11111110101", /* 227 - */ "11111110111", /* 228 - */ "11111111011", /* 229 - */ "11111111101", /* 230 - */ "11111111111", /* 231 - */ "101010101011", /* 232 - */ "101010101101", /* 233 - */ "101010101111", /* 234 - */ "101010110101", /* 235 - */ "101010110111", /* 236 - */ "101010111011", /* 237 - */ "101010111101", /* 238 - */ "101010111111", /* 239 - */ "101011010101", /* 240 - */ "101011010111", /* 241 - */ "101011011011", /* 242 - */ "101011011101", /* 243 - */ "101011011111", /* 244 - */ "101011101011", /* 245 - */ "101011101101", /* 246 - */ "101011101111", /* 247 - */ "101011110101", /* 248 - */ "101011110111", /* 249 - */ "101011111011", /* 250 - */ "101011111101", /* 251 - */ "101011111111", /* 252 - */ "101101010101", /* 253 - */ "101101010111", /* 254 - */ "101101011011" /* 255 - */ }; static void program_pit_ch2 (uint32_t divisor, uint8_t mode) { out_8 (PIT_MCR, PIT_MCR_CH2 | PIT_MCR_AM_LOHI | mode); out_8 (PIT_DATA_CH2, divisor & 0xff); out_8 (PIT_DATA_CH2, divisor >> 8); } static void set_mask (int mask, uint8_t what) { uint8_t v; v = in_8 (PORTB); if (mask) v &= ~(what); else v |= what; out_8 (PORTB, v); } static void count_cycles (unsigned num) { while (num--) { while (!(in_8 (PORTB) & PORTB_SP_STATUS)); while (in_8 (PORTB) & PORTB_SP_STATUS); } } static void give_to_pit (void) { uint64_t t; set_mask (1, PORTB_SP_CH2_GATE); program_pit_ch2 (DIVISOR, PIT_MCR_MODE3); for (;;) { t = rdtsc (); if (t > t_f) { t_f += tsc_2f; f = !f; if (f ^ p) { set_mask (1, PORTB_SP_GATE); set_mask (0, PORTB_SP_CH2_GATE); set_mask (0, PORTB_SP_GATE); return; } else { set_mask (0, PORTB_SP_GATE); } } } } void take_from_pit (void) { uint64_t t; t = rdtsc (); while (t_b < t) t_b += tsc_baud; while (!(in_8 (PORTB) & PORTB_SP_STATUS)); /*Low */ while ((in_8 (PORTB) & PORTB_SP_STATUS)); /*High */ t_f = rdtsc (); set_mask (1, PORTB_SP_GATE | PORTB_SP_CH2_GATE); t_f += tsc_2f; while (t_b < t_f) t_b += tsc_baud; } static void start (void) { /* This gets OUT2 from the 8254 high*/ set_mask (1, PORTB_SP_CH2_GATE); t_b = t_f = rdtsc (); } static void send_bit_raw (int b) { uint64_t t; if (!b) p = !p; t_b += tsc_baud; while ((t = rdtsc ()) < t_b) { if (t > t_f) { t_f += tsc_2f; f = !f; set_mask (f ^ p, PORTB_SP_GATE); } } } static void send_bit (int b) { send_bit_raw (b); } static void stop (void) { set_mask (1, PORTB_SP_GATE | PORTB_SP_CH2_GATE); } static void send_sync (int bits, int bit) { if (bit == 2) { bits >>= 1; while (bits--) { send_bit (1); send_bit (0); } } else { while (bits--) send_bit (bit); } } static void send_char (uint8_t c) { const char *v = varicode_fsk[c]; if (!v) return; while (*v) send_bit (*(v++) - '0'); send_bit (0); send_bit (0); } void modem_xmit (void *_buf, size_t buf_len) { uint8_t *buf = _buf; take_from_pit (); send_sync (baud / 5, 0); send_char (0); while (buf_len--) send_char (*(buf++)); give_to_pit (); } void modem_on (unsigned _baud) { uint64_t tsc, tsc_start; baud = _baud; program_pit_ch2 (DIVISOR, PIT_MCR_MODE3); set_mask (0, PORTB_SP_GATE | PORTB_SP_CH2_GATE); count_cycles (10); tsc_start = rdtsc (); count_cycles (F); tsc = rdtsc (); tsc -= tsc_start; tsc_baud = (tsc / baud); tsc_2f = (tsc / (2 * F)); start (); send_sync (baud * 2, 0); give_to_pit (); } void modem_off (void) { take_from_pit (); send_sync (150, 1); stop (); } int main (int argc, char *argv[]) { char msg[] = "The quick brown fox jumps over the lazy dog\r\n"; iopl (3); cli (); modem_on (125); modem_xmit (msg, sizeof (msg)); modem_off (); sti (); return 0; }