Commit | Line | Data |
---|---|---|
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 | |
63 | uint32 ke_AC = 0;\r | |
64 | uint32 ke_MQ = 0;\r | |
65 | uint32 ke_SC = 0;\r | |
66 | uint32 ke_SR = 0;\r | |
67 | \r | |
68 | DEVICE ke_dev;\r | |
69 | t_stat ke_rd (int32 *data, int32 PA, int32 access);\r | |
70 | t_stat ke_wr (int32 data, int32 PA, int32 access);\r | |
71 | t_stat ke_reset (DEVICE *dptr);\r | |
72 | uint32 ke_set_SR (void);\r | |
73 | \r | |
74 | DIB ke_dib = { IOBA_KE, IOLN_KE, &ke_rd, &ke_wr, 0 };\r | |
75 | \r | |
76 | UNIT ke_unit = {\r | |
77 | UDATA (NULL, UNIT_DISABLE, 0)\r | |
78 | };\r | |
79 | \r | |
80 | REG 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 | |
88 | MTAB 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 | |
94 | DEVICE 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 | |
104 | t_stat ke_rd (int32 *data, int32 PA, int32 access)\r | |
105 | { \r | |
106 | switch (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 | |
129 | return SCPE_OK;\r | |
130 | }\r | |
131 | \r | |
132 | /* KE write - writes trigger actual arithmetic */\r | |
133 | \r | |
134 | t_stat ke_wr (int32 data, int32 PA, int32 access)\r | |
135 | {\r | |
136 | int32 quo, t32, sout, sign;\r | |
137 | uint32 absd, absr;\r | |
138 | \r | |
139 | switch (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 | |
313 | ke_set_SR ();\r | |
314 | return SCPE_OK;\r | |
315 | }\r | |
316 | \r | |
317 | /* Update status register based on current AC, MQ */\r | |
318 | \r | |
319 | uint32 ke_set_SR (void)\r | |
320 | {\r | |
321 | ke_SR &= ~KE_SR_DYN; /* clr dynamic bits */\r | |
322 | if (ke_MQ == 0) /* MQ == 0? */\r | |
323 | ke_SR |= KE_SR_MQZ;\r | |
324 | if (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 | |
331 | if (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 | |
336 | return ke_SR;\r | |
337 | }\r | |
338 | \r | |
339 | /* Reset routine */\r | |
340 | \r | |
341 | t_stat ke_reset (DEVICE *dptr)\r | |
342 | {\r | |
343 | ke_SR = 0;\r | |
344 | ke_SC = 0;\r | |
345 | ke_AC = 0;\r | |
346 | ke_MQ = 0;\r | |
347 | return SCPE_OK;\r | |
348 | }\r |