1 // license:BSD-3-Clause
2 // copyright-holders:Patrick Mackinlay
3
4 /*
5 * National Semiconductor 32081 Floating-Point Unit.
6 *
7 * Sources:
8 * - http://bitsavers.org/components/national/_dataBooks/1989_National_Microprocessor_Databook_32000_NSC800.pdf
9 *
10 * TODO:
11 * - testing
12 */
13
14 #include "emu.h"
15 #include "ns32081.h"
16
17 #include "softfloat3/source/include/softfloat.h"
18
19 #define LOG_GENERAL (1U << 0)
20 //#define VERBOSE (LOG_GENERAL)
21
22 #include "logmacro.h"
23
24 DEFINE_DEVICE_TYPE(NS32081, ns32081_device, "ns32081", "National Semiconductor 32081 Floating-Point Unit")
25
26 enum fsr_mask : u32
27 {
28 FSR_TT = 0x00000007, // trap type
29 FSR_UEN = 0x00000008, // underflow trap enable
30 FSR_UF = 0x00000010, // underflow flag
31 FSR_IEN = 0x00000020, // inexact result trap enable
32 FSR_IF = 0x00000040, // inexact result flag
33 FSR_RM = 0x00000180, // rounding mode
34 FSR_SWF = 0x0000fe00, // software field
35 };
36
37 enum rm_mask : u32
38 {
39 RM_N = 0x00000000, // round to nearest value
40 RM_Z = 0x00000080, // round toward zero
41 RM_U = 0x00000100, // round toward positive infinity
42 RM_D = 0x00000180, // round toward negative infinity
43 };
44
45 enum tt_mask : u32
46 {
47 TT_UND = 0x00000001, // underflow
48 TT_OVF = 0x00000002, // overflow
49 TT_DVZ = 0x00000003, // divide by zero
50 TT_ILL = 0x00000004, // illegal instruction
51 TT_INV = 0x00000005, // invalid operation
52 TT_INX = 0x00000006, // inexact result
53 TT_RSV = 0x00000007, // reserved
54 };
55
56 enum state : unsigned
57 {
58 IDLE = 0,
59 OPERATION = 1, // awaiting operation word
60 OPERAND = 2, // awaiting operands
61 STATUS = 4, // status word available
62 RESULT = 5, // result word available
63 };
64
65 enum idbyte : u8
66 {
67 FORMAT_9 = 0x3e,
68 FORMAT_11 = 0xbe,
69 };
70
71 enum operand_length : unsigned
72 {
73 LENGTH_F = 4, // single precision
74 LENGTH_L = 8, // double precision
75 };
76
77 enum size_code : unsigned
78 {
79 SIZE_B = 0,
80 SIZE_W = 1,
81 SIZE_D = 3,
82 };
83
ns32081_device(machine_config const & mconfig,char const * tag,device_t * owner,u32 clock)84 ns32081_device::ns32081_device(machine_config const &mconfig, char const *tag, device_t *owner, u32 clock)
85 : device_t(mconfig, NS32081, tag, owner, clock)
86 , ns32000_slave_interface(mconfig, *this)
87 {
88 }
89
device_start()90 void ns32081_device::device_start()
91 {
92 save_item(NAME(m_fsr));
93 save_item(NAME(m_f));
94
95 save_item(NAME(m_idbyte));
96 save_item(NAME(m_opword));
97 save_item(STRUCT_MEMBER(m_op, expected));
98 save_item(STRUCT_MEMBER(m_op, issued));
99 save_item(STRUCT_MEMBER(m_op, value));
100 save_item(NAME(m_status));
101
102 save_item(NAME(m_state));
103 save_item(NAME(m_tcy));
104
105 m_complete = machine().scheduler().timer_alloc(timer_expired_delegate(FUNC(ns32081_device::complete), this));
106 }
107
device_reset()108 void ns32081_device::device_reset()
109 {
110 m_fsr = 0;
111 std::fill_n(&m_f[0], ARRAY_LENGTH(m_f), 0);
112
113 m_state = IDLE;
114 }
115
state_add(device_state_interface & parent,int & index)116 void ns32081_device::state_add(device_state_interface &parent, int &index)
117 {
118 parent.state_add(index++, "FSR", m_fsr).formatstr("%04X");
119
120 for (unsigned i = 0; i < 8; i++)
121 parent.state_add(index++, util::string_format("F%d", i).c_str(), m_f[i]).formatstr("%08X");
122 }
123
read_st(int * icount)124 u16 ns32081_device::read_st(int *icount)
125 {
126 if (m_state == STATUS)
127 {
128 m_state = (m_op[2].issued == m_op[2].expected) ? IDLE : RESULT;
129
130 if (icount)
131 *icount -= m_tcy;
132
133 LOG("read_st status 0x%04x tcy %d %s (%s)\n", m_status, m_tcy,
134 (m_state == RESULT ? "results pending" : "complete"), machine().describe_context());
135
136 return m_status;
137 }
138
139 logerror("protocol error reading status word (%s)\n", machine().describe_context());
140 return 0;
141 }
142
read_op()143 u16 ns32081_device::read_op()
144 {
145 if (m_state == RESULT && m_op[2].issued < m_op[2].expected)
146 {
147 u16 const data = u16(m_op[2].value >> (m_op[2].issued * 8));
148 LOG("read_op word %d data 0x%04x (%s)\n", m_op[2].issued >> 1, data, machine().describe_context());
149
150 m_op[2].issued += 2;
151
152 if (m_op[2].issued == m_op[2].expected)
153 {
154 LOG("read_op last result word issued\n");
155 m_state = IDLE;
156 }
157
158 return data;
159 }
160
161 logerror("protocol error reading result word (%s)\n", machine().describe_context());
162 return 0;
163 }
164
write_id(u16 data)165 void ns32081_device::write_id(u16 data)
166 {
167 bool const match = (data == FORMAT_9) || (data == FORMAT_11);
168
169 if (match)
170 {
171 LOG("write_id match 0x%04x (%s)\n", data, machine().describe_context());
172 m_state = OPERATION;
173 }
174 else
175 {
176 LOG("write_id ignore 0x%04x (%s)\n", data, machine().describe_context());
177 m_state = IDLE;
178 }
179
180 m_idbyte = u8(data);
181 }
182
write_op(u16 data)183 void ns32081_device::write_op(u16 data)
184 {
185 switch (m_state)
186 {
187 case OPERATION:
188 m_opword = swapendian_int16(data);
189 LOG("write_op opword 0x%04x (%s)\n", m_opword, machine().describe_context());
190
191 // initialize operands
192 for (operand &op : m_op)
193 {
194 op.expected = 0;
195 op.issued = 0;
196 op.value = 0;
197 }
198
199 // decode operands
200 if (m_idbyte == FORMAT_9)
201 {
202 // format 9: 1111 1222 22oo ofii
203 unsigned const f_length = BIT(m_opword, 2) ? LENGTH_F : LENGTH_L;
204 unsigned const size = m_opword & 3;
205
206 switch ((m_opword >> 3) & 7)
207 {
208 case 0: // movif
209 m_op[0].expected = size + 1;
210 m_op[2].expected = f_length;
211 break;
212 case 1: // lfsr
213 m_op[0].expected = 4;
214 break;
215 case 2: // movlf
216 m_op[0].expected = LENGTH_L;
217 m_op[2].expected = f_length;
218 break;
219 case 3: // movfl
220 m_op[0].expected = LENGTH_F;
221 m_op[2].expected = f_length;
222 break;
223 case 4: // roundfi
224 case 5: // truncfi
225 case 7: // floorfi
226 m_op[0].expected = f_length;
227 m_op[2].expected = size + 1;
228 break;
229 case 6: // sfsr
230 m_op[2].expected = 4;
231 break;
232 }
233 }
234 else if (m_idbyte == FORMAT_11)
235 {
236 // format 11: 1111 1222 22oo oo0f
237 unsigned const opcode = (m_opword >> 2) & 15;
238 unsigned const f_length = BIT(m_opword, 0) ? LENGTH_F : LENGTH_L;
239
240 m_op[0].expected = f_length;
241
242 // even opcodes have two input operands
243 if (!BIT(opcode, 0))
244 m_op[1].expected = f_length;
245
246 // all operations except CMPf issue a result
247 if (opcode != 2)
248 m_op[2].expected = f_length;
249 }
250
251 // operand 1 in register
252 if (m_op[0].expected && !(m_opword & 0xc000))
253 {
254 // exclude integer operands
255 if (m_idbyte == FORMAT_11 || ((m_opword >> 3) & 7) > 1)
256 {
257 unsigned const reg = (m_opword >> 11) & 7;
258 LOG("write_op read f%d\n", reg);
259
260 m_op[0].value = m_f[reg ^ 0];
261 if (m_op[0].expected == 8)
262 m_op[0].value |= u64(m_f[reg ^ 1]) << 32;
263
264 m_op[0].issued = m_op[0].expected;
265 }
266 }
267
268 // operand 2 in register
269 if (m_op[1].expected && !(m_opword & 0x0600))
270 {
271 unsigned const reg = (m_opword >> 6) & 7;
272 LOG("write_op read f%d\n", reg);
273
274 m_op[1].value = m_f[reg ^ 0];
275 if (m_op[1].expected == 8)
276 m_op[1].value |= u64(m_f[reg ^ 1]) << 32;
277
278 m_op[1].issued = m_op[1].expected;
279 }
280
281 m_state = OPERAND;
282 break;
283
284 case OPERAND:
285 // check awaiting operand word
286 if (m_op[0].issued < m_op[0].expected || m_op[1].issued < m_op[1].expected)
287 {
288 unsigned const n = (m_op[0].issued < m_op[0].expected) ? 0 : 1;
289 operand &op = m_op[n];
290
291 LOG("write_op op%d data 0x%04x (%s)\n", n, data, machine().describe_context());
292
293 // insert word into operand value
294 op.value |= u64(data) << (op.issued * 8);
295 op.issued += 2;
296 }
297 else
298 logerror("protocol error unexpected operand word 0x%04x (%s)\n", data, machine().describe_context());
299 break;
300 }
301
302 // start execution when all operands are available
303 if (m_state == OPERAND && m_op[0].issued >= m_op[0].expected && m_op[1].issued >= m_op[1].expected)
304 execute();
305 }
306
execute()307 void ns32081_device::execute()
308 {
309 softfloat_exceptionFlags = 0;
310 m_fsr &= ~FSR_TT;
311
312 m_status = 0;
313 m_tcy = 0;
314
315 switch (m_idbyte)
316 {
317 case FORMAT_9:
318 // format 9: 1111 1222 22oo ofii
319 {
320 bool const single = BIT(m_opword, 2);
321 unsigned const f_length = single ? LENGTH_F : LENGTH_L;
322 unsigned const size = m_opword & 3;
323
324 switch ((m_opword >> 3) & 7)
325 {
326 case 0:
327 // MOVif src,dest
328 // gen,gen
329 // read.i,write.f
330 {
331 s32 const src =
332 (size == SIZE_D) ? s32(m_op[0].value) :
333 (size == SIZE_W) ? s16(m_op[0].value) :
334 s8(m_op[0].value);
335
336 if (single)
337 m_op[2].value = i32_to_f32(src).v;
338 else
339 m_op[2].value = i32_to_f64(src).v;
340 m_op[2].expected = f_length;
341 m_tcy = 53;
342 }
343 break;
344 case 1:
345 // LFSR src
346 // gen
347 // read.D
348 m_fsr = u16(m_op[0].value);
349
350 switch (m_fsr & FSR_RM)
351 {
352 case RM_N: softfloat_roundingMode = softfloat_round_near_even; break;
353 case RM_Z: softfloat_roundingMode = softfloat_round_minMag; break;
354 case RM_U: softfloat_roundingMode = softfloat_round_max; break;
355 case RM_D: softfloat_roundingMode = softfloat_round_min; break;
356 }
357 m_tcy = 18;
358 break;
359 case 2:
360 // MOVLF src,dest
361 // gen,gen
362 // read.L,write.F
363 m_op[2].value = f64_to_f32(float64_t{ m_op[0].value }).v;
364 m_op[2].expected = f_length;
365 m_tcy = (m_opword & 0xc000) ? 23 : 27;
366 break;
367 case 3:
368 // MOVFL src,dest
369 // gen,gen
370 // read.F,write.L
371 m_op[2].value = f32_to_f64(float32_t{ u32(m_op[0].value) }).v;
372 m_op[2].expected = f_length;
373 m_tcy = (m_opword & 0xc000) ? 22 : 26;
374 break;
375 case 4:
376 // ROUNDfi src,dest
377 // gen,gen
378 // read.f,write.i
379 if (single)
380 m_op[2].value = f32_to_i64(float32_t{ u32(m_op[0].value) }, softfloat_round_near_even, true);
381 else
382 m_op[2].value = f64_to_i64(float64_t{ m_op[0].value }, softfloat_round_near_even, true);
383
384 if ((size == SIZE_D && s64(m_op[2].value) != s32(m_op[2].value))
385 || (size == SIZE_W && s64(m_op[2].value) != s16(m_op[2].value))
386 || (size == SIZE_B && s64(m_op[2].value) != s8(m_op[2].value)))
387 softfloat_exceptionFlags |= softfloat_flag_overflow;
388
389 m_op[2].expected = size + 1;
390 m_tcy = (m_opword & 0xc000) ? 53 : 66;
391 break;
392 case 5:
393 // TRUNCfi src,dest
394 // gen,gen
395 // read.f,write.i
396 if (single)
397 m_op[2].value = f32_to_i64(float32_t{ u32(m_op[0].value) }, softfloat_round_minMag, true);
398 else
399 m_op[2].value = f64_to_i64(float64_t{ m_op[0].value }, softfloat_round_minMag, true);
400
401 if ((size == SIZE_D && s64(m_op[2].value) != s32(m_op[2].value))
402 || (size == SIZE_W && s64(m_op[2].value) != s16(m_op[2].value))
403 || (size == SIZE_B && s64(m_op[2].value) != s8(m_op[2].value)))
404 softfloat_exceptionFlags |= softfloat_flag_overflow;
405
406 m_op[2].expected = size + 1;
407 m_tcy = (m_opword & 0xc000) ? 53 : 66;
408 break;
409 case 6:
410 // SFSR dest
411 // gen
412 // write.D
413 m_op[2].value = m_fsr;
414 m_op[2].expected = 4;
415 m_tcy = 13;
416 break;
417 case 7:
418 // FLOORfi src,dest
419 // gen,gen
420 // read.f,write.i
421 if (single)
422 m_op[2].value = f32_to_i64(float32_t{ u32(m_op[0].value) }, softfloat_round_min, true);
423 else
424 m_op[2].value = f64_to_i64(float64_t{ m_op[0].value }, softfloat_round_min, true);
425
426 if ((size == SIZE_D && s64(m_op[2].value) != s32(m_op[2].value))
427 || (size == SIZE_W && s64(m_op[2].value) != s16(m_op[2].value))
428 || (size == SIZE_B && s64(m_op[2].value) != s8(m_op[2].value)))
429 softfloat_exceptionFlags |= softfloat_flag_overflow;
430
431 m_op[2].expected = size + 1;
432 m_tcy = (m_opword & 0xc000) ? 53 : 66;
433 break;
434 }
435 }
436 break;
437
438 case FORMAT_11:
439 // format 11: 1111122222oooo0f
440 {
441 bool const single = BIT(m_opword, 0);
442 unsigned const f_length = single ? LENGTH_F : LENGTH_L;
443
444 switch ((m_opword >> 2) & 15)
445 {
446 case 0x0:
447 // ADDf src,dest
448 // gen,gen
449 // read.f,rmw.f
450 if (single)
451 m_op[2].value = f32_add(float32_t{ u32(m_op[1].value) }, float32_t{ u32(m_op[0].value) }).v;
452 else
453 m_op[2].value = f64_add(float64_t{ m_op[1].value }, float64_t{ m_op[0].value }).v;
454 m_op[2].expected = f_length;
455 m_tcy = (m_opword & 0xc600) ? 70 : 74;
456 break;
457 case 0x1:
458 // MOVf src,dest
459 // gen,gen
460 // read.f,write.f
461 m_op[2].value = m_op[0].value;
462 m_op[2].expected = f_length;
463 m_tcy = (m_opword & 0xc000) ? 23 : 27;
464 break;
465 case 0x2:
466 // CMPf src1,src2
467 // gen,gen
468 // read.f,read.f
469 if (m_op[0].value == m_op[1].value)
470 m_status |= SLAVE_Z;
471 if ((single && f32_le(float32_t{ u32(m_op[1].value) }, float32_t{ u32(m_op[0].value) }))
472 || (!single && f64_le(float64_t{ m_op[1].value }, float64_t{ m_op[0].value })))
473 m_status |= SLAVE_N;
474 m_tcy = (m_opword & 0xc600) ? 45 : 49;
475 break;
476 case 0x3:
477 // Trap(SLAVE)
478 m_fsr |= TT_ILL;
479 m_status = SLAVE_Q;
480 break;
481 case 0x4:
482 // SUBf src,dest
483 // gen,gen
484 // read.f,rmw.f
485 if (single)
486 m_op[2].value = f32_sub(float32_t{ u32(m_op[1].value) }, float32_t{ u32(m_op[0].value) }).v;
487 else
488 m_op[2].value = f64_sub(float64_t{ m_op[1].value }, float64_t{ m_op[0].value }).v;
489 m_op[2].expected = f_length;
490 m_tcy = (m_opword & 0xc600) ? 70 : 74;
491 break;
492 case 0x5:
493 // NEGf src,dest
494 // gen,gen
495 // read.f,write.f
496 if (single)
497 m_op[2].value = f32_mul(float32_t{ u32(m_op[0].value) }, i32_to_f32(-1)).v;
498 else
499 m_op[2].value = f64_mul(float64_t{ m_op[0].value }, i32_to_f64(-1)).v;
500 m_op[2].expected = f_length;
501 m_tcy = (m_opword & 0xc000) ? 20 : 24;
502 break;
503 case 0x8:
504 // DIVf src,dest
505 // gen,gen
506 // read.f,rmw.f
507 if (single)
508 m_op[2].value = f32_div(float32_t{ u32(m_op[1].value) }, float32_t{ u32(m_op[0].value) }).v;
509 else
510 m_op[2].value = f64_div(float64_t{ m_op[1].value }, float64_t{ m_op[0].value }).v;
511 m_op[2].expected = f_length;
512 m_tcy = ((m_opword & 0xc600) ? 55 : 59) + (single ? 30 : 60);
513 break;
514 case 0x9:
515 // Trap(SLAVE)
516 m_fsr |= TT_ILL;
517 m_status = SLAVE_Q;
518 break;
519 case 0xc:
520 // MULf src,dest
521 // gen,gen
522 // read.f,rmw.f
523 if (single)
524 m_op[2].value = f32_mul(float32_t{ u32(m_op[1].value) }, float32_t{ u32(m_op[0].value) }).v;
525 else
526 m_op[2].value = f64_mul(float64_t{ m_op[1].value }, float64_t{ m_op[0].value }).v;
527 m_op[2].expected = f_length;
528 m_tcy = ((m_opword & 0xc600) ? 30 : 34) + (single ? 14 : 28);
529 break;
530 case 0xd:
531 // ABSf src,dest
532 // gen,gen
533 // read.f,write.f
534 if (single)
535 if (f32_lt(float32_t{ u32(m_op[0].value) }, float32_t{ 0 }))
536 m_op[2].value = f32_mul(float32_t{ u32(m_op[0].value) }, i32_to_f32(-1)).v;
537 else
538 m_op[2].value = float32_t{ u32(m_op[0].value) }.v;
539 else
540 if (f64_lt(float64_t{ m_op[0].value }, float64_t{ 0 }))
541 m_op[2].value = f64_mul(float64_t{ m_op[0].value }, i32_to_f64(-1)).v;
542 else
543 m_op[2].value = float64_t{ m_op[0].value }.v;
544 m_op[2].expected = f_length;
545 m_tcy = (m_opword & 0xc000) ? 20 : 24;
546 break;
547 }
548 }
549 break;
550 }
551
552 // check for exceptions
553 if (softfloat_exceptionFlags & softfloat_flag_underflow)
554 {
555 m_fsr |= FSR_UF | TT_UND;
556
557 if (m_fsr & FSR_UEN)
558 m_status |= SLAVE_Q;
559 else
560 m_op[2].value = 0;
561 }
562 else if (softfloat_exceptionFlags & softfloat_flag_overflow)
563 {
564 m_fsr |= TT_OVF;
565 m_status |= SLAVE_Q;
566 }
567 else if (softfloat_exceptionFlags & softfloat_flag_infinite)
568 {
569 m_fsr |= TT_DVZ;
570 m_status |= SLAVE_Q;
571 }
572 else if (softfloat_exceptionFlags & softfloat_flag_invalid)
573 m_fsr |= TT_INV;
574 else if (softfloat_exceptionFlags & softfloat_flag_inexact)
575 {
576 m_fsr |= FSR_IF | TT_INX;
577
578 if (m_fsr & FSR_IEN)
579 m_status |= SLAVE_Q;
580 }
581
582 // exceptions suppress result issue
583 if (m_status & SLAVE_Q)
584 m_op[2].expected = 0;
585
586 if (VERBOSE & LOG_GENERAL)
587 {
588 static char const *format9[] = { "movif", "lfsr", "movlf", "movfl", "roundfi", "truncfi", "sfsr", "floorfi" };
589 static char const *format11[] =
590 {
591 "addf", "movf", "cmpf", nullptr, "subf", "negf", nullptr, nullptr,
592 "divf", nullptr, nullptr, nullptr, "mulf", "absf", nullptr, nullptr
593 };
594
595 if (m_status & SLAVE_Q)
596 LOG("execute %s 0x%x,0x%x exception\n",
597 (m_idbyte == FORMAT_9)
598 ? format9[(m_opword >> 3) & 7]
599 : format11[(m_opword >> 2) & 15],
600 m_op[0].value, m_op[1].value);
601 else
602 LOG("execute %s 0x%x,0x%x result 0x%x\n",
603 (m_idbyte == FORMAT_9)
604 ? format9[(m_opword >> 3) & 7]
605 : format11[(m_opword >> 2) & 15],
606 m_op[0].value, m_op[1].value, m_op[2].value);
607 }
608
609 // write-back floating point register results
610 if (m_op[2].expected && !(m_opword & 0x0600))
611 {
612 // exclude integer results (roundfi, truncfi, sfsr, floorfi)
613 if (m_idbyte == FORMAT_11 || ((m_opword >> 3) & 7) < 4)
614 {
615 unsigned const reg = (m_opword >> 6) & 7;
616
617 LOG("execute write-back f%d\n", reg);
618
619 m_f[reg ^ 0] = u32(m_op[2].value >> 0);
620 if (m_op[2].expected == 8)
621 m_f[reg ^ 1] = u32(m_op[2].value >> 32);
622
623 m_op[2].issued = m_op[2].expected;
624 }
625 }
626
627 m_state = STATUS;
628
629 if (m_out_scb)
630 m_complete->adjust(attotime::from_ticks(m_tcy, clock()));
631 }
632
complete(void * buf,s32 param)633 void ns32081_device::complete(void *buf, s32 param)
634 {
635 m_out_scb(0);
636 m_out_scb(1);
637 }
638