First Commit of my working state
[simh.git] / PDP11 / pdp11_kg.c
CommitLineData
196ba1fc
PH
1/* pdp11_kg.c - Communications Arithmetic Option KG11-A\r
2\r
3 Copyright (c) 2007-2008, John A. Dundas III\r
4\r
5 Permission is hereby granted, free of charge, to any person obtaining a\r
6 copy of this software and associated documentation files (the "Software"),\r
7 to deal in the Software without restriction, including without limitation\r
8 the rights to use, copy, modify, merge, publish, distribute, sublicense,\r
9 and/or sell copies of the Software, and to permit persons to whom the\r
10 Software is furnished to do so, subject to the following conditions:\r
11\r
12 The above copyright notice and this permission notice shall be included in\r
13 all copies or substantial portions of the Software.\r
14\r
15 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\r
16 IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\r
17 FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL\r
18 ROBERT M SUPNIK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER\r
19 IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN\r
20 CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.\r
21\r
22 Except as contained in this notice, the name of the author shall not be\r
23 used in advertising or otherwise to promote the sale, use or other dealings\r
24 in this Software without prior written authorization from the author.\r
25\r
26 kg KG11-A Communications Arithmetic Option (M7251)\r
27\r
28 08-Jan-08 JAD First public release integrated with SIMH V3.7-3.\r
29 09-Dec-07 JAD SIMH-style debugging.\r
30 Finished validating against real hardware.\r
31 Support for up to 8 units, the maximum.\r
32 Keep all module data in the UNIT structure.\r
33 Clean up bit and mask definitions.\r
34 01-Dec-07 JAD Now work on the corner cases that the\r
35 diagnostic does not check.\r
36 CLR does not clear the QUO bit.\r
37 Correct SR write logic.\r
38 29-Nov-07 JAD Original implementation and testing based on\r
39 an idea from 07-Jul-03. Passes the ZKGAB0\r
40 diagnostic.\r
41\r
42 Information necessary to create this simulation was gathered from\r
43 a number of sources including:\r
44\r
45 KG11-A Exclusive-OR and CRC block check manual, DEC-11-HKGAA-B-D\r
46 <http://www.computer.museum.uq.edu.au/pdf/DEC-11-HKGAA-B-D%20KG11-A%20Exclusive-OR%20and%20CRC%20Block%20Check%20Manual.pdf>\r
47 Maintenance print set\r
48 <http://bitsavers.org/pdf/dec/unibus/KG11A_EngrDrws.pdf>\r
49 A Painless Guide to CRC Error Detection Algorithms, Ross N. Williams\r
50 <http://www.ross.net/crc/download/crc_v3.txt">\r
51\r
52 The original PDP-11 instruction set, as implemented in the /20,\r
53 /15, /10, and /5, did not include XOR. [One of the differences\r
54 tables incorrectly indicates the /04 does not implement this\r
55 instruction.] This device implements XOR, XORB, and a variety of\r
56 CRCs.\r
57\r
58 The maintenance prints indicate that the device was probably available\r
59 starting in late 1971. May need to check further. The first edition\r
60 of the manual was May 1972.\r
61\r
62 The device was still sold at least as late as mid-1982 according\r
63 to the PDP-11 Systems and Options Summary. RSTS/E included support\r
64 for up to 8 units in support of the 2780 emulation or for use with\r
65 DP11, DU11, or DUP11. The device appears to have been retired by\r
66 1983-03, and possibly earlier.\r
67\r
68 I/O Page Registers\r
69\r
70 SR 7707x0 (read-write) status\r
71 BCC 7707x2 (read-only) BCC (block check character)\r
72 DR 7707x4 (write-only) data\r
73\r
74 Vector: none\r
75\r
76 Priority: none\r
77\r
78 The KG11-A is a programmed-I/O, non-interrupting device. Therefore\r
79 no vector or bus request level are necessary. It is a Unibus device\r
80 but since it does not address memory itself (it only presents\r
81 registers in the I/O page) it is compatible with extended Unibus\r
82 machines (22-bit) as well as traditional Unibus.\r
83\r
84 Implements 5 error detection codes:\r
85 LRC-8\r
86 LRC-16\r
87 CRC-12\r
88 CRC-16\r
89 CRC-CCITT\r
90*/\r
91\r
92#if !defined (VM_PDP11)\r
93#error "KG11 is not supported!"\r
94#endif\r
95#include "pdp11_defs.h"\r
96\r
97extern FILE *sim_deb;\r
98extern REG cpu_reg[];\r
99extern int32 R[];\r
100\r
101#ifndef KG_UNITS\r
102#define KG_UNITS (8)\r
103#endif\r
104\r
105/* Control and Status Register */\r
106\r
107#define KGSR_V_QUO (8) /* RO */\r
108#define KGSR_V_DONE (7) /* RO */\r
109#define KGSR_V_SEN (6) /* R/W shift enable */\r
110#define KGSR_V_STEP (5) /* W */\r
111#define KGSR_V_CLR (4) /* W */\r
112#define KGSR_V_DDB (3) /* R/W double data byte */\r
113#define KGSR_V_CRCIC (2) /* R/W */\r
114#define KGSR_V_LRC (1) /* R/W */\r
115#define KGSR_V_16 (0) /* R/W */\r
116\r
117#define KGSR_M_QUO (1u << KGSR_V_QUO)\r
118#define KGSR_M_DONE (1u << KGSR_V_DONE)\r
119#define KGSR_M_SEN (1u << KGSR_V_SEN)\r
120#define KGSR_M_STEP (1u << KGSR_V_STEP)\r
121#define KGSR_M_CLR (1u << KGSR_V_CLR)\r
122#define KGSR_M_DDB (1u << KGSR_V_DDB)\r
123#define KGSR_M_CRCIC (1u << KGSR_V_CRCIC)\r
124#define KGSR_M_LRC (1u << KGSR_V_LRC)\r
125#define KGSR_M_16 (1u << KGSR_V_16)\r
126\r
127#define KG_SR_RDMASK (KGSR_M_QUO | KGSR_M_DONE | KGSR_M_SEN | KGSR_M_DDB | \\r
128 KGSR_M_CRCIC | KGSR_M_LRC | KGSR_M_16)\r
129#define KG_SR_WRMASK (KGSR_M_SEN | KGSR_M_DDB | KGSR_M_CRCIC | \\r
130 KGSR_M_LRC | KGSR_M_16)\r
131\r
132#define KG_SR_POLYMASK (KGSR_M_CRCIC|KGSR_M_LRC|KGSR_M_16)\r
133\r
134/* Unit structure redefinitions */\r
135#define SR u3\r
136#define BCC u4\r
137#define DR u5\r
138#define PULSCNT u6\r
139\r
140#define POLY_LRC8 (0x0008)\r
141#define POLY_LRC16 (0x0080)\r
142#define POLY_CRC12 (0x0f01)\r
143#define POLY_CRC16 (0xa001)\r
144#define POLY_CCITT (0x8408)\r
145\r
146static const struct {\r
147 uint16 poly;\r
148 uint16 pulses;\r
149 const char * const name;\r
150} config[] = {\r
151 /* DDB=0 */\r
152 { POLY_CRC12, 6, "CRC-12" },\r
153 { POLY_CRC16, 8, "CRC-16" },\r
154 { POLY_LRC8, 8, "LRC-8" },\r
155 { POLY_LRC16, 8, "LRC-16" },\r
156 { 0, 0, "undefined" },\r
157 { POLY_CCITT, 8, "CRC-CCITT" },\r
158 { 0, 0, "undefined" },\r
159 { 0, 0, "undefined" },\r
160 /* DDB=1 */\r
161 { POLY_CRC12, 12, "CRC-12" },\r
162 { POLY_CRC16, 16, "CRC-16" },\r
163 { POLY_LRC8, 16, "LRC-8" },\r
164 { POLY_LRC16, 16, "LRC-16" },\r
165 { 0, 0, "undefined" },\r
166 { POLY_CCITT, 16, "CRC-CCITT" },\r
167 { 0, 0, "undefined" },\r
168 { 0, 0, "undefined" }\r
169};\r
170\r
171/* Forward declarations */\r
172\r
173static t_stat kg_rd (int32 *, int32, int32);\r
174static t_stat kg_wr (int32, int32, int32);\r
175static t_stat kg_reset (DEVICE *);\r
176static void do_poly (int, t_bool);\r
177static t_stat set_units (UNIT *, int32, char *, void *);\r
178\r
179/* 16-bit rotate right */\r
180\r
181#define ROR(n,v) (((v >> n) & DMASK) | (v << (16 - n)) & DMASK)\r
182\r
183/* 8-bit rotate right */\r
184\r
185#define RORB(n,v) (((v & 0377) >> n) | ((v << (8 - n)) & 0377))\r
186\r
187/* KG data structures\r
188\r
189 kg_dib KG PDP-11 device information block\r
190 kg_unit KG unit descriptor\r
191 kg_reg KG register list\r
192 kg_mod KG modifiers table\r
193 kg_debug KG debug names table\r
194 kg_dev KG device descriptor\r
195*/\r
196\r
197static DIB kg_dib = {\r
198 IOBA_KG,\r
199 (IOLN_KG + 2) * KG_UNITS,\r
200 &kg_rd,\r
201 &kg_wr,\r
202 0, 0, 0, { NULL }\r
203};\r
204\r
205static UNIT kg_unit[] = {\r
206 { UDATA (NULL, 0, 0) },\r
207 { UDATA (NULL, UNIT_DISABLE + UNIT_DIS, 0) },\r
208 { UDATA (NULL, UNIT_DISABLE + UNIT_DIS, 0) },\r
209 { UDATA (NULL, UNIT_DISABLE + UNIT_DIS, 0) },\r
210 { UDATA (NULL, UNIT_DISABLE + UNIT_DIS, 0) },\r
211 { UDATA (NULL, UNIT_DISABLE + UNIT_DIS, 0) },\r
212 { UDATA (NULL, UNIT_DISABLE + UNIT_DIS, 0) },\r
213 { UDATA (NULL, UNIT_DISABLE + UNIT_DIS, 0) },\r
214};\r
215\r
216static const REG kg_reg[] = {\r
217 { ORDATA (SR0, kg_unit[0].SR, 16) },\r
218 { ORDATA (SR1, kg_unit[1].SR, 16) },\r
219 { ORDATA (SR2, kg_unit[2].SR, 16) },\r
220 { ORDATA (SR3, kg_unit[3].SR, 16) },\r
221 { ORDATA (SR4, kg_unit[4].SR, 16) },\r
222 { ORDATA (SR5, kg_unit[5].SR, 16) },\r
223 { ORDATA (SR6, kg_unit[6].SR, 16) },\r
224 { ORDATA (SR7, kg_unit[7].SR, 16) },\r
225 { ORDATA (BCC0, kg_unit[0].BCC, 16) },\r
226 { ORDATA (BCC1, kg_unit[1].BCC, 16) },\r
227 { ORDATA (BCC2, kg_unit[2].BCC, 16) },\r
228 { ORDATA (BCC3, kg_unit[3].BCC, 16) },\r
229 { ORDATA (BCC4, kg_unit[4].BCC, 16) },\r
230 { ORDATA (BCC5, kg_unit[5].BCC, 16) },\r
231 { ORDATA (BCC6, kg_unit[6].BCC, 16) },\r
232 { ORDATA (BCC7, kg_unit[7].BCC, 16) },\r
233 { ORDATA (DR0, kg_unit[0].DR, 16) },\r
234 { ORDATA (DR1, kg_unit[1].DR, 16) },\r
235 { ORDATA (DR2, kg_unit[2].DR, 16) },\r
236 { ORDATA (DR3, kg_unit[3].DR, 16) },\r
237 { ORDATA (DR4, kg_unit[4].DR, 16) },\r
238 { ORDATA (DR5, kg_unit[5].DR, 16) },\r
239 { ORDATA (DR6, kg_unit[6].DR, 16) },\r
240 { ORDATA (DR7, kg_unit[7].DR, 16) },\r
241 { ORDATA (PULSCNT0, kg_unit[0].PULSCNT, 16) },\r
242 { ORDATA (PULSCNT1, kg_unit[1].PULSCNT, 16) },\r
243 { ORDATA (PULSCNT2, kg_unit[2].PULSCNT, 16) },\r
244 { ORDATA (PULSCNT3, kg_unit[3].PULSCNT, 16) },\r
245 { ORDATA (PULSCNT4, kg_unit[4].PULSCNT, 16) },\r
246 { ORDATA (PULSCNT5, kg_unit[5].PULSCNT, 16) },\r
247 { ORDATA (PULSCNT6, kg_unit[6].PULSCNT, 16) },\r
248 { ORDATA (PULSCNT7, kg_unit[7].PULSCNT, 16) },\r
249 { ORDATA (DEVADDR, kg_dib.ba, 32), REG_HRO },\r
250 { NULL }\r
251};\r
252\r
253static const MTAB kg_mod[] = {\r
254 { MTAB_XTD|MTAB_VDV, 0, "ADDRESS", NULL, NULL, &show_addr, NULL },\r
255 { MTAB_XTD|MTAB_VDV, 0, NULL, "UNITS=0..8", &set_units, NULL, NULL },\r
256 { 0 }\r
257};\r
258\r
259#define DBG_REG (01)\r
260#define DBG_POLY (02)\r
261#define DBG_CYCLE (04)\r
262\r
263static const DEBTAB kg_debug[] = {\r
264 {"REG", DBG_REG},\r
265 {"POLY", DBG_POLY},\r
266 {"CYCLE", DBG_CYCLE},\r
267 {0},\r
268};\r
269\r
270DEVICE kg_dev = {\r
271 "KG", (UNIT *) &kg_unit, (REG *) kg_reg, (MTAB *) kg_mod,\r
272 KG_UNITS, 8, 16, 2, 8, 16,\r
273 NULL, /* examine */\r
274 NULL, /* deposit */\r
275 &kg_reset, /* reset */\r
276 NULL, /* boot */\r
277 NULL, /* attach */\r
278 NULL, /* detach */\r
279 &kg_dib,\r
280 DEV_DISABLE | DEV_DIS | DEV_UBUS | DEV_DEBUG,\r
281 0, /* debug control */\r
282 (DEBTAB *) &kg_debug, /* debug flags */\r
283 NULL, /* memory size chage */\r
284 NULL /* logical name */\r
285};\r
286\f /* KG I/O address routines */\r
287\r
288static t_stat kg_rd (int32 *data, int32 PA, int32 access)\r
289{\r
290 int unit = (PA >> 3) & 07;\r
291\r
292 if ((unit >= KG_UNITS) || (kg_unit[unit].flags & UNIT_DIS))\r
293 return (SCPE_NXM);\r
294 switch ((PA >> 1) & 03) {\r
295\r
296 case 00: /* SR */\r
297 if (DEBUG_PRI(kg_dev, DBG_REG))\r
298 fprintf (sim_deb, ">>KG%d: rd SR %06o, PC %06o\n",\r
299 unit, kg_unit[unit].SR, PC);\r
300 *data = kg_unit[unit].SR & KG_SR_RDMASK;\r
301 break;\r
302\r
303 case 01: /* BCC */\r
304 if (DEBUG_PRI(kg_dev, DBG_REG))\r
305 fprintf (sim_deb, ">>KG%d rd BCC %06o, PC %06o\n",\r
306 unit, kg_unit[unit].BCC, PC);\r
307 *data = kg_unit[unit].BCC & DMASK;\r
308 break;\r
309\r
310 case 02: /* DR */\r
311 break;\r
312\r
313 default:\r
314 break;\r
315 }\r
316 return (SCPE_OK);\r
317}\r
318\r
319static t_stat kg_wr (int32 data, int32 PA, int32 access)\r
320{\r
321 int setup;\r
322 int unit = (PA >> 3) & 07;\r
323\r
324 if ((unit >= KG_UNITS) || (kg_unit[unit].flags & UNIT_DIS))\r
325 return (SCPE_NXM);\r
326 switch ((PA >> 1) & 03) {\r
327\r
328 case 00: /* SR */\r
329 if (access == WRITEB)\r
330 data = (PA & 1) ?\r
331 (kg_unit[unit].SR & 0377) | (data << 8) :\r
332 (kg_unit[unit].SR & ~0377) | data;\r
333 if (DEBUG_PRI(kg_dev, DBG_REG))\r
334 fprintf (sim_deb, ">>KG%d: wr SR %06o, PC %06o\n",\r
335 unit, data, PC);\r
336 if (data & KGSR_M_CLR) {\r
337 kg_unit[unit].PULSCNT = 0; /* not sure about this */\r
338 kg_unit[unit].BCC = 0;\r
339 kg_unit[unit].SR |= KGSR_M_DONE;\r
340 }\r
341 setup = (kg_unit[unit].SR & 017) ^ (data & 017);\r
342 kg_unit[unit].SR = (kg_unit[unit].SR & ~KG_SR_WRMASK) |\r
343 (data & KG_SR_WRMASK);\r
344 /* if low 4b changed, reset C1 & C2 */\r
345 if (setup) {\r
346 kg_unit[unit].PULSCNT = 0;\r
347 if (DEBUG_PRI(kg_dev, DBG_POLY))\r
348 fprintf (sim_deb, ">>KG%d poly %s %d\n",\r
349 unit, config[data & 017].name, config[data & 017].pulses);\r
350 }\r
351 if (data & KGSR_M_SEN)\r
352 break;\r
353 if (data & KGSR_M_STEP) {\r
354 do_poly (unit, TRUE);\r
355 break;\r
356 }\r
357 break;\r
358\r
359 case 01: /* BCC */\r
360 break; /* ignored */\r
361\r
362 case 02: /* DR */\r
363 if (access == WRITEB)\r
364 data = (PA & 1) ?\r
365 (kg_unit[unit].DR & 0377) | (data << 8) :\r
366 (kg_unit[unit].DR & ~0377) | data;\r
367 kg_unit[unit].DR = data & DMASK;\r
368 if (DEBUG_PRI(kg_dev, DBG_REG))\r
369 fprintf (sim_deb, ">>KG%d: wr DR %06o, data %06o, PC %06o\n",\r
370 unit, kg_unit[unit].DR, data, PC);\r
371 kg_unit[unit].SR &= ~KGSR_M_DONE;\r
372\r
373/* In a typical device, this is normally where we would use sim_activate()\r
374 to initiate an I/O to be completed later. The KG is a little\r
375 different. When it was first introduced, it's computation operation\r
376 completed before another instruction could execute (on early PDP-11s),\r
377 and software often took "advantage" of this fact by not bothering\r
378 to check the status of the DONE bit. In reality, the execution\r
379 time of the polynomial is dependent upon the width of the poly; if\r
380 8 bits 1us, if 16 bits, 2us. Since known existing software will\r
381 break if we actually defer the computation, it is performed immediately\r
382 instead. However this could easily be made into a run-time option,\r
383 if there were software to validate correct operation. */\r
384\r
385 if (kg_unit[unit].SR & KGSR_M_SEN)\r
386 do_poly (unit, FALSE);\r
387 break;\r
388\r
389 default:\r
390 break;\r
391 }\r
392 return (SCPE_OK);\r
393}\r
394\r
395/* KG reset */\r
396\r
397static t_stat kg_reset (DEVICE *dptr)\r
398{\r
399 int i;\r
400\r
401 if (DEBUG_PRI(kg_dev, DBG_REG))\r
402 fprintf (sim_deb, ">>KG: reset PC %06o\n", PC);\r
403 for (i = 0; i < KG_UNITS; i++) {\r
404 kg_unit[i].SR = KGSR_M_DONE;\r
405 kg_unit[i].BCC = 0;\r
406 kg_unit[i].PULSCNT = 0;\r
407 }\r
408 return (SCPE_OK);\r
409}\r
410\r
411static void cycleOneBit (int unit)\r
412{\r
413 int quo;\r
414\r
415 if (DEBUG_PRI(kg_dev, DBG_CYCLE))\r
416 fprintf (sim_deb, ">>KG%d: cycle s BCC %06o DR %06o\n",\r
417 unit, kg_unit[unit].BCC, kg_unit[unit].DR);\r
418 if (kg_unit[unit].SR & KGSR_M_DONE)\r
419 return;\r
420 if ((kg_unit[unit].SR & KG_SR_POLYMASK) == 0)\r
421 kg_unit[unit].BCC = (kg_unit[unit].BCC & 077) |\r
422 ((kg_unit[unit].BCC >> 2) & 07700);\r
423 kg_unit[unit].SR &= ~KGSR_M_QUO;\r
424 quo = (kg_unit[unit].BCC & 01) ^ (kg_unit[unit].DR & 01);\r
425 kg_unit[unit].BCC = (kg_unit[unit].BCC & ~01) | quo;\r
426 if (kg_unit[unit].SR & KGSR_M_LRC)\r
427 kg_unit[unit].BCC = (kg_unit[unit].SR & KGSR_M_16) ?\r
428 ROR(1, kg_unit[unit].BCC) :\r
429 RORB(1, kg_unit[unit].BCC);\r
430 else\r
431 kg_unit[unit].BCC = (kg_unit[unit].BCC & 01) ?\r
432 (kg_unit[unit].BCC >> 1) ^ config[kg_unit[unit].SR & 07].poly :\r
433 kg_unit[unit].BCC >> 1;\r
434 kg_unit[unit].DR >>= 1;\r
435 kg_unit[unit].SR |= quo << KGSR_V_QUO;\r
436 if ((kg_unit[unit].SR & KG_SR_POLYMASK) == 0)\r
437 kg_unit[unit].BCC = (kg_unit[unit].BCC & 077) |\r
438 ((kg_unit[unit].BCC & 07700) << 2);\r
439 kg_unit[unit].PULSCNT++;\r
440 if (kg_unit[unit].PULSCNT >= config[kg_unit[unit].SR & 017].pulses)\r
441 kg_unit[unit].SR |= KGSR_M_DONE;\r
442 if (DEBUG_PRI(kg_dev, DBG_CYCLE))\r
443 fprintf (sim_deb, ">>KG%d: cycle e BCC %06o DR %06o\n",\r
444 unit, kg_unit[unit].BCC, kg_unit[unit].DR);\r
445}\r
446\r
447static void do_poly (int unit, t_bool step)\r
448{\r
449 if (kg_unit[unit].SR & KGSR_M_DONE)\r
450 return;\r
451 if (step)\r
452 cycleOneBit (unit);\r
453 else {\r
454 while (!(kg_unit[unit].SR & KGSR_M_DONE))\r
455 cycleOneBit (unit);\r
456 }\r
457}\r
458\r
459static t_stat set_units (UNIT *u, int32 val, char *s, void *desc)\r
460{\r
461 int32 i, units;\r
462 t_stat stat;\r
463\r
464 if (s == NULL)\r
465 return (SCPE_ARG);\r
466 units = get_uint (s, 10, KG_UNITS, &stat);\r
467 if (stat != SCPE_OK)\r
468 return (stat);\r
469 for (i = 0; i < KG_UNITS; i++) {\r
470 if (i < units)\r
471 kg_unit[i].flags &= ~UNIT_DIS;\r
472 else\r
473 kg_unit[i].flags |= UNIT_DIS;\r
474 }\r
475 kg_dev.numunits = units;\r
476 return (SCPE_OK);\r
477}\r