First Commit of my working state
[simh.git] / PDP11 / pdp11_pclk.c
1 /* pdp11_pclk.c: KW11P programmable clock simulator
2
3 Copyright (c) 1993-2008, Robert M Supnik
4 Written by John Dundas, used with his gracious permission
5
6 Permission is hereby granted, free of charge, to any person obtaining a
7 copy of this software and associated documentation files (the "Software"),
8 to deal in the Software without restriction, including without limitation
9 the rights to use, copy, modify, merge, publish, distribute, sublicense,
10 and/or sell copies of the Software, and to permit persons to whom the
11 Software is furnished to do so, subject to the following conditions:
12
13 The above copyright notice and this permission notice shall be included in
14 all copies or substantial portions of the Software.
15
16 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17 IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18 FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
19 ROBERT M SUPNIK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
20 IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
21 CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
22
23 Except as contained in this notice, the name of Robert M Supnik shall not be
24 used in advertising or otherwise to promote the sale, use or other dealings
25 in this Software without prior written authorization from Robert M Supnik.
26
27 pclk KW11P line frequency clock
28
29 20-May-08 RMS Standardized clock delay at 1mips
30 18-Jun-07 RMS Added UNIT_IDLE flag
31 07-Jul-05 RMS Removed extraneous externs
32
33 KW11-P Programmable Clock
34
35 I/O Page Registers:
36
37 CSR 17 772 540
38 CSB 17 772 542
39 CNT 17 772 544
40
41 Vector: 0104
42
43 Priority: BR6
44
45 ** Theory of Operation **
46
47 A real KW11-P is built around the following major components:
48 - 16-bit up/down counter
49 - 16-bit count set buffer
50 - 9-bit control and status register
51 - clocks: crystal controlled (1) 100 kHz and (2) 10 kHz clocks,
52 (3) a 50/60 Hz line frequency clock, and (4) an analog signal
53 input trigger
54 This software emulator for SIMH implements all of the above with
55 the exception of the external input trigger, which is arbitrarily
56 wired to 10Hz.
57
58 Operation of this emulator is rather simplistic as compared to the
59 actual device. The register read and write routines are responsible
60 for copying internal state from the simulated device to the operating
61 program. Clock state variables are altered in the write routine
62 as well as the desired clock ticking rate. Possible rates are
63 given in the table below.
64
65 Rate Bit 2 Bit 1
66 100 kHz 0 0
67 10 kHz 0 1
68 Line frequency 1 0
69 External 1 1
70
71 I think SIMH would have a hard time actually keeping up with a 100
72 kHz ticking rate. I haven't tried this to verify, though.
73
74 The clock service routine (pclk_svc) is responsible for ticking
75 the clock. The routine does implement up/down, repeat vs.
76 single-interrupt, and single clocking (maintenance). The routine
77 updates the internal state according to the options selected and
78 signals interrupts when appropriate.
79
80 For a complete description of the device, please see DEC-11-HPWB-D
81 KW11-P Programmable Real-Time Clock Manual.
82
83 ** Notes **
84
85 1. The device is disabled by default.
86
87 2. Use XXDP V2.5 test program ZKWBJ1.BIC; loads at 1000, starts at
88 1100? Seems to execute the first few tests correctly then waits
89 for input from the console. I don't have a description of how this
90 diagnostic works and thus don't know how to proceed from that point.
91
92 3. The read and write routines don't do anything with odd address
93 accesses. The manual says that byte writes don't work.
94
95 4. RSTS can use this clock in place of the standard KW11-L line
96 frequency clock. In order to do this, use the DEFAULT response in
97 the OPTION: dialog. To the Preferred clock prompt answer "P".
98 Then you have the option of line frequency "L" or some multiple of
99 50 between 50 and 1000 to use the programmable portion of the clock.
100
101 5. This is really a Unibus peripheral and thus doesn't actually make
102 sense within a J-11 system as there never was a Qbus version of
103 this to the best of my knowledge. However the OSs I have tried
104 don't appear to exhibit any dissonance between this option and the
105 processor/bus emulation. I think the options that would make
106 somewhat more sense in a Qbus environment the KWV11-C and/or KWV11-S.
107 I don't know if any of the -11 OSs contained support for using
108 these as the system clock, though.
109 */
110
111 #include "pdp11_defs.h"
112
113 #define PCLKCSR_RDMASK 0100377 /* readable */
114 #define PCLKCSR_WRMASK 0000137 /* writeable */
115
116 #define UNIT_V_LINE50HZ (UNIT_V_UF + 0)
117 #define UNIT_LINE50HZ (1 << UNIT_V_LINE50HZ)
118
119 /* CSR - 17772540 */
120
121 #define CSR_V_FIX 5 /* single tick */
122 #define CSR_V_UPDN 4 /* down/up */
123 #define CSR_V_MODE 3 /* single/repeat */
124 #define CSR_FIX (1u << CSR_V_FIX)
125 #define CSR_UPDN (1u << CSR_V_UPDN)
126 #define CSR_MODE (1u << CSR_V_MODE)
127 #define CSR_V_RATE 1 /* rate */
128 #define CSR_M_RATE 03
129 #define CSR_GETRATE(x) (((x) >> CSR_V_RATE) & CSR_M_RATE)
130
131 extern int32 int_req[IPL_HLVL];
132
133 uint32 pclk_csr = 0; /* control/status */
134 uint32 pclk_csb = 0; /* count set buffer */
135 uint32 pclk_ctr = 0; /* counter */
136 static uint32 rate[4] = { 100000, 10000, 60, 10 }; /* ticks per second */
137 static uint32 xtim[4] = { 10, 100, 16667, 100000 }; /* nominal time delay */
138
139 DEVICE pclk_dev;
140 t_stat pclk_rd (int32 *data, int32 PA, int32 access);
141 t_stat pclk_wr (int32 data, int32 PA, int32 access);
142 t_stat pclk_svc (UNIT *uptr);
143 t_stat pclk_reset (DEVICE *dptr);
144 t_stat pclk_set_line (UNIT *uptr, int32 val, char *cptr, void *desc);
145 void pclk_tick (void);
146
147 /* PCLK data structures
148
149 pclk_dev PCLK device descriptor
150 pclk_unit PCLK unit descriptor
151 pclk_reg PCLK register list
152 */
153
154 DIB pclk_dib = {
155 IOBA_PCLK, IOLN_PCLK, &pclk_rd, &pclk_wr,
156 1, IVCL (PCLK), VEC_PCLK, { NULL }
157 };
158
159 UNIT pclk_unit = { UDATA (&pclk_svc, UNIT_IDLE, 0) };
160
161 REG pclk_reg[] = {
162 { ORDATA (CSR, pclk_csr, 16) },
163 { ORDATA (CSB, pclk_csb, 16) },
164 { ORDATA (CNT, pclk_ctr, 16) },
165 { FLDATA (INT, IREQ (PCLK), INT_V_PCLK) },
166 { FLDATA (OVFL, pclk_csr, CSR_V_ERR) },
167 { FLDATA (DONE, pclk_csr, CSR_V_DONE) },
168 { FLDATA (IE, pclk_csr, CSR_V_IE) },
169 { FLDATA (UPDN, pclk_csr, CSR_V_UPDN) },
170 { FLDATA (MODE, pclk_csr, CSR_V_MODE) },
171 { FLDATA (RUN, pclk_csr, CSR_V_GO) },
172 { BRDATA (TIME, xtim, 10, 32, 4), REG_NZ + PV_LEFT },
173 { BRDATA (TPS, rate, 10, 32, 4), REG_NZ + PV_LEFT },
174 { DRDATA (CURTIM, pclk_unit.wait, 32), REG_HRO },
175 { ORDATA (DEVADDR, pclk_dib.ba, 32), REG_HRO },
176 { ORDATA (DEVVEC, pclk_dib.vec, 16), REG_HRO },
177 { NULL }
178 };
179
180 MTAB pclk_mod[] = {
181 { UNIT_LINE50HZ, UNIT_LINE50HZ, "50 Hz", "50HZ", &pclk_set_line },
182 { UNIT_LINE50HZ, 0, "60 Hz", "60HZ", &pclk_set_line },
183 { MTAB_XTD|MTAB_VDV, 0, "ADDRESS", NULL,
184 NULL, &show_addr, NULL },
185 { MTAB_XTD|MTAB_VDV, 0, "VECTOR", "VECTOR",
186 &set_vec, &show_vec, NULL },
187 { 0 }
188 };
189
190 DEVICE pclk_dev = {
191 "PCLK", &pclk_unit, pclk_reg, pclk_mod,
192 1, 0, 0, 0, 0, 0,
193 NULL, NULL, &pclk_reset,
194 NULL, NULL, NULL,
195 &pclk_dib, DEV_DISABLE | DEV_DIS | DEV_UBUS | DEV_QBUS
196 };
197
198 /* Clock I/O address routines */
199
200 t_stat pclk_rd (int32 *data, int32 PA, int32 access)
201 {
202 switch ((PA >> 1) & 03) {
203
204 case 00: /* CSR */
205 *data = pclk_csr & PCLKCSR_RDMASK; /* return CSR */
206 pclk_csr = pclk_csr & ~(CSR_ERR | CSR_DONE); /* clr err, done */
207 CLR_INT (PCLK); /* clr intr */
208 break;
209
210 case 01: /* buffer */
211 *data = 0; /* read only */
212 break;
213
214 case 02: /* counter */
215 *data = pclk_ctr & DMASK; /* return counter */
216 break;
217 }
218
219 return SCPE_OK;
220 }
221
222 t_stat pclk_wr (int32 data, int32 PA, int32 access)
223 {
224 int32 old_csr = pclk_csr;
225 int32 rv;
226
227 switch ((PA >> 1) & 03) {
228
229 case 00: /* CSR */
230 pclk_csr = data & PCLKCSR_WRMASK; /* clear and write */
231 CLR_INT (PCLK); /* clr intr */
232 rv = CSR_GETRATE (pclk_csr); /* new rate */
233 pclk_unit.wait = xtim[rv]; /* new delay */
234 if ((pclk_csr & CSR_GO) == 0) { /* stopped? */
235 sim_cancel (&pclk_unit); /* cancel */
236 if (data & CSR_FIX) pclk_tick (); /* fix? tick */
237 }
238 else if (((old_csr & CSR_GO) == 0) || /* run 0 -> 1? */
239 (rv != CSR_GETRATE (old_csr))) { /* rate change? */
240 sim_cancel (&pclk_unit); /* cancel */
241 sim_activate (&pclk_unit, /* start clock */
242 sim_rtcn_init (pclk_unit.wait, TMR_PCLK));
243 }
244 break;
245
246 case 01: /* buffer */
247 pclk_csb = pclk_ctr = data; /* store ctr */
248 pclk_csr = pclk_csr & ~(CSR_ERR | CSR_DONE); /* clr err, done */
249 CLR_INT (PCLK); /* clr intr */
250 break;
251
252 case 02: /* counter */
253 break; /* read only */
254 }
255
256 return SCPE_OK;
257 }
258
259 /* Clock tick (automatic or manual) */
260
261 void pclk_tick (void)
262 {
263 if (pclk_csr & CSR_UPDN) /* up or down? */
264 pclk_ctr = (pclk_ctr + 1) & DMASK; /* 1 = up */
265 else pclk_ctr = (pclk_ctr - 1) & DMASK; /* 0 = down */
266 if (pclk_ctr == 0) { /* reached zero? */
267 if (pclk_csr & CSR_DONE) /* done already set? */
268 pclk_csr = pclk_csr | CSR_ERR; /* set error */
269 else pclk_csr = pclk_csr | CSR_DONE; /* else set done */
270 if (pclk_csr & CSR_IE) SET_INT (PCLK); /* if IE, set int */
271 if (pclk_csr & CSR_MODE) pclk_ctr = pclk_csb; /* if rpt, reload */
272 else {
273 pclk_csb = 0; /* else clr ctr */
274 pclk_csr = pclk_csr & ~CSR_GO; /* and clr go */
275 }
276 }
277 return;
278 }
279
280 /* Clock service */
281
282 t_stat pclk_svc (UNIT *uptr)
283 {
284 int32 rv;
285
286 pclk_tick (); /* tick clock */
287 if ((pclk_csr & CSR_GO) == 0) return SCPE_OK; /* done? */
288 rv = CSR_GETRATE (pclk_csr); /* get rate */
289 sim_activate (&pclk_unit, sim_rtcn_calb (rate[rv], TMR_PCLK));
290 return SCPE_OK;
291 }
292
293 /* Clock reset */
294
295 t_stat pclk_reset (DEVICE *dptr)
296 {
297 pclk_csr = 0; /* clear reg */
298 pclk_csb = 0;
299 pclk_ctr = 0;
300 CLR_INT (PCLK); /* clear int */
301 sim_cancel (&pclk_unit); /* cancel */
302 pclk_unit.wait = xtim[0]; /* reset delay */
303 return SCPE_OK;
304 }
305
306 /* Set line frequency */
307
308 t_stat pclk_set_line (UNIT *uptr, int32 val, char *cptr, void *desc)
309 {
310 if (val == UNIT_LINE50HZ) rate[2] = 50;
311 else rate[2] = 60;
312 return SCPE_OK;
313 }