1 /* DZ11-based DEC 5000/200 Serial chip emulation.
2 Copyright 2003 Brian R. Gaeke.
3
4 This file is part of VMIPS.
5
6 VMIPS is free software; you can redistribute it and/or modify it
7 under the terms of the GNU General Public License as published by the
8 Free Software Foundation; either version 2 of the License, or (at your
9 option) any later version.
10
11 VMIPS is distributed in the hope that it will be useful, but
12 WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
13 or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
14 for more details.
15
16 You should have received a copy of the GNU General Public License along
17 with VMIPS; if not, write to the Free Software Foundation, Inc.,
18 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */
19
20 /* DZ11 serial device as implemented the DECstation 5000/200
21 *
22 * This version has not been tested with multiple serial lines. An actual DZ11
23 * supports 4 lines (on the 5000/200, these are the keyboard, mouse, modem,
24 * and printer ports.)
25 *
26 * This version does not support the CSR<MAINT> (loopback enable) bit.
27 *
28 * If you are having trouble, try recompiling with the SERIAL_DEBUG macro
29 * defined; see below. This will spew a large amount of stuff, some of which
30 * may be helpful.
31 */
32
33 /* #define SERIAL_DEBUG 1 */
34
35 #include "cpu.h"
36 #include "deccsr.h"
37 #include "decserial.h"
38 #include "deviceexc.h"
39 #include "mapper.h"
40 #include "vmips.h"
41 #include <cassert>
42
DECSerialDevice(Clock * clock,uint8 deccsr_irq_)43 DECSerialDevice::DECSerialDevice (Clock *clock, uint8 deccsr_irq_)
44 : TerminalController (clock, KEYBOARD_POLL_NS, KEYBOARD_REPOLL_NS,
45 DISPLAY_READY_DELAY_NS),
46 deccsr_irq (deccsr_irq_)
47 {
48 extent = 0x80000;
49 master_clear ();
50 }
51
52 void
master_clear()53 DECSerialDevice::master_clear ()
54 {
55 #if defined(SERIAL_DEBUG)
56 fprintf (stderr, "DZ11 Master clear!\n");
57 #endif
58 csr = 0;
59 rbuf &= ~DZ_RBUF_DVAL;
60 lpr = 0;
61 tcr = 0;
62 }
63
64 static bool
TCR_LINE_ENABLED(uint32 Tcr,unsigned int Line)65 TCR_LINE_ENABLED (uint32 Tcr, unsigned int Line)
66 {
67 return ((bool) ((Tcr) & (DZ_TCR_LNENB0 << (Line))));
68 }
69
70 static unsigned int
CSR_TLINE(unsigned int Line)71 CSR_TLINE (unsigned int Line)
72 {
73 return (((Line) & 0x03) << 8);
74 }
75
76 #if defined(SERIAL_DEBUG)
77 static unsigned int
GET_CURRENT_CSR_TLINE(uint32 Csr)78 GET_CURRENT_CSR_TLINE (uint32 Csr)
79 {
80 return (((Csr) >> 8) & 0x03);
81 }
82 #endif
83
84 static unsigned int
RBUF_RLINE(unsigned int Line)85 RBUF_RLINE (unsigned int Line)
86 {
87 return (((Line) & 0x03) << 8);
88 }
89
receiver_done(const int line) const90 bool DECSerialDevice::receiver_done (const int line) const {
91 return (line_connected (line) && (lines[line].keyboard_state == READY));
92 }
93
transmitter_ready(const int line) const94 bool DECSerialDevice::transmitter_ready (const int line) const {
95 return (line_connected (line) && (lines[line].display_state == READY)
96 && TCR_LINE_ENABLED(tcr, line));
97 }
98
99 uint32
fetch_word(uint32 offset,int mode,DeviceExc * client)100 DECSerialDevice::fetch_word (uint32 offset, int mode, DeviceExc *client)
101 {
102 uint32 rv = 0;
103 switch (offset & 0x18) {
104 case DZ_CSR:
105 csr &= ~(DZ_CSR_RDONE | DZ_CSR_TRDY | DZ_CSR_TLINE);
106 if (csr & DZ_CSR_MSE)
107 // Scan for lines with data.
108 for (int line = 0; line < 4; ++line) {
109 if (receiver_done (line))
110 csr |= DZ_CSR_RDONE;
111 if (transmitter_ready (line)) {
112 csr |= DZ_CSR_TRDY;
113 csr |= CSR_TLINE(line);
114 }
115 }
116 rv = csr;
117 break;
118 case DZ_RBUF:
119 rbuf &= ~DZ_RBUF_DVAL;
120 for (int line = 0; line < 4; ++line) {
121 if (receiver_done (line)) {
122 unready_keyboard (line);
123 rbuf = lines[line].keyboard_char | DZ_RBUF_DVAL;
124 rbuf |= RBUF_RLINE(line);
125 break;
126 }
127 }
128 rv = rbuf;
129 break;
130 case DZ_TCR:
131 rv = tcr;
132 #if defined(SERIAL_DEBUG)
133 fprintf (stderr, "PC=0x%x DZ11 TCR read as 0x%x\n",
134 machine->cpu->debug_get_pc(), rv);
135 #endif
136 break;
137 case DZ_MSR:
138 rv = msr;
139 #if defined(SERIAL_DEBUG)
140 fprintf (stderr, "DZ11 MSR read as 0x%x\n", rv);
141 #endif
142 break;
143 }
144 if (machine->cpu->is_bigendian()) {
145 rv <<= 16;
146 }
147 return machine->physmem->mips_to_host_word(rv);
148 }
149
150 bool
keyboardInterruptReadyForLine(const int line) const151 DECSerialDevice::keyboardInterruptReadyForLine (const int line) const {
152 return keyboard_interrupt_enable && receiver_done (line);
153 }
154
155 bool
displayInterruptReadyForLine(const int line) const156 DECSerialDevice::displayInterruptReadyForLine (const int line) const {
157 return display_interrupt_enable && transmitter_ready (line);
158 }
159
160 void
store_word(uint32 offset,uint32 odata,DeviceExc * client)161 DECSerialDevice::store_word (uint32 offset, uint32 odata, DeviceExc *client)
162 {
163 uint32 data = machine->physmem->host_to_mips_word(odata);
164 #if defined(SERIAL_DEBUG)
165 fprintf(stderr,"DZ11 Store(0x%08x) got 0x%08x, storing 0x%08x\n", offset,odata,data);
166 #endif
167 // For testing purposes, we would like this device to work even when the
168 // machine is in big-endian mode. The issues are: (0) This is not
169 // necessarily true of the actual hardware. (1) The hardware contains
170 // 16-bit registers, but we only implement store_word. (2) The DeviceMap
171 // class only implements store_byte and store_halfword in terms of
172 // store_word; it makes no provision for implementing store_byte and
173 // store_word in terms of store_halfword. So, hack it to work by peeking at
174 // the CPU endianness, and shifting the data if we are in big-endian mode.
175 if (machine->cpu->is_bigendian()) {
176 data >>= 16;
177 }
178 uint16 data16 = data & 0x0ffff;
179 switch (offset & 0x18) {
180 case DZ_CSR:
181 #if defined(SERIAL_DEBUG)
182 fprintf (stderr, "DZ11 write CSR as %x\n", data16);
183 #endif
184 csr = data16;
185 if (csr & DZ_CSR_CLR)
186 master_clear ();
187 display_interrupt_enable = (csr & DZ_CSR_TIE);
188 keyboard_interrupt_enable = (csr & DZ_CSR_RIE);
189 #if defined(SERIAL_DEBUG)
190 fprintf (stderr, "DZ11 Keyboard IE is now %s, Display IE now %s, "
191 "selected tx line now %d\n",
192 keyboard_interrupt_enable ? "on" : "off",
193 display_interrupt_enable ? "on" : "off",
194 GET_CURRENT_CSR_TLINE (csr));
195 #endif
196 break;
197 case DZ_LPR:
198 #if defined(SERIAL_DEBUG)
199 fprintf (stderr, "DZ11 write LPR as %x\n", data16);
200 #endif
201 lpr = data16;
202 break;
203 case DZ_TCR:
204 #if defined(SERIAL_DEBUG)
205 fprintf (stderr, "PC=0x%x DZ11 write TCR as %x (%x)\n",
206 machine->cpu->debug_get_pc(), data16, odata);
207 #endif
208 tcr = data16;
209 break;
210 case DZ_TDR: {
211 int line = 3; // FIXME: should be GET_CURRENT_CSR_TLINE (csr);
212 if (line_connected (line))
213 unready_display (line, data16 & 0xff);
214 break;
215 }
216 }
217
218 // Check whether we have to assert or deassert the CSR IRQ now because
219 // flags changed.
220 bool any_enable_is_on = (csr & (DZ_CSR_RIE | DZ_CSR_TIE));
221 if (!any_enable_is_on) {
222 // They turned all the interrupt enable bits off. So cancel any pending
223 // interrupt and just return.
224 deassertCSRInt();
225 return;
226 }
227 // There is an interrupt enable on. Check for a source which has been
228 // ready to trigger an interrupt.
229 bool any_source_is_ready = false;
230 for (int line = 0; line < 4; ++line)
231 any_source_is_ready = any_source_is_ready
232 || (displayInterruptReadyForLine (line)
233 || keyboardInterruptReadyForLine (line));
234 if (any_source_is_ready) {
235 assertCSRInt();
236 } else {
237 deassertCSRInt();
238 }
239 }
240
241 void
assertCSRInt()242 DECSerialDevice::assertCSRInt () {
243 assert (machine->deccsr_device && "DECCSR device required for DECSerial");
244 machine->deccsr_device->assertInt (deccsr_irq);
245 }
246
247 void
deassertCSRInt()248 DECSerialDevice::deassertCSRInt () {
249 assert (machine->deccsr_device && "DECCSR device required for DECSerial");
250 machine->deccsr_device->deassertInt (deccsr_irq);
251 }
252
253 void
unready_display(int line,char data)254 DECSerialDevice::unready_display (int line, char data)
255 {
256 TerminalController::unready_display (line, data);
257 deassertCSRInt();
258 }
259
260 void
ready_display(int line)261 DECSerialDevice::ready_display (int line)
262 {
263 TerminalController::ready_display (line);
264 if (displayInterruptReadyForLine(line))
265 assertCSRInt();
266 }
267
268 void
unready_keyboard(int line)269 DECSerialDevice::unready_keyboard (int line)
270 {
271 TerminalController::unready_keyboard (line);
272 deassertCSRInt();
273 }
274
275 void
ready_keyboard(int line)276 DECSerialDevice::ready_keyboard (int line)
277 {
278 TerminalController::ready_keyboard (line);
279 if (keyboardInterruptReadyForLine(line))
280 assertCSRInt();
281 }
282
283