-
Notifications
You must be signed in to change notification settings - Fork 42
/
i82c55a.c
152 lines (141 loc) · 3.59 KB
/
i82c55a.c
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
/*
* Minimal emulation of an 82C55A in mode 0 only.
*/
#include <stdio.h>
#include <stdint.h>
#include <stdlib.h>
#include <string.h>
#include "i82c55a.h"
struct i82c55a {
uint8_t out_a;
uint8_t out_b;
uint8_t out_c;
uint8_t ctrl;
#define CTRL_EN 0x80
#define CTRL_MODEA 0x60
#define CTRL_INPUTA 0x10
#define CTRL_INPUTCU 0x08
#define CTRL_MDOEB 0x04
#define CTRL_INPUTB 0x02
#define CTRL_INPUTCL 0x01
int trace;
};
/*
* Read from the PPI.
*/
uint8_t i82c55a_read(struct i82c55a *ppi, uint8_t addr)
{
uint8_t tmp;
switch (addr & 3) {
case 0:
if (ppi->ctrl & CTRL_INPUTA)
return i82c55a_input(ppi, 0);
return ppi->out_a;
case 1:
if (ppi->ctrl & CTRL_INPUTB)
return i82c55a_input(ppi, 1);
return ppi->out_b;
case 2:
tmp = i82c55a_input(ppi, 2);
if (!(ppi->ctrl & CTRL_INPUTCL)) {
tmp &= 0xF0;
tmp |= ppi->out_c & 0x0F;
}
if (!(ppi->ctrl & CTRL_INPUTCU)) {
tmp &= 0x0F;
tmp |= ppi->out_c & 0xF0;
}
return tmp;
case 3:
return ppi->ctrl;
default:
fprintf(stderr, "Unreachable.\n");
exit(1);
}
}
/*
* Write to the PPI
*/
void i82c55a_write(struct i82c55a *ppi, uint8_t addr, uint8_t val)
{
uint8_t tmp;
addr &= 3;
switch(addr) {
case 0:
ppi->out_a = val;
if (!(ppi->ctrl & CTRL_INPUTA))
i82c55a_output(ppi, 0, val);
break;
case 1:
ppi->out_b = val;
if (!(ppi->ctrl & CTRL_INPUTB))
i82c55a_output(ppi, 1, val);
break;
case 2:
/* Port C can be half input half output */
ppi->out_c = val;
tmp = val;
/* All inputs - done */
if ((ppi->ctrl & (CTRL_INPUTCU|CTRL_INPUTCL)) == (CTRL_INPUTCU|CTRL_INPUTCL))
break;
/* If the high bits are input then as outputs they are pulled up */
if (ppi->ctrl & CTRL_INPUTCU)
tmp |= 0xF0;
/* Ditto for the low bits */
if (ppi->ctrl & CTRL_INPUTCL)
tmp |= 0x0F;
/* Report the byte */
i82c55a_output(ppi, 2, tmp);
break;
case 3:
if (val & 0x80) { /* Control write */
ppi->ctrl = val;
/* Clear anything set to output */
if (!(val & CTRL_INPUTCU))
ppi->out_c &= 0xF0;
if (!(val & CTRL_INPUTB))
ppi->out_b = 0;
if (!(val & CTRL_INPUTCL))
ppi->out_c &= 0x0F;
if (!(val & CTRL_INPUTA))
ppi->out_a = 0;
break;
}
/* Bit operation */
tmp = (val >> 1) & 0x07;
ppi->out_c &= ~(1 << tmp);
ppi->out_c |= (val & 1) << tmp;
/* Make it into a port C write and recurse */
i82c55a_write(ppi, 2, ppi->out_c);
break;
}
}
void i82c55a_reset(struct i82c55a *ppi)
{
/* Reset goes entirely input */
ppi->ctrl = 0x9B;
/* This puts everything into input mode with pullups */
i82c55a_output(ppi, 0, 0xFF);
i82c55a_output(ppi, 1, 0xFF);
i82c55a_output(ppi, 2, 0xFF);
/* The output register state does not matter and it will be zeroed
if we switch anything to output anyway */
}
struct i82c55a *i82c55a_create(void)
{
struct i82c55a *ppi = malloc(sizeof(struct i82c55a));
if (ppi == NULL) {
fprintf(stderr, "Out of memory.\n");
exit(1);
}
i82c55a_reset(ppi);
return ppi;
}
void i82c55a_free(struct i82c55a *ppi)
{
free(ppi);
}
void i82c55a_trace(struct i82c55a *ppi, int onoff)
{
ppi->trace = onoff;
}