First Commit of my working state
[simh.git] / PDP11 / pdp11_ke.c
CommitLineData
196ba1fc
PH
1/* pdp11_ke.c: PDP-11/20 extended arithmetic element\r
2\r
3 Copyright (c) 1993-2008, Robert M Supnik\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 ke_ACTION OF CONTRke_ACT, 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 Robert M Supnik 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 Robert M Supnik.\r
25\r
26 This code draws on prior work by Tim Shoppa and Brad Parker. My thanks for\r
27 to them for letting me use their work.\r
28\r
29 EAE PDP-11/20 extended arithmetic element\r
30*/\r
31\r
32#include "pdp11_defs.h"\r
33\r
34#define GET_SIGN_L(v) (((v) >> 31) & 1)\r
35#define GET_SIGN_W(v) (((v) >> 15) & 1)\r
36#define GET_SIGN_B(v) (((v) >> 7) & 1)\r
37\r
38/* KE11A I/O address offsets 0177300 - 0177316 */\r
39\r
40#define KE_DIV 000 /* divide */\r
41#define KE_AC 002 /* accumulator */\r
42#define KE_MQ 004 /* MQ */\r
43#define KE_MUL 006 /* multiply */\r
44#define KE_SC 010 /* step counter */\r
45#define KE_NOR 012 /* normalize */\r
46#define KE_LSH 014 /* logical shift */\r
47#define KE_ASH 016 /* arithmetic shift */\r
48\r
49/* Status register */\r
50\r
51#define KE_SR_C 0001 /* carry */\r
52#define KE_SR_SXT 0002 /* AC<15:0> = MQ<15> */\r
53#define KE_SR_Z 0004 /* AC = MQ = 0 */\r
54#define KE_SR_MQZ 0010 /* MQ = 0 */\r
55#define KE_SR_ACZ 0020 /* AC = 0 */\r
56#define KE_SR_ACM1 0040 /* AC = 177777 */\r
57#define KE_SR_N 0100 /* last op negative */\r
58#define KE_SR_NXV 0200 /* last op ovf XOR N */\r
59#define KE_SR_DYN (KE_SR_SXT|KE_SR_Z|KE_SR_MQZ|KE_SR_ACZ|KE_SR_ACM1)\r
60\r
61/* Visible state */\r
62\r
63uint32 ke_AC = 0;\r
64uint32 ke_MQ = 0;\r
65uint32 ke_SC = 0;\r
66uint32 ke_SR = 0;\r
67\r
68DEVICE ke_dev;\r
69t_stat ke_rd (int32 *data, int32 PA, int32 access);\r
70t_stat ke_wr (int32 data, int32 PA, int32 access);\r
71t_stat ke_reset (DEVICE *dptr);\r
72uint32 ke_set_SR (void);\r
73\r
74DIB ke_dib = { IOBA_KE, IOLN_KE, &ke_rd, &ke_wr, 0 };\r
75\r
76UNIT ke_unit = {\r
77 UDATA (NULL, UNIT_DISABLE, 0)\r
78 };\r
79\r
80REG ke_reg[] = {\r
81 { ORDATA (AC, ke_AC, 16) },\r
82 { ORDATA (MQ, ke_MQ, 16) },\r
83 { ORDATA (SC, ke_SC, 6) },\r
84 { ORDATA (SR, ke_SR, 8) },\r
85 { NULL }\r
86 };\r
87\r
88MTAB ke_mod[] = {\r
89 { MTAB_XTD|MTAB_VDV, 0, "ADDRESS", NULL,\r
90 NULL, &show_addr, NULL },\r
91 { 0 }\r
92 };\r
93\r
94DEVICE ke_dev = {\r
95 "KE", &ke_unit, ke_reg, ke_mod,\r
96 1, 10, 31, 1, 8, 8,\r
97 NULL, NULL, &ke_reset,\r
98 NULL, NULL, NULL,\r
99 &ke_dib, DEV_DISABLE | DEV_DIS | DEV_UBUS\r
100 };\r
101\r
102/* KE read - reads are always 16b, to even addresses */\r
103\r
104t_stat ke_rd (int32 *data, int32 PA, int32 access)\r
105{ \r
106switch (PA & 016) { /* decode PA<3:1> */\r
107\r
108 case KE_AC: /* AC */\r
109 *data = ke_AC;\r
110 break;\r
111\r
112 case KE_MQ: /* MQ */\r
113 *data = ke_MQ;\r
114 break;\r
115\r
116 case KE_NOR: /* norm (SC) */\r
117 *data = ke_SC;\r
118 break;\r
119\r
120 case KE_SC: /* SR/SC */\r
121 *data = (ke_set_SR () << 8) | ke_SC;\r
122 break;\r
123\r
124 default:\r
125 *data = 0;\r
126 break;\r
127 }\r
128\r
129return SCPE_OK;\r
130}\r
131\r
132/* KE write - writes trigger actual arithmetic */\r
133\r
134t_stat ke_wr (int32 data, int32 PA, int32 access)\r
135{\r
136int32 quo, t32, sout, sign;\r
137uint32 absd, absr;\r
138\r
139switch (PA & 017) { /* decode PA<3:0> */\r
140\r
141 case KE_DIV: /* divide */\r
142 if ((access == WRITEB) && GET_SIGN_B (data)) /* byte write? */\r
143 data |= 0177400; /* sext data to 16b */\r
144 ke_SR = 0; /* N = V = C = 0 */\r
145 t32 = (ke_AC << 16) | ke_MQ; /* 32b divd */\r
146 if (GET_SIGN_W (ke_AC)) /* sext (divd) */\r
147 t32 = t32 | ~017777777777;\r
148 if (GET_SIGN_W (data)) /* sext (divr) */\r
149 data = data | ~077777;\r
150 absd = abs (t32);\r
151 absr = abs (data);\r
152 if ((absd >> 16) >= absr) { /* divide fails? */\r
153\r
154/* Based on the documentation, here's what has happened:\r
155\r
156 SC = 16.\r
157 SR<c> = (AC<15> == data<15>)\r
158 AC'MQ = (AC'MQ << 1) | SR<c>\r
159 AC = SR<c>? AC - data: AC + data\r
160 SR<c> = (AC<15> == data<15>)\r
161 SC = SC - 1\r
162 stop\r
163*/\r
164\r
165 sign = GET_SIGN_W (ke_AC ^ data) ^ 1; /* 1 if signs match */\r
166 ke_AC = (ke_AC << 1) | (ke_MQ >> 15);\r
167 ke_AC = (sign? ke_AC - data: ke_AC + data) & DMASK;\r
168 ke_MQ = ((ke_MQ << 1) | sign) & DMASK;\r
169 if (GET_SIGN_W (ke_AC ^ data) == 0) /* 0 if signs match */\r
170 ke_SR |= KE_SR_C; \r
171 ke_SC = 15; /* SC clocked once */\r
172 ke_SR |= KE_SR_NXV; /* set overflow */\r
173 }\r
174 else {\r
175 ke_SC = 0;\r
176 quo = t32 / data;\r
177 ke_MQ = quo & DMASK; /* MQ has quo */\r
178 ke_AC = (t32 % data) & DMASK; /* AC has rem */\r
179 if ((quo > 32767) || (quo < -32768)) /* quo overflow? */\r
180 ke_SR |= KE_SR_NXV; /* set overflow */\r
181 }\r
182 if (GET_SIGN_W (ke_MQ)) /* result negative? */\r
183 ke_SR ^= (KE_SR_N | KE_SR_NXV); /* N = 1, compl NXV */\r
184 break;\r
185\r
186 case KE_AC: /* AC */\r
187 if ((access == WRITEB) && GET_SIGN_B (data)) /* byte write? */\r
188 data |= 0177400; /* sext data to 16b */\r
189 ke_AC = data;\r
190 break;\r
191\r
192 case KE_AC + 1: /* AC odd byte */\r
193 ke_AC = (ke_AC & 0377) | (data << 8);\r
194 break;\r
195\r
196 case KE_MQ: /* MQ */\r
197 if ((access == WRITEB) && GET_SIGN_B (data)) /* byte write? */\r
198 data |= 0177400; /* sext data to 16b */\r
199 ke_MQ = data;\r
200 if (GET_SIGN_W (ke_MQ)) /* sext MQ to AC */\r
201 ke_AC = 0177777;\r
202 else ke_AC = 0;\r
203 break;\r
204\r
205 case KE_MQ + 1: /* MQ odd byte */\r
206 ke_MQ = (ke_MQ & 0377) | (data << 8);\r
207 if (GET_SIGN_W (ke_MQ)) /* sext MQ to AC */\r
208 ke_AC = 0177777;\r
209 else ke_AC = 0;\r
210 break;\r
211\r
212 case KE_MUL: /* multiply */\r
213 if ((access == WRITEB) && GET_SIGN_B (data)) /* byte write? */\r
214 data |= 0177400; /* sext data to 16b */\r
215 ke_SC = 0;\r
216 if (GET_SIGN_W (data)) /* sext operands */\r
217 data |= ~077777;\r
218 t32 = ke_MQ;\r
219 if (GET_SIGN_W (t32))\r
220 t32 |= ~077777;\r
221 t32 = t32 * data;\r
222 ke_AC = (t32 >> 16) & DMASK;\r
223 ke_MQ = t32 & DMASK;\r
224 if (GET_SIGN_W (ke_AC)) /* result negative? */\r
225 ke_SR = KE_SR_N | KE_SR_NXV; /* N = 1, V = C = 0 */\r
226 else ke_SR = 0; /* N = 0, V = C = 0 */\r
227 break;\r
228\r
229 case KE_SC: /* SC */\r
230 if (access == WRITEB) /* ignore byte writes */\r
231 return SCPE_OK;\r
232 ke_SR = (data >> 8) & (KE_SR_NXV|KE_SR_N|KE_SR_C);\r
233 ke_SC = data & 077;\r
234 break;\r
235\r
236 case KE_NOR: /* normalize */\r
237 for (ke_SC = 0; ke_SC < 31; ke_SC++) { /* max 31 shifts */\r
238 if (((ke_AC == 0140000) && (ke_MQ == 0)) || /* special case? */\r
239 (GET_SIGN_W (ke_AC ^ (ke_AC << 1)))) /* AC<15> != AC<14>? */\r
240 break;\r
241 ke_AC = ((ke_AC << 1) | (ke_MQ >> 15)) & DMASK;\r
242 ke_MQ = (ke_MQ << 1) & DMASK;\r
243 }\r
244 if (GET_SIGN_W (ke_AC)) /* result negative? */\r
245 ke_SR = KE_SR_N | KE_SR_NXV; /* N = 1, V = C = 0 */\r
246 else ke_SR = 0; /* N = 0, V = C = 0 */\r
247 break;\r
248\r
249 case KE_LSH: /* logical shift */\r
250 ke_SC = 0;\r
251 ke_SR = 0; /* N = V = C = 0 */\r
252 data = data & 077; /* 6b shift count */\r
253 if (data != 0) {\r
254 t32 = (ke_AC << 16) | ke_MQ; /* 32b operand */\r
255 if (sign = GET_SIGN_W (ke_AC)) /* sext operand */\r
256 t32 = t32 | ~017777777777;\r
257 if (data < 32) { /* [1,31] - left */\r
258 sout = (t32 >> (32 - data)) | (-sign << data);\r
259 t32 = ((uint32) t32) << data; /* do shift (zext) */\r
260 if (sout != (GET_SIGN_L (t32)? -1: 0)) /* bits lost = sext? */\r
261 ke_SR |= KE_SR_NXV; /* no, V = 1 */\r
262 if (sout & 1) /* last bit lost = 1? */\r
263 ke_SR |= KE_SR_C; /* yes, C = 1 */\r
264 }\r
265 else { /* [32,63] = -32,-1 */\r
266 if ((t32 >> (63 - data)) & 1) /* last bit lost = 1? */\r
267 ke_SR |= KE_SR_C; /* yes, C = 1*/\r
268 t32 = (data != 32)? ((uint32) t32) >> (64 - data): 0;\r
269 }\r
270 ke_AC = (t32 >> 16) & DMASK;\r
271 ke_MQ = t32 & DMASK;\r
272 }\r
273 if (GET_SIGN_W (ke_AC)) /* result negative? */\r
274 ke_SR ^= (KE_SR_N | KE_SR_NXV); /* N = 1, compl NXV */\r
275 break;\r
276\r
277/* EAE ASH differs from EIS ASH and cannot use the same overflow test */\r
278\r
279 case KE_ASH: /* arithmetic shift */\r
280 ke_SC = 0;\r
281 ke_SR = 0; /* N = V = C = 0 */\r
282 data = data & 077; /* 6b shift count */\r
283 if (data != 0) {\r
284 t32 = (ke_AC << 16) | ke_MQ; /* 32b operand */\r
285 if (sign = GET_SIGN_W (ke_AC)) /* sext operand */\r
286 t32 = t32 | ~017777777777;\r
287 if (data < 32) { /* [1,31] - left */\r
288 sout = (t32 >> (31 - data)) | (-sign << data);\r
289 t32 = (t32 & 020000000000) | ((t32 << data) & 017777777777);\r
290 if (sout != (GET_SIGN_L (t32)? -1: 0)) /* bits lost = sext? */\r
291 ke_SR |= KE_SR_NXV; /* no, V = 1 */\r
292 if (sout & 1) /* last bit lost = 1? */\r
293 ke_SR |= KE_SR_C; /* yes, C = 1 */\r
294 }\r
295 else { /* [32,63] = -32,-1 */\r
296 if ((t32 >> (63 - data)) & 1) /* last bit lost = 1? */\r
297 ke_SR |= KE_SR_C; /* yes, C = 1 */\r
298 t32 = (data != 32)? /* special case 32 */\r
299 (((uint32) t32) >> (64 - data)) | (-sign << (data - 32)):\r
300 -sign;\r
301 }\r
302 ke_AC = (t32 >> 16) & DMASK;\r
303 ke_MQ = t32 & DMASK;\r
304 }\r
305 if (GET_SIGN_W (ke_AC)) /* result negative? */\r
306 ke_SR ^= (KE_SR_N | KE_SR_NXV); /* N = 1, compl NXV */\r
307 break;\r
308\r
309 default: /* all others ignored */\r
310 return SCPE_OK;\r
311 } /* end switch PA */\r
312\r
313ke_set_SR ();\r
314return SCPE_OK;\r
315}\r
316\r
317/* Update status register based on current AC, MQ */\r
318\r
319uint32 ke_set_SR (void)\r
320{\r
321ke_SR &= ~KE_SR_DYN; /* clr dynamic bits */\r
322if (ke_MQ == 0) /* MQ == 0? */\r
323 ke_SR |= KE_SR_MQZ;\r
324if (ke_AC == 0) { /* AC == 0? */\r
325 ke_SR |= KE_SR_ACZ;\r
326 if (GET_SIGN_W (ke_MQ) == 0) /* MQ positive? */\r
327 ke_SR |= KE_SR_SXT;\r
328 if (ke_MQ == 0) /* MQ zero? */\r
329 ke_SR |= KE_SR_Z;\r
330 }\r
331if (ke_AC == 0177777) { /* AC == 177777? */\r
332 ke_SR |= KE_SR_ACM1;\r
333 if (GET_SIGN_W (ke_MQ) == 1) /* MQ negative? */\r
334 ke_SR |= KE_SR_SXT;\r
335 }\r
336return ke_SR;\r
337}\r
338\r
339/* Reset routine */\r
340\r
341t_stat ke_reset (DEVICE *dptr)\r
342{\r
343ke_SR = 0;\r
344ke_SC = 0;\r
345ke_AC = 0;\r
346ke_MQ = 0;\r
347return SCPE_OK;\r
348}\r