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