1 /* pdp11_ke.c: PDP-11/20 extended arithmetic element
3 Copyright (c) 1993-2008, Robert M Supnik
5 Permission is hereby granted, free of charge, to any person obtaining a
6 copy of this software and associated documentation files (the "Software"),
7 to deal in the Software without restriction, including without limitation
8 the rights to use, copy, modify, merge, publish, distribute, sublicense,
9 and/or sell copies of the Software, and to permit persons to whom the
10 Software is furnished to do so, subject to the following conditions:
12 The above copyright notice and this permission notice shall be included in
13 all copies or substantial portions of the Software.
15 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
18 ROBERT M SUPNIK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
19 IN AN ke_ACTION OF CONTRke_ACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
20 CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
22 Except as contained in this notice, the name of Robert M Supnik shall not be
23 used in advertising or otherwise to promote the sale, use or other dealings
24 in this Software without prior written authorization from Robert M Supnik.
26 This code draws on prior work by Tim Shoppa and Brad Parker. My thanks for
27 to them for letting me use their work.
29 EAE PDP-11/20 extended arithmetic element
32 #include "pdp11_defs.h"
34 #define GET_SIGN_L(v) (((v) >> 31) & 1)
35 #define GET_SIGN_W(v) (((v) >> 15) & 1)
36 #define GET_SIGN_B(v) (((v) >> 7) & 1)
38 /* KE11A I/O address offsets 0177300 - 0177316 */
40 #define KE_DIV 000 /* divide */
41 #define KE_AC 002 /* accumulator */
42 #define KE_MQ 004 /* MQ */
43 #define KE_MUL 006 /* multiply */
44 #define KE_SC 010 /* step counter */
45 #define KE_NOR 012 /* normalize */
46 #define KE_LSH 014 /* logical shift */
47 #define KE_ASH 016 /* arithmetic shift */
51 #define KE_SR_C 0001 /* carry */
52 #define KE_SR_SXT 0002 /* AC<15:0> = MQ<15> */
53 #define KE_SR_Z 0004 /* AC = MQ = 0 */
54 #define KE_SR_MQZ 0010 /* MQ = 0 */
55 #define KE_SR_ACZ 0020 /* AC = 0 */
56 #define KE_SR_ACM1 0040 /* AC = 177777 */
57 #define KE_SR_N 0100 /* last op negative */
58 #define KE_SR_NXV 0200 /* last op ovf XOR N */
59 #define KE_SR_DYN (KE_SR_SXT|KE_SR_Z|KE_SR_MQZ|KE_SR_ACZ|KE_SR_ACM1)
69 t_stat
ke_rd (int32
*data
, int32 PA
, int32 access
);
70 t_stat
ke_wr (int32 data
, int32 PA
, int32 access
);
71 t_stat
ke_reset (DEVICE
*dptr
);
72 uint32
ke_set_SR (void);
74 DIB ke_dib
= { IOBA_KE
, IOLN_KE
, &ke_rd
, &ke_wr
, 0 };
77 UDATA (NULL
, UNIT_DISABLE
, 0)
81 { ORDATA (AC
, ke_AC
, 16) },
82 { ORDATA (MQ
, ke_MQ
, 16) },
83 { ORDATA (SC
, ke_SC
, 6) },
84 { ORDATA (SR
, ke_SR
, 8) },
89 { MTAB_XTD
|MTAB_VDV
, 0, "ADDRESS", NULL
,
90 NULL
, &show_addr
, NULL
},
95 "KE", &ke_unit
, ke_reg
, ke_mod
,
97 NULL
, NULL
, &ke_reset
,
99 &ke_dib
, DEV_DISABLE
| DEV_DIS
| DEV_UBUS
102 /* KE read - reads are always 16b, to even addresses */
104 t_stat
ke_rd (int32
*data
, int32 PA
, int32 access
)
106 switch (PA
& 016) { /* decode PA<3:1> */
116 case KE_NOR
: /* norm (SC) */
120 case KE_SC
: /* SR/SC */
121 *data
= (ke_set_SR () << 8) | ke_SC
;
132 /* KE write - writes trigger actual arithmetic */
134 t_stat
ke_wr (int32 data
, int32 PA
, int32 access
)
136 int32 quo
, t32
, sout
, sign
;
139 switch (PA
& 017) { /* decode PA<3:0> */
141 case KE_DIV
: /* divide */
142 if ((access
== WRITEB
) && GET_SIGN_B (data
)) /* byte write? */
143 data
|= 0177400; /* sext data to 16b */
144 ke_SR
= 0; /* N = V = C = 0 */
145 t32
= (ke_AC
<< 16) | ke_MQ
; /* 32b divd */
146 if (GET_SIGN_W (ke_AC
)) /* sext (divd) */
147 t32
= t32
| ~017777777777;
148 if (GET_SIGN_W (data
)) /* sext (divr) */
149 data
= data
| ~077777;
152 if ((absd
>> 16) >= absr
) { /* divide fails? */
154 /* Based on the documentation, here's what has happened:
157 SR<c> = (AC<15> == data<15>)
158 AC'MQ = (AC'MQ << 1) | SR<c>
159 AC = SR<c>? AC - data: AC + data
160 SR<c> = (AC<15> == data<15>)
165 sign
= GET_SIGN_W (ke_AC
^ data
) ^ 1; /* 1 if signs match */
166 ke_AC
= (ke_AC
<< 1) | (ke_MQ
>> 15);
167 ke_AC
= (sign
? ke_AC
- data
: ke_AC
+ data
) & DMASK
;
168 ke_MQ
= ((ke_MQ
<< 1) | sign
) & DMASK
;
169 if (GET_SIGN_W (ke_AC
^ data
) == 0) /* 0 if signs match */
171 ke_SC
= 15; /* SC clocked once */
172 ke_SR
|= KE_SR_NXV
; /* set overflow */
177 ke_MQ
= quo
& DMASK
; /* MQ has quo */
178 ke_AC
= (t32
% data
) & DMASK
; /* AC has rem */
179 if ((quo
> 32767) || (quo
< -32768)) /* quo overflow? */
180 ke_SR
|= KE_SR_NXV
; /* set overflow */
182 if (GET_SIGN_W (ke_MQ
)) /* result negative? */
183 ke_SR
^= (KE_SR_N
| KE_SR_NXV
); /* N = 1, compl NXV */
187 if ((access
== WRITEB
) && GET_SIGN_B (data
)) /* byte write? */
188 data
|= 0177400; /* sext data to 16b */
192 case KE_AC
+ 1: /* AC odd byte */
193 ke_AC
= (ke_AC
& 0377) | (data
<< 8);
197 if ((access
== WRITEB
) && GET_SIGN_B (data
)) /* byte write? */
198 data
|= 0177400; /* sext data to 16b */
200 if (GET_SIGN_W (ke_MQ
)) /* sext MQ to AC */
205 case KE_MQ
+ 1: /* MQ odd byte */
206 ke_MQ
= (ke_MQ
& 0377) | (data
<< 8);
207 if (GET_SIGN_W (ke_MQ
)) /* sext MQ to AC */
212 case KE_MUL
: /* multiply */
213 if ((access
== WRITEB
) && GET_SIGN_B (data
)) /* byte write? */
214 data
|= 0177400; /* sext data to 16b */
216 if (GET_SIGN_W (data
)) /* sext operands */
219 if (GET_SIGN_W (t32
))
222 ke_AC
= (t32
>> 16) & DMASK
;
224 if (GET_SIGN_W (ke_AC
)) /* result negative? */
225 ke_SR
= KE_SR_N
| KE_SR_NXV
; /* N = 1, V = C = 0 */
226 else ke_SR
= 0; /* N = 0, V = C = 0 */
230 if (access
== WRITEB
) /* ignore byte writes */
232 ke_SR
= (data
>> 8) & (KE_SR_NXV
|KE_SR_N
|KE_SR_C
);
236 case KE_NOR
: /* normalize */
237 for (ke_SC
= 0; ke_SC
< 31; ke_SC
++) { /* max 31 shifts */
238 if (((ke_AC
== 0140000) && (ke_MQ
== 0)) || /* special case? */
239 (GET_SIGN_W (ke_AC
^ (ke_AC
<< 1)))) /* AC<15> != AC<14>? */
241 ke_AC
= ((ke_AC
<< 1) | (ke_MQ
>> 15)) & DMASK
;
242 ke_MQ
= (ke_MQ
<< 1) & DMASK
;
244 if (GET_SIGN_W (ke_AC
)) /* result negative? */
245 ke_SR
= KE_SR_N
| KE_SR_NXV
; /* N = 1, V = C = 0 */
246 else ke_SR
= 0; /* N = 0, V = C = 0 */
249 case KE_LSH
: /* logical shift */
251 ke_SR
= 0; /* N = V = C = 0 */
252 data
= data
& 077; /* 6b shift count */
254 t32
= (ke_AC
<< 16) | ke_MQ
; /* 32b operand */
255 if (sign
= GET_SIGN_W (ke_AC
)) /* sext operand */
256 t32
= t32
| ~017777777777;
257 if (data
< 32) { /* [1,31] - left */
258 sout
= (t32
>> (32 - data
)) | (-sign
<< data
);
259 t32
= ((uint32
) t32
) << data
; /* do shift (zext) */
260 if (sout
!= (GET_SIGN_L (t32
)? -1: 0)) /* bits lost = sext? */
261 ke_SR
|= KE_SR_NXV
; /* no, V = 1 */
262 if (sout
& 1) /* last bit lost = 1? */
263 ke_SR
|= KE_SR_C
; /* yes, C = 1 */
265 else { /* [32,63] = -32,-1 */
266 if ((t32
>> (63 - data
)) & 1) /* last bit lost = 1? */
267 ke_SR
|= KE_SR_C
; /* yes, C = 1*/
268 t32
= (data
!= 32)? ((uint32
) t32
) >> (64 - data
): 0;
270 ke_AC
= (t32
>> 16) & DMASK
;
273 if (GET_SIGN_W (ke_AC
)) /* result negative? */
274 ke_SR
^= (KE_SR_N
| KE_SR_NXV
); /* N = 1, compl NXV */
277 /* EAE ASH differs from EIS ASH and cannot use the same overflow test */
279 case KE_ASH
: /* arithmetic shift */
281 ke_SR
= 0; /* N = V = C = 0 */
282 data
= data
& 077; /* 6b shift count */
284 t32
= (ke_AC
<< 16) | ke_MQ
; /* 32b operand */
285 if (sign
= GET_SIGN_W (ke_AC
)) /* sext operand */
286 t32
= t32
| ~017777777777;
287 if (data
< 32) { /* [1,31] - left */
288 sout
= (t32
>> (31 - data
)) | (-sign
<< data
);
289 t32
= (t32
& 020000000000) | ((t32
<< data
) & 017777777777);
290 if (sout
!= (GET_SIGN_L (t32
)? -1: 0)) /* bits lost = sext? */
291 ke_SR
|= KE_SR_NXV
; /* no, V = 1 */
292 if (sout
& 1) /* last bit lost = 1? */
293 ke_SR
|= KE_SR_C
; /* yes, C = 1 */
295 else { /* [32,63] = -32,-1 */
296 if ((t32
>> (63 - data
)) & 1) /* last bit lost = 1? */
297 ke_SR
|= KE_SR_C
; /* yes, C = 1 */
298 t32
= (data
!= 32)? /* special case 32 */
299 (((uint32
) t32
) >> (64 - data
)) | (-sign
<< (data
- 32)):
302 ke_AC
= (t32
>> 16) & DMASK
;
305 if (GET_SIGN_W (ke_AC
)) /* result negative? */
306 ke_SR
^= (KE_SR_N
| KE_SR_NXV
); /* N = 1, compl NXV */
309 default: /* all others ignored */
311 } /* end switch PA */
317 /* Update status register based on current AC, MQ */
319 uint32
ke_set_SR (void)
321 ke_SR
&= ~KE_SR_DYN
; /* clr dynamic bits */
322 if (ke_MQ
== 0) /* MQ == 0? */
324 if (ke_AC
== 0) { /* AC == 0? */
326 if (GET_SIGN_W (ke_MQ
) == 0) /* MQ positive? */
328 if (ke_MQ
== 0) /* MQ zero? */
331 if (ke_AC
== 0177777) { /* AC == 177777? */
333 if (GET_SIGN_W (ke_MQ
) == 1) /* MQ negative? */
341 t_stat
ke_reset (DEVICE
*dptr
)