-
Notifications
You must be signed in to change notification settings - Fork 0
/
decserial.cc
282 lines (255 loc) · 7.77 KB
/
decserial.cc
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
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
/* DZ11-based DEC 5000/200 Serial chip emulation.
Copyright 2003 Brian R. Gaeke.
This file is part of VMIPS.
VMIPS is free software; you can redistribute it and/or modify it
under the terms of the GNU General Public License as published by the
Free Software Foundation; either version 2 of the License, or (at your
option) any later version.
VMIPS 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 General Public License
for more details.
You should have received a copy of the GNU General Public License along
with VMIPS; if not, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */
/* DZ11 serial device as implemented the DECstation 5000/200
*
* This version has not been tested with multiple serial lines. An actual DZ11
* supports 4 lines (on the 5000/200, these are the keyboard, mouse, modem,
* and printer ports.)
*
* This version does not support the CSR<MAINT> (loopback enable) bit.
*
* If you are having trouble, try recompiling with the SERIAL_DEBUG macro
* defined; see below. This will spew a large amount of stuff, some of which
* may be helpful.
*/
/* #define SERIAL_DEBUG 1 */
#include "cpu.h"
#include "deccsr.h"
#include "decserial.h"
#include "deviceexc.h"
#include "mapper.h"
#include "vmips.h"
#include <cassert>
DECSerialDevice::DECSerialDevice (Clock *clock, uint8 deccsr_irq_)
: TerminalController (clock, KEYBOARD_POLL_NS, KEYBOARD_REPOLL_NS,
DISPLAY_READY_DELAY_NS),
deccsr_irq (deccsr_irq_)
{
extent = 0x80000;
master_clear ();
}
void
DECSerialDevice::master_clear ()
{
#if defined(SERIAL_DEBUG)
fprintf (stderr, "DZ11 Master clear!\n");
#endif
csr = 0;
rbuf &= ~DZ_RBUF_DVAL;
lpr = 0;
tcr = 0;
}
static bool
TCR_LINE_ENABLED (uint32 Tcr, unsigned int Line)
{
return ((bool) ((Tcr) & (DZ_TCR_LNENB0 << (Line))));
}
static unsigned int
CSR_TLINE (unsigned int Line)
{
return (((Line) & 0x03) << 8);
}
#if defined(SERIAL_DEBUG)
static unsigned int
GET_CURRENT_CSR_TLINE (uint32 Csr)
{
return (((Csr) >> 8) & 0x03);
}
#endif
static unsigned int
RBUF_RLINE (unsigned int Line)
{
return (((Line) & 0x03) << 8);
}
bool DECSerialDevice::receiver_done (const int line) const {
return (line_connected (line) && (lines[line].keyboard_state == READY));
}
bool DECSerialDevice::transmitter_ready (const int line) const {
return (line_connected (line) && (lines[line].display_state == READY)
&& TCR_LINE_ENABLED(tcr, line));
}
uint32
DECSerialDevice::fetch_word (uint32 offset, int mode, DeviceExc *client)
{
uint32 rv = 0;
switch (offset & 0x18) {
case DZ_CSR:
csr &= ~(DZ_CSR_RDONE | DZ_CSR_TRDY | DZ_CSR_TLINE);
if (csr & DZ_CSR_MSE)
// Scan for lines with data.
for (int line = 0; line < 4; ++line) {
if (receiver_done (line))
csr |= DZ_CSR_RDONE;
if (transmitter_ready (line)) {
csr |= DZ_CSR_TRDY;
csr |= CSR_TLINE(line);
}
}
rv = csr;
break;
case DZ_RBUF:
rbuf &= ~DZ_RBUF_DVAL;
for (int line = 0; line < 4; ++line) {
if (receiver_done (line)) {
unready_keyboard (line);
rbuf = lines[line].keyboard_char | DZ_RBUF_DVAL;
rbuf |= RBUF_RLINE(line);
break;
}
}
rv = rbuf;
break;
case DZ_TCR:
rv = tcr;
#if defined(SERIAL_DEBUG)
fprintf (stderr, "PC=0x%x DZ11 TCR read as 0x%x\n",
machine->cpu->debug_get_pc(), rv);
#endif
break;
case DZ_MSR:
rv = msr;
#if defined(SERIAL_DEBUG)
fprintf (stderr, "DZ11 MSR read as 0x%x\n", rv);
#endif
break;
}
if (machine->cpu->is_bigendian()) {
rv <<= 16;
}
return machine->physmem->mips_to_host_word(rv);
}
bool
DECSerialDevice::keyboardInterruptReadyForLine (const int line) const {
return keyboard_interrupt_enable && receiver_done (line);
}
bool
DECSerialDevice::displayInterruptReadyForLine (const int line) const {
return display_interrupt_enable && transmitter_ready (line);
}
void
DECSerialDevice::store_word (uint32 offset, uint32 odata, DeviceExc *client)
{
uint32 data = machine->physmem->host_to_mips_word(odata);
#if defined(SERIAL_DEBUG)
fprintf(stderr,"DZ11 Store(0x%08x) got 0x%08x, storing 0x%08x\n", offset,odata,data);
#endif
// For testing purposes, we would like this device to work even when the
// machine is in big-endian mode. The issues are: (0) This is not
// necessarily true of the actual hardware. (1) The hardware contains
// 16-bit registers, but we only implement store_word. (2) The DeviceMap
// class only implements store_byte and store_halfword in terms of
// store_word; it makes no provision for implementing store_byte and
// store_word in terms of store_halfword. So, hack it to work by peeking at
// the CPU endianness, and shifting the data if we are in big-endian mode.
if (machine->cpu->is_bigendian()) {
data >>= 16;
}
uint16 data16 = data & 0x0ffff;
switch (offset & 0x18) {
case DZ_CSR:
#if defined(SERIAL_DEBUG)
fprintf (stderr, "DZ11 write CSR as %x\n", data16);
#endif
csr = data16;
if (csr & DZ_CSR_CLR)
master_clear ();
display_interrupt_enable = (csr & DZ_CSR_TIE);
keyboard_interrupt_enable = (csr & DZ_CSR_RIE);
#if defined(SERIAL_DEBUG)
fprintf (stderr, "DZ11 Keyboard IE is now %s, Display IE now %s, "
"selected tx line now %d\n",
keyboard_interrupt_enable ? "on" : "off",
display_interrupt_enable ? "on" : "off",
GET_CURRENT_CSR_TLINE (csr));
#endif
break;
case DZ_LPR:
#if defined(SERIAL_DEBUG)
fprintf (stderr, "DZ11 write LPR as %x\n", data16);
#endif
lpr = data16;
break;
case DZ_TCR:
#if defined(SERIAL_DEBUG)
fprintf (stderr, "PC=0x%x DZ11 write TCR as %x (%x)\n",
machine->cpu->debug_get_pc(), data16, odata);
#endif
tcr = data16;
break;
case DZ_TDR: {
int line = 3; // FIXME: should be GET_CURRENT_CSR_TLINE (csr);
if (line_connected (line))
unready_display (line, data16 & 0xff);
break;
}
}
// Check whether we have to assert or deassert the CSR IRQ now because
// flags changed.
bool any_enable_is_on = (csr & (DZ_CSR_RIE | DZ_CSR_TIE));
if (!any_enable_is_on) {
// They turned all the interrupt enable bits off. So cancel any pending
// interrupt and just return.
deassertCSRInt();
return;
}
// There is an interrupt enable on. Check for a source which has been
// ready to trigger an interrupt.
bool any_source_is_ready = false;
for (int line = 0; line < 4; ++line)
any_source_is_ready = any_source_is_ready
|| (displayInterruptReadyForLine (line)
|| keyboardInterruptReadyForLine (line));
if (any_source_is_ready) {
assertCSRInt();
} else {
deassertCSRInt();
}
}
void
DECSerialDevice::assertCSRInt () {
assert (machine->deccsr_device && "DECCSR device required for DECSerial");
machine->deccsr_device->assertInt (deccsr_irq);
}
void
DECSerialDevice::deassertCSRInt () {
assert (machine->deccsr_device && "DECCSR device required for DECSerial");
machine->deccsr_device->deassertInt (deccsr_irq);
}
void
DECSerialDevice::unready_display (int line, char data)
{
TerminalController::unready_display (line, data);
deassertCSRInt();
}
void
DECSerialDevice::ready_display (int line)
{
TerminalController::ready_display (line);
if (displayInterruptReadyForLine(line))
assertCSRInt();
}
void
DECSerialDevice::unready_keyboard (int line)
{
TerminalController::unready_keyboard (line);
deassertCSRInt();
}
void
DECSerialDevice::ready_keyboard (int line)
{
TerminalController::ready_keyboard (line);
if (keyboardInterruptReadyForLine(line))
assertCSRInt();
}