1 /************************************************************************
2 
3 	ST register functions
4 
5 ************************************************************************/
6 
7 /*
8 	remember that the OP ST bit is maintained in lastparity
9 */
10 
11 /*
12 	setstat sets the ST_OP bit according to lastparity
13 
14 	It must be called before reading the ST register.
15 */
16 
setstat(void)17 static void setstat(void)
18 {
19 	int i;
20 	UINT8 a;
21 
22 	I.STATUS &= ~ ST_OP;
23 
24 	/* We set the parity bit. */
25 	a = lastparity;
26 
27 	for (i=0; i<8; i++)     /* 8 bits to test */
28 	{
29 		if (a & 1)  /* If current bit is set */
30 			I.STATUS ^= ST_OP;  /* we toggle the ST_OP bit */
31 
32 		a >>= 1;    /* Next bit. */
33 	}
34 }
35 
36 /*
37 	getstat sets emulator's lastparity variable according to 9900's STATUS bits.
38 	It must be called on interrupt return, or when, for some reason,
39 	the emulated program sets the STATUS register directly.
40 */
getstat(void)41 static void getstat(void)
42 {
43 #if TMS99XX_MODEL <= TMS9985_ID
44 	I.STATUS &= ST_MASK;  /* unused bits are forced to 0 */
45 #endif
46 
47 	if (I.STATUS & ST_OP)
48 		lastparity = 1;
49 	else
50 		lastparity = 0;
51 }
52 
53 /*
54 	A few words about the following functions.
55 
56 	A big portability issue is the behavior of the ">>" instruction with the sign bit, which has
57 	not been normalised.  Every compiler does whatever it thinks smartest.
58 	My code assumed that when shifting right signed numbers, the operand is left-filled with a
59 	copy of sign bit, and that when shifting unsigned variables, it is left-filled with 0s.
60 	This is probably the most logical behaviour, and it is the behavior of CW PRO3 - most time
61 	(the exception is that ">>=" instructions always copy the sign bit (!)).  But some compilers
62 	are bound to disagree.
63 
64 	So, I had to create special functions with predefined tables included, so that this code work
65 	on every compiler.  BUT this is a real slow-down.
66 	So, you might have to include a few lines in assembly to make this work better.
67 	Sorry about this, this problem is really unpleasant and absurd, but it is not my fault.
68 */
69 
70 
71 static const UINT16 right_shift_mask_table[17] =
72 {
73 	0xFFFF,
74 	0x7FFF,
75 	0x3FFF,
76 	0x1FFF,
77 	0x0FFF,
78 	0x07FF,
79 	0x03FF,
80 	0x01FF,
81 	0x00FF,
82 	0x007F,
83 	0x003F,
84 	0x001F,
85 	0x000F,
86 	0x0007,
87 	0x0003,
88 	0x0001,
89 	0x0000
90 };
91 
92 static const UINT16 inverted_right_shift_mask_table[17] =
93 {
94 	0x0000,
95 	0x8000,
96 	0xC000,
97 	0xE000,
98 	0xF000,
99 	0xF800,
100 	0xFC00,
101 	0xFE00,
102 	0xFF00,
103 	0xFF80,
104 	0xFFC0,
105 	0xFFE0,
106 	0xFFF0,
107 	0xFFF8,
108 	0xFFFC,
109 	0xFFFE,
110 	0xFFFF
111 };
112 
logical_right_shift(UINT16 val,int c)113 static INLINE UINT16 logical_right_shift(UINT16 val, int c)
114 {
115 	return((val>>c) & right_shift_mask_table[c]);
116 }
117 
arithmetic_right_shift(INT16 val,int c)118 static INLINE INT16 arithmetic_right_shift(INT16 val, int c)
119 {
120 	if (val < 0)
121 		return((val>>c) | inverted_right_shift_mask_table[c]);
122 	else
123 		return((val>>c) & right_shift_mask_table[c]);
124 }
125 
126 
127 
128 
129 
130 /*
131 	Set lae
132 */
setst_lae(INT16 val)133 static INLINE void setst_lae(INT16 val)
134 {
135 	I.STATUS &= ~ (ST_LGT | ST_AGT | ST_EQ);
136 
137 	if (val > 0)
138 		I.STATUS |= (ST_LGT | ST_AGT);
139 	else if (val < 0)
140 		I.STATUS |= ST_LGT;
141 	else
142 		I.STATUS |= ST_EQ;
143 }
144 
145 
146 /*
147 	Set laep (BYTE)
148 */
setst_byte_laep(INT8 val)149 static INLINE void setst_byte_laep(INT8 val)
150 {
151 	I.STATUS &= ~ (ST_LGT | ST_AGT | ST_EQ);
152 
153 	if (val > 0)
154 		I.STATUS |= (ST_LGT | ST_AGT);
155 	else if (val < 0)
156 		I.STATUS |= ST_LGT;
157 	else
158 		I.STATUS |= ST_EQ;
159 
160 	lastparity = val;
161 }
162 
163 /*
164 	For COC, CZC, and TB
165 */
setst_e(UINT16 val,UINT16 to)166 static INLINE void setst_e(UINT16 val, UINT16 to)
167 {
168 	if (val == to)
169 		I.STATUS |= ST_EQ;
170 	else
171 		I.STATUS &= ~ ST_EQ;
172 }
173 
174 /*
175 	For CI, C, CB
176 */
setst_c_lae(UINT16 to,UINT16 val)177 static INLINE void setst_c_lae(UINT16 to, UINT16 val)
178 {
179 	I.STATUS &= ~ (ST_LGT | ST_AGT | ST_EQ);
180 
181 	if (val == to)
182 		I.STATUS |= ST_EQ;
183 	else
184 	{
185 		if ( ((INT16) val) > ((INT16) to) )
186 			I.STATUS |= ST_AGT;
187 		if ( ((UINT16) val) > ((UINT16) to) )
188 		I.STATUS |= ST_LGT;
189 	}
190 }
191 
192 #define wadd(addr,expr) { int lval = setst_add_laeco(readword(addr), (expr)); writeword((addr),lval); }
193 #define wsub(addr,expr) { int lval = setst_sub_laeco(readword(addr), (expr)); writeword((addr),lval); }
194 
195 #if defined(__POWERPC__) && !defined(__GNUC__) && !defined(_XBOX)
196 
197 // setst_add_32_laeco :
198 // - computes a+b
199 // - sets L, A, E, Carry and Overflow in st
200 //
201 // a -> r3, b -> r4, st -> r5
202 // preserve r6-r12
203 
setst_add_32_laeco(register INT32 a,register INT32 b,register INT16 st)204 static INT32 asm setst_add_32_laeco(register INT32 a, register INT32 b, register INT16 st)
205 {
206 #if (TMS99XX_MODEL == TMS9940_ID)
207   mr r6,a           // save operand
208 #endif
209   addco. r3, b, a   // add, set CR0, and set CA and OV
210   clrlwi st, st, 21 // clear L, A, E, C, O flags (-> keep bits 21-31)
211   mcrxr cr1         // move XER (-> CA and CO bits) to CR1
212 
213   beq- null_result  // if EQ is set, jump to null_result.  No jump is more likely than a jump.
214   bgt+ positive_result  // if GT is set, jump to positive_result.  A jump is more likely than no jump.
215   ori st, st, ST_LGT    // if result < 0, set ST_LGT
216   b next    // now set carry & overflow as needed
217 
218 null_result:
219   ori st, st, ST_EQ // if result == 0, set ST_EQ
220   b next
221 
222 positive_result:
223   ori st, st, ST_LGT | ST_AGT   // if result > 0, set ST_LGT and ST_AGT
224 
225 next:
226 #if (TMS99XX_MODEL == TMS9940_ID)
227   andi. st, st, (~ ST_DC) & 0xFFFF
228 
229   or r7,r6,b
230   and r6,r6,b
231   andc r7,r7,r3
232   or r6,r7,r6
233   andis. r6,r6,0x0800
234   beq+ nodecimalcarry
235   ori st, st, ST_DC
236 nodecimalcarry:
237 #endif
238 
239   bf+ cr1*4+2, nocarry  // if CA is not set, jump to nocarry.  A jump is more likely than no jump.
240   ori st, st, ST_C  // else set ST_C
241 nocarry:
242   bflr+ cr1*4+1     // if OV is not set, return.  A jump is more likely than no jump.
243   ori st, st, ST_OV // else set ST_OV
244   blr   // return
245 }
246 
247 // setst_sub_32_laeco :
248 // - computes a-b
249 // - sets L, A, E, Carry and Overflow in ST
250 //
251 // a -> r3, b -> r4, st -> r5
252 // preserve r6-r12
253 
setst_sub_32_laeco(register INT32 a,register INT32 b,register INT16 st)254 static INT32 asm setst_sub_32_laeco(register INT32 a, register INT32 b, register INT16 st)
255 {
256 #if (TMS99XX_MODEL == TMS9940_ID)
257   mr r6,a           // save operand
258 #endif
259   subco. r3, a, b   // sub, set CR0, and set CA and OV
260   clrlwi st, st, 21 // clear L, A, E, C, O flags (-> keep bits 21-31)
261   mcrxr cr1         // move XER (-> CA and CO bits) to CR1
262 
263   beq- null_result  // if EQ is set, jump to null_result.  No jump is more likely than a jump.
264   bgt+ positive_result  // if GT is set, jump to positive_result.  A jump is more likely than no jump.
265   ori st, st, ST_LGT    // if result < 0, set ST_LGT
266   b next    // now set carry & overflow as needed
267 
268 null_result:
269   ori st, st, ST_EQ // if result == 0, set ST_EQ
270   b next
271 
272 positive_result:
273   ori st, st, ST_LGT | ST_AGT   // if result > 0, set ST_LGT and ST_AGT
274 
275 next:
276 #if (TMS99XX_MODEL == TMS9940_ID)
277   andi. st, st, (~ ST_DC) & 0xFFFF
278 
279   orc r7,r6,b
280   andc r6,r6,b
281   andc r7,r7,r3
282   or r6,r7,r6
283   andis. r6,r6,0x0800
284   beq- nodecimalcarry
285   ori st, st, ST_DC
286 nodecimalcarry:
287 #endif
288 
289   bf- cr1*4+2, nocarry  // if CA is not set, jump to nocarry.  No jump is more likely than a jump.
290   ori st, st, ST_C  // else set ST_C
291 nocarry:
292   bflr+ cr1*4+1     // if OV is not set, return.  A jump is more likely than no jump.
293   ori st, st, ST_OV // else set ST_OV
294   blr   // return
295 }
296 
297 //
298 // Set laeco for add
299 //
setst_add_laeco(register INT16 a,register INT16 b)300 static INT16 asm setst_add_laeco(register INT16 a, register INT16 b)
301 { // a -> r3, b -> r4
302 //  lwz r6, I(RTOC)   // load pointer to I
303   _asm_get_global(r6,I)
304 
305   slwi a, a, 16     // shift a
306   slwi b, b, 16     // shift b
307 
308   lhz r5, 4(r6)     // load ST
309 
310   mflr r12  // save LR (we KNOW setst_add_32_laeco will not alter this register)
311 
312   bl setst_add_32_laeco // perform addition and set flags
313 
314   mtlr r12  // restore LR
315   srwi r3, r3, 16   // shift back result
316   sth r5, 4(r6)     // save new ST
317   blr       // and return
318 }
319 
320 //
321 //  Set laeco for subtract
322 //
setst_sub_laeco(register INT16 a,register INT16 b)323 static INT16 asm setst_sub_laeco(register INT16 a, register INT16 b)
324 {
325 //  lwz r6, I(RTOC)
326   _asm_get_global(r6,I)
327 
328   slwi a, a, 16
329   slwi b, b, 16
330 
331   lhz r5, 4(r6)
332 
333   mflr r12
334 
335   bl setst_sub_32_laeco // perform substraction and set flags
336 
337   mtlr r12
338   srwi r3, r3, 16
339   sth r5, 4(r6)
340   blr
341 }
342 
343 //
344 // Set laecop for add (BYTE)
345 //
setst_addbyte_laecop(register INT8 a,register INT8 b)346 static INT8 asm setst_addbyte_laecop(register INT8 a, register INT8 b)
347 { // a -> r3, b -> r4
348 //  lwz r6, I(RTOC)
349   _asm_get_global(r6,I)
350 
351   slwi a, a, 24     // shift a
352   slwi b, b, 24     // shift b
353 
354   lhz r5, 4(r6)     // load ST
355 
356   mflr r12  // save LR (we KNOW setst_add_32_laeco will not alter this register)
357 
358   bl setst_add_32_laeco // perform addition and set flags
359 
360   srwi r3, r3, 24   // shift back result
361   mtlr r12  // restore LR
362   sth r5, 4(r6)     // save new ST
363 //  stb r3, lastparity(RTOC)  // copy result to lastparity
364   _asm_set_global_b(r3,lastparity)  // copy result to lastparity
365   blr       // and return
366 }
367 
368 //
369 // Set laecop for subtract (BYTE)
370 //
setst_subbyte_laecop(register INT8 a,register INT8 b)371 static INT8 asm setst_subbyte_laecop(register INT8 a, register INT8 b)
372 { // a -> r3, b -> r4
373 //  lwz r6, I(RTOC)
374   _asm_get_global(r6,I)
375 
376   slwi a, a, 24
377   slwi b, b, 24
378 
379   lhz r5, 4(r6)
380 
381   mflr r12
382 
383   bl setst_sub_32_laeco // perform substraction and set flags
384 
385   srwi r3, r3, 24
386   mtlr r12
387   sth r5, 4(r6)
388 //  stb r3, lastparity(RTOC)
389   _asm_set_global_b(r3,lastparity)  // copy result to lastparity
390   blr
391 }
392 
393 #else
394 
395 /* Could do with some equivalent functions for non power PC's */
396 
397 /*
398 	Set laeco for add
399 */
setst_add_laeco(int a,int b)400 static INLINE INT16 setst_add_laeco(int a, int b)
401 {
402 	UINT32 res;
403 	INT16 res2;
404 
405 	I.STATUS &= ~ (ST_LGT | ST_AGT | ST_EQ | ST_C | ST_OV);
406 
407 	res = (a & 0xffff) + (b & 0xffff);
408 
409 	if (res & 0x10000)
410 		I.STATUS |= ST_C;
411 
412 	if ((res ^ b) & (res ^ a) & 0x8000)
413 		I.STATUS |= ST_OV;
414 
415 #if (TMS99XX_MODEL == TMS9940_ID)
416 	if (((a & b) | ((a | b) & ~ res)) & 0x0800)
417 		I.STATUS |= ST_DC;
418 #endif
419 
420 	res2 = (INT16) res;
421 
422 	if (res2 > 0)
423 		I.STATUS |= ST_LGT | ST_AGT;
424 	else if (res2 < 0)
425 		I.STATUS |= ST_LGT;
426 	else
427 		I.STATUS |= ST_EQ;
428 
429 	return res2;
430 }
431 
432 
433 /*
434 	Set laeco for subtract
435 */
setst_sub_laeco(int a,int b)436 static INLINE INT16 setst_sub_laeco(int a, int b)
437 {
438 	UINT32 res;
439 	INT16 res2;
440 
441 	I.STATUS &= ~ (ST_LGT | ST_AGT | ST_EQ | ST_C | ST_OV);
442 
443 	res = (a & 0xffff) - (b & 0xffff);
444 
445 	if (! (res & 0x10000))
446 		I.STATUS |= ST_C;
447 
448 	if ((a ^ b) & (a ^ res) & 0x8000)
449 		I.STATUS |= ST_OV;
450 
451 #if (TMS99XX_MODEL == TMS9940_ID)
452 	if (((a & ~ b) | ((a | ~ b) & ~ res)) & 0x0800)
453 		I.STATUS |= ST_DC;
454 #endif
455 
456 	res2 = (INT16) res;
457 
458 	if (res2 > 0)
459 		I.STATUS |= ST_LGT | ST_AGT;
460 	else if (res2 < 0)
461 		I.STATUS |= ST_LGT;
462 	else
463 		I.STATUS |= ST_EQ;
464 
465 	return res2;
466 }
467 
468 
469 /*
470 	Set laecop for add (BYTE)
471 */
setst_addbyte_laecop(int a,int b)472 static INLINE INT8 setst_addbyte_laecop(int a, int b)
473 {
474 	unsigned int res;
475 	INT8 res2;
476 
477 	I.STATUS &= ~ (ST_LGT | ST_AGT | ST_EQ | ST_C | ST_OV | ST_OP);
478 
479 	res = (a & 0xff) + (b & 0xff);
480 
481 	if (res & 0x100)
482 		I.STATUS |= ST_C;
483 
484 	if ((res ^ b) & (res ^ a) & 0x80)
485 		I.STATUS |= ST_OV;
486 
487 #if (TMS99XX_MODEL == TMS9940_ID)
488 	if (((a & b) | ((a | b) & ~ res)) & 0x08)
489 		I.STATUS |= ST_DC;
490 #endif
491 
492 	res2 = (INT8) res;
493 
494 	if (res2 > 0)
495 		I.STATUS |= ST_LGT | ST_AGT;
496 	else if (res2 < 0)
497 		I.STATUS |= ST_LGT;
498 	else
499 		I.STATUS |= ST_EQ;
500 
501 	lastparity = res2;
502 
503 	return res2;
504 }
505 
506 
507 /*
508 	Set laecop for subtract (BYTE)
509 */
setst_subbyte_laecop(int a,int b)510 static INLINE INT8 setst_subbyte_laecop(int a, int b)
511 {
512 	unsigned int res;
513 	INT8 res2;
514 
515 	I.STATUS &= ~ (ST_LGT | ST_AGT | ST_EQ | ST_C | ST_OV | ST_OP);
516 
517 	res = (a & 0xff) - (b & 0xff);
518 
519 	if (! (res & 0x100))
520 		I.STATUS |= ST_C;
521 
522 	if ((a ^ b) & (a ^ res) & 0x80)
523 		I.STATUS |= ST_OV;
524 
525 #if (TMS99XX_MODEL == TMS9940_ID)
526 	if (((a & ~ b) | ((a | ~ b) & ~ res)) & 0x08)
527 		I.STATUS |= ST_DC;
528 #endif
529 
530 	res2 = (INT8) res;
531 
532 	if (res2 > 0)
533 		I.STATUS |= ST_LGT | ST_AGT;
534 	else if (res2 < 0)
535 		I.STATUS |= ST_LGT;
536 	else
537 		I.STATUS |= ST_EQ;
538 
539 	lastparity = res2;
540 
541 	return res2;
542 }
543 
544 #endif
545 
546 
547 
548 /*
549 	For NEG
550 */
setst_laeo(INT16 val)551 static INLINE void setst_laeo(INT16 val)
552 {
553 	I.STATUS &= ~ (ST_LGT | ST_AGT | ST_EQ | ST_OV);
554 
555 	if (val > 0)
556 		I.STATUS |= ST_LGT | ST_AGT;
557 	else if (val < 0)
558 	{
559 	I.STATUS |= ST_LGT;
560 	if (((UINT16) val) == 0x8000)
561 		I.STATUS |= ST_OV;
562 	}
563 	else
564 		I.STATUS |= ST_EQ;
565 }
566 
567 
568 
569 /*
570 	Meat of SRA
571 */
setst_sra_laec(INT16 a,UINT16 c)572 static INLINE UINT16 setst_sra_laec(INT16 a, UINT16 c)
573 {
574 	I.STATUS &= ~ (ST_LGT | ST_AGT | ST_EQ | ST_C);
575 
576 	if (c != 0)
577 	{
578 		a = arithmetic_right_shift(a, c-1);
579 		if (a & 1)  // The carry bit equals the last bit that is shifted out
580 			I.STATUS |= ST_C;
581 		a = arithmetic_right_shift(a, 1);
582 	}
583 
584 	if (a > 0)
585 		I.STATUS |= ST_LGT | ST_AGT;
586 	else if (a < 0)
587 		I.STATUS |= ST_LGT;
588 	else
589 		I.STATUS |= ST_EQ;
590 
591 	return a;
592 }
593 
594 
595 /*
596 	Meat of SRL.  Same algorithm as SRA, except that we fills in with 0s.
597 */
setst_srl_laec(UINT16 a,UINT16 c)598 static INLINE UINT16 setst_srl_laec(UINT16 a,UINT16 c)
599 {
600 	I.STATUS &= ~ (ST_LGT | ST_AGT | ST_EQ | ST_C);
601 
602 	if (c != 0)
603 	{
604 		a = logical_right_shift(a, c-1);
605 		if (a & 1)
606 			I.STATUS |= ST_C;
607 		a = logical_right_shift(a, 1);
608 	}
609 
610 	if (((INT16) a) > 0)
611 		I.STATUS |= ST_LGT | ST_AGT;
612 	else if (((INT16) a) < 0)
613 		I.STATUS |= ST_LGT;
614 	else
615 		I.STATUS |= ST_EQ;
616 
617 	return a;
618 }
619 
620 
621 //
622 // Meat of SRC
623 //
setst_src_laec(UINT16 a,UINT16 c)624 static INLINE UINT16 setst_src_laec(UINT16 a,UINT16 c)
625 {
626 	I.STATUS &= ~ (ST_LGT | ST_AGT | ST_EQ | ST_C);
627 
628 	if (c != 0)
629 	{
630 		a = logical_right_shift(a, c) | (a << (16-c));
631 		if (a & 0x8000) // The carry bit equals the last bit that is shifted out
632 			I.STATUS |= ST_C;
633 	}
634 
635 	if (((INT16) a) > 0)
636 		I.STATUS |= ST_LGT | ST_AGT;
637 	else if (((INT16) a) < 0)
638 		I.STATUS |= ST_LGT;
639 	else
640 		I.STATUS |= ST_EQ;
641 
642 	return a;
643 }
644 
645 
646 //
647 // Meat of SLA
648 //
setst_sla_laeco(UINT16 a,UINT16 c)649 static INLINE UINT16 setst_sla_laeco(UINT16 a, UINT16 c)
650 {
651 	I.STATUS &= ~ (ST_LGT | ST_AGT | ST_EQ | ST_C | ST_OV);
652 
653 	if (c != 0)
654 	{
655 		{
656 			register UINT16 mask;
657 			register UINT16 ousted_bits;
658 
659 			mask = 0xFFFF << (16-c-1);
660 			ousted_bits = a & mask;
661 
662 			if (ousted_bits)        // If ousted_bits is neither all 0s
663 				if (ousted_bits ^ mask)   // nor all 1s,
664 					I.STATUS |= ST_OV;  // we set overflow
665 		}
666 
667 		a <<= c-1;
668 		if (a & 0x8000) // The carry bit equals the last bit that is shifted out
669 			I.STATUS |= ST_C;
670 
671 		a <<= 1;
672 	}
673 
674 	if (((INT16) a) > 0)
675 		I.STATUS |= ST_LGT | ST_AGT;
676 	else if (((INT16) a) < 0)
677 		I.STATUS |= ST_LGT;
678 	else
679 		I.STATUS |= ST_EQ;
680 
681 	return a;
682 }
683 
684 
685 /***********************************************************************/
686 
687