comparison Plugins/Input/console/Spc_Cpu.cpp @ 90:252843aac42f trunk

[svn] Import the initial sources for console music support.
author nenolod
date Tue, 01 Nov 2005 19:57:26 -0800
parents
children 7c5e886205ef
comparison
equal deleted inserted replaced
89:feeda0dda3ce 90:252843aac42f
1
2 // Game_Music_Emu 0.2.4. http://www.slack.net/~ant/libs/
3
4 #include "Spc_Cpu.h"
5
6 #include <limits.h>
7
8 #include "blargg_endian.h"
9 #include "Snes_Spc.h"
10
11 /* Copyright (C) 2004-2005 Shay Green. This module is free software; you
12 can redistribute it and/or modify it under the terms of the GNU Lesser
13 General Public License as published by the Free Software Foundation; either
14 version 2.1 of the License, or (at your option) any later version. This
15 module is distributed in the hope that it will be useful, but WITHOUT ANY
16 WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
17 FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for
18 more details. You should have received a copy of the GNU Lesser General
19 Public License along with this module; if not, write to the Free Software
20 Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */
21
22 #include BLARGG_SOURCE_BEGIN
23
24 // Several instructions are commented out (or not even implemented). These aren't
25 // used by the SPC files tested.
26
27 // Optimize performance for the most common instructions, and size for the rest:
28 //
29 // 15% 0xF0 BEQ rel
30 // 8% 0xE4 MOV A,dp
31 // 4% 0xF5 MOV A,abs+X
32 // 4% 0xD0 BNE rel
33 // 4% 0x6F RET
34 // 4% 0x3F CALL addr
35 // 4% 0xF4 MOV A,dp+X
36 // 3% 0xC4 MOV dp,A
37 // 2% 0xEB MOV Y,dp
38 // 2% 0x3D INC X
39 // 2% 0xF6 MOV A,abs+Y
40 // (1% and below not shown)
41
42 Spc_Cpu::Spc_Cpu( uint8_t* ram_, Snes_Spc* e ) :
43 ram( ram_ ),
44 emu( *e )
45 {
46 remain_ = 0;
47 BOOST_STATIC_ASSERT( sizeof (int) >= 4 );
48 }
49
50 #define READ( addr ) (emu.read( addr ))
51 #define WRITE( addr, value ) (emu.write( addr, value ))
52
53 #define READ_DP( addr ) READ( (addr) + dp )
54 #define WRITE_DP( addr, value ) WRITE( (addr) + dp, value )
55
56 #define READ_PROG( addr ) (ram [addr])
57 #define READ_PROG16( addr ) GET_LE16( &READ_PROG( addr ) )
58
59 int Spc_Cpu::read( spc_addr_t addr )
60 {
61 return READ( addr );
62 }
63
64 void Spc_Cpu::write( spc_addr_t addr, int data )
65 {
66 WRITE( addr, data );
67 }
68
69 // Cycle table derived from text copy of SPC-700 manual (using regular expressions)
70 static const unsigned char cycle_table [0x100] = {
71 2,8,4,5,3,4,3,6,2,6,5,4,5,4,6,8,
72 2,8,4,5,4,5,5,6,5,5,6,5,2,2,4,6,
73 2,8,4,5,3,4,3,6,2,6,5,4,5,4,5,4,
74 2,8,4,5,4,5,5,6,5,5,6,5,2,2,3,8,
75 2,8,4,5,3,4,3,6,2,6,4,4,5,4,6,6,
76 2,8,4,5,4,5,5,6,5,5,4,5,2,2,4,3,
77 2,8,4,5,3,4,3,6,2,6,4,4,5,4,5,5,
78 2,8,4,5,4,5,5,6,5,5,5,5,2,2,3,6,
79 2,8,4,5,3,4,3,6,2,6,5,4,5,2,4,5,
80 2,8,4,5,4,5,5,6,5,5,5,5,2,2,12,5,
81 3,8,4,5,3,4,3,6,2,6,4,4,5,2,4,4,
82 2,8,4,5,4,5,5,6,5,5,5,5,2,2,3,4,
83 3,8,4,5,4,5,4,7,2,5,6,4,5,2,4,9,
84 2,8,4,5,5,6,6,7,4,5,4,5,2,2,6,3,
85 2,8,4,5,3,4,3,6,2,4,5,3,4,3,4,3,
86 2,8,4,5,4,5,5,6,3,4,5,4,2,2,4,3
87 };
88
89 // The C,mem instructions are hardly used, so a non-inline function is used for
90 // the common access code.
91 unsigned Spc_Cpu::mem_bit( spc_addr_t pc )
92 {
93 unsigned addr = READ_PROG16( pc );
94 unsigned t = READ( addr & 0x1fff ) >> (addr >> 13);
95 return (t << 8) & 0x100;
96 }
97
98 #include BLARGG_ENABLE_OPTIMIZER
99
100 spc_time_t Spc_Cpu::run( spc_time_t cycle_count )
101 {
102 remain_ = cycle_count;
103
104 #if BLARGG_CPU_POWERPC
105 uint8_t* const ram = this->ram; // cache
106 #endif
107
108 // Stack pointer is kept one greater than usual SPC stack pointer to allow
109 // common pre-decrement and post-increment memory instructions that some
110 // processors have. Address wrap-around isn't supported.
111 #define PUSH( v ) (*--sp = (v))
112 #define PUSH16( v ) (sp -= 2, SET_LE16( sp, v ))
113 #define POP() (*sp++)
114 #define SET_SP( v ) (sp = ram + 0x101 + (v))
115 #define GET_SP() (sp - 0x101 - ram)
116
117 uint8_t* sp;
118 SET_SP( r.sp );
119
120 // registers
121 unsigned pc = r.pc;
122 int a = r.a;
123 int x = r.x;
124 int y = r.y;
125
126 // status flags
127
128 const int st_n = 0x80;
129 const int st_v = 0x40;
130 const int st_p = 0x20;
131 const int st_b = 0x10;
132 const int st_h = 0x08;
133 const int st_i = 0x04;
134 const int st_z = 0x02;
135 const int st_c = 0x01;
136
137 // Special encoding for negative and zero being set simultaneously (by POP PSW).
138 // To do: be sure this works properly (I copied it from my NES 6502 emulator).
139 #define IS_NEG (int ((nz + 0x800) | (nz << (CHAR_BIT * sizeof (int) - 8))) < 0)
140
141 #define CALC_STATUS( out ) do { \
142 out = status & ~(st_n | st_z | st_c); \
143 out |= (c >> 8) & st_c; \
144 out |= (dp >> 3) & st_p; \
145 if ( IS_NEG ) out |= st_n; \
146 if ( !(uint8_t) nz ) out |= st_z; \
147 } while ( 0 )
148
149 #define SET_STATUS( in ) do { \
150 status = in & ~(st_n | st_z | st_c | st_p); \
151 c = in << 8; \
152 nz = in << 4; \
153 nz &= 0x820; \
154 nz ^= ~0xDF; \
155 dp = (in << 3) & 0x100; \
156 } while ( 0 )
157
158 uint8_t status;
159 int c; // store C as 'c' & 0x100.
160 int nz; // store Z as 'nz' & 0xFF == 0 (see above for encoding of N)
161 unsigned dp; // direct page base
162 {
163 int temp = r.status;
164 SET_STATUS( temp );
165 }
166
167 goto loop;
168
169 unsigned data; // first operand of instruction and temporary across function calls
170
171 // Common endings for instructions
172 cbranch_taken_loop: // compare and branch
173 data = READ_PROG( pc );
174 branch_taken_loop: // taken branch (displacement already in 'data')
175 pc += (BOOST::int8_t) data; // sign-extend
176 remain_ -= 2;
177 inc_pc_loop: // end of instruction with an operand
178 pc++;
179 loop:
180
181 // Be sure all registers are in range. PC and SP wrap-around isn't handled so
182 // those checks might fail, but a, x, and y should always be in range.
183 check( (unsigned) pc < 0x10000 );
184 check( (unsigned) GET_SP() < 0x100 );
185 check( (unsigned) a < 0x100 );
186 check( (unsigned) x < 0x100 );
187 check( (unsigned) y < 0x100 );
188
189 // Read opcode and first operand. Optimize if processor's byte order is known
190 // and non-portable constructs are allowed.
191 #if BLARGG_NONPORTABLE && BLARGG_BIG_ENDIAN
192 data = *(BOOST::uint16_t*) &READ_PROG( pc );
193 pc++;
194 unsigned opcode = data >> 8;
195 data = (uint8_t) data;
196
197 #elif BLARGG_NONPORTABLE && BLARGG_LITTLE_ENDIAN
198 data = *(BOOST::uint16_t*) &READ_PROG( pc );
199 pc++;
200 unsigned opcode = (uint8_t) data;
201 data >>= 8;
202
203 #else
204 unsigned opcode = READ_PROG( pc );
205 pc++;
206 data = READ_PROG( pc );
207
208 #endif
209
210 if ( remain_ <= 0 )
211 goto stop;
212
213 remain_ -= cycle_table [opcode];
214
215 // Use 'data' for temporaries whose lifetime crosses read/write calls, otherwise
216 // use a local temporary.
217 switch ( opcode )
218 {
219
220 #define BRANCH( cond ) \
221 if ( cond ) \
222 goto branch_taken_loop; \
223 goto inc_pc_loop;
224
225 // Most-Common
226
227 case 0xF0: // BEQ (most common)
228 BRANCH( !(uint8_t) nz )
229
230 case 0xD0: // BNE
231 BRANCH( (uint8_t) nz )
232
233 case 0x3F: // CALL
234 PUSH16( pc + 2 );
235 pc = READ_PROG16( pc );
236 goto loop;
237
238 case 0x6F: // RET
239 pc = POP();
240 pc += POP() * 0x100;
241 goto loop;
242
243 #define CASE( n ) case n:
244
245 // Define common address modes based on opcode for immediate mode. Execution
246 // ends with data set to the address of the operand.
247 #define ADDR_MODES( op ) \
248 CASE( op - 0x02 ) /* (X) */ \
249 data = x + dp; \
250 pc--; \
251 goto end_##op; \
252 CASE( op + 0x0F ) /* (dp)+Y */ \
253 data = READ_PROG16( data + dp ) + y;\
254 goto end_##op; \
255 CASE( op - 0x01 ) /* (dp+X) */ \
256 data = READ_PROG16( uint8_t (data + x) + dp );\
257 goto end_##op; \
258 CASE( op + 0x0E ) /* abs+Y */ \
259 data += y; \
260 goto abs_##op; \
261 CASE( op + 0x0D ) /* abs+X */ \
262 data += x; \
263 CASE( op - 0x03 ) /* abs */ \
264 abs_##op: \
265 pc++; \
266 data += 0x100 * READ_PROG( pc );\
267 goto end_##op; \
268 CASE( op + 0x0C ) /* dp+X */ \
269 data = uint8_t (data + x); \
270 CASE( op - 0x04 ) /* dp */ \
271 data += dp; \
272 end_##op:
273
274 // 1. 8-bit Data Transmission Commands. Group I
275
276 ADDR_MODES( 0xE8 ) // MOV A,addr
277 // case 0xE4: // MOV a,dp (most common)
278 mov_a_addr:
279 a = nz = READ( data );
280 goto inc_pc_loop;
281 case 0xBF: // MOV A,(X)+
282 data = x + dp;
283 x = uint8_t (x + 1);
284 pc--;
285 goto mov_a_addr;
286
287 case 0xE8: // MOV A,imm
288 a = data;
289 nz = data;
290 goto inc_pc_loop;
291
292 case 0xF9: // MOV X,dp+Y
293 data = uint8_t (data + y);
294 case 0xF8: // MOV X,dp
295 data += dp;
296 goto mov_x_addr;
297 case 0xE9: // MOV X,abs
298 data = READ_PROG16( pc );
299 pc++;
300 mov_x_addr:
301 data = READ( data );
302 case 0xCD: // MOV X,imm
303 x = data;
304 nz = data;
305 goto inc_pc_loop;
306
307 case 0xFB: // MOV Y,dp+X
308 data = uint8_t (data + x);
309 case 0xEB: // MOV Y,dp
310 data += dp;
311 goto mov_y_addr;
312 case 0xEC: // MOV Y,abs
313 data = READ_PROG16( pc );
314 pc++;
315 mov_y_addr:
316 data = READ( data );
317 case 0x8D: // MOV Y,imm
318 y = data;
319 nz = data;
320 goto inc_pc_loop;
321
322 // 2. 8-BIT DATA TRANSMISSION COMMANDS. GROUP 2.
323
324 ADDR_MODES( 0xC8 ) // MOV addr,A
325 WRITE( data, a );
326 goto inc_pc_loop;
327
328 {
329 int temp;
330 case 0xCC: // MOV abs,Y
331 temp = y;
332 goto mov_abs_temp;
333 case 0xC9: // MOV abs,X
334 temp = x;
335 mov_abs_temp:
336 WRITE( READ_PROG16( pc ), temp );
337 pc += 2;
338 goto loop;
339 }
340
341 case 0xD9: // MOV dp+Y,X
342 data = uint8_t (data + y);
343 case 0xD8: // MOV dp,X
344 WRITE( data + dp, x );
345 goto inc_pc_loop;
346
347 case 0xDB: // MOV dp+X,Y
348 data = uint8_t (data + x);
349 case 0xCB: // MOV dp,Y
350 WRITE( data + dp, y );
351 goto inc_pc_loop;
352
353 case 0xFA: // MOV dp,dp
354 data = READ( data + dp );
355 case 0x8F: // MOV dp,#imm
356 pc++;
357 WRITE_DP( READ_PROG( pc ), data );
358 goto inc_pc_loop;
359
360 // 3. 8-BIT DATA TRANSMISSIN COMMANDS, GROUP 3.
361
362 case 0x7D: // MOV A,X
363 a = x;
364 nz = x;
365 goto loop;
366
367 case 0xDD: // MOV A,Y
368 a = y;
369 nz = y;
370 goto loop;
371
372 case 0x5D: // MOV X,A
373 x = a;
374 nz = a;
375 goto loop;
376
377 case 0xFD: // MOV Y,A
378 y = a;
379 nz = a;
380 goto loop;
381
382 case 0x9D: // MOV X,SP
383 x = nz = GET_SP();
384 goto loop;
385
386 case 0xBD: // MOV SP,X
387 SET_SP( x );
388 goto loop;
389
390 //case 0xC6: // MOV (X),A (handled by MOV addr,A in group 2)
391
392 case 0xAF: // MOV (X)+,A
393 WRITE_DP( x, a );
394 x++;
395 goto loop;
396
397 // 5. 8-BIT LOGIC OPERATION COMMANDS.
398
399 #define LOGICAL_OP( op, func ) \
400 ADDR_MODES( op ) /* addr */ \
401 data = READ( data ); \
402 case op: /* imm */ \
403 nz = a func##= data; \
404 goto inc_pc_loop; \
405 { unsigned addr; \
406 case op + 0x11: /* X,Y */ \
407 data = READ_DP( y ); \
408 addr = x + dp; \
409 pc--; \
410 goto addr_##op; \
411 case op + 0x01: /* dp,dp */ \
412 data = READ_DP( data ); \
413 case op + 0x10: /*dp,imm*/\
414 pc++; \
415 addr = READ_PROG( pc ) + dp;\
416 addr_##op: \
417 nz = data func READ( addr );\
418 WRITE( addr, nz ); \
419 goto inc_pc_loop; \
420 }
421
422 LOGICAL_OP( 0x28, & ); // AND
423
424 LOGICAL_OP( 0x08, | ); // OR
425
426 LOGICAL_OP( 0x48, ^ ); // EOR
427
428 // 4. 8-BIT ARITHMETIC OPERATION COMMANDS.
429
430 ADDR_MODES( 0x68 ) // CMP addr
431 data = READ( data );
432 case 0x68: // CMP imm
433 nz = a - data;
434 c = ~nz;
435 goto inc_pc_loop;
436
437 case 0x79: // CMP (X),(Y)
438 data = READ_DP( x );
439 nz = data - READ_DP( y );
440 c = ~nz;
441 goto loop;
442
443 case 0x69: // CMP (dp),(dp)
444 data = READ_DP( data );
445 case 0x78: // CMP dp,imm
446 pc++;
447 nz = READ_DP( READ_PROG( pc ) ) - data;
448 c = ~nz;
449 goto inc_pc_loop;
450
451 case 0x3E: // CMP X,dp
452 data += dp;
453 goto cmp_x_addr;
454 case 0x1E: // CMP X,abs
455 data = READ_PROG16( pc );
456 pc++;
457 cmp_x_addr:
458 data = READ( data );
459 case 0xC8: // CMP X,imm
460 nz = x - data;
461 c = ~nz;
462 goto inc_pc_loop;
463
464 case 0x7E: // CMP Y,dp
465 data += dp;
466 goto cmp_y_addr;
467 case 0x5E: // CMP Y,abs
468 data = READ_PROG16( pc );
469 pc++;
470 cmp_y_addr:
471 data = READ( data );
472 case 0xAD: // CMP Y,imm
473 nz = y - data;
474 c = ~nz;
475 goto inc_pc_loop;
476
477 {
478 int addr;
479 case 0xB9: // SBC (x),(y)
480 case 0x99: // ADC (x),(y)
481 pc--; // compensate for inc later
482 data = READ_DP( x );
483 addr = y + dp;
484 goto adc_addr;
485 case 0xA9: // SBC dp,dp
486 case 0x89: // ADC dp,dp
487 data = READ_DP( data );
488 case 0xB8: // SBC dp,imm
489 case 0x98: // ADC dp,imm
490 pc++;
491 addr = READ_PROG( pc ) + dp;
492 adc_addr:
493 nz = READ( addr );
494 goto adc_data;
495
496 // catch ADC and SBC together, then decode later based on operand
497 #undef CASE
498 #define CASE( n ) case n: case (n) + 0x20:
499 ADDR_MODES( 0x88 ) // ADC/SBC addr
500 data = READ( data );
501 case 0xA8: // SBC imm
502 case 0x88: // ADC imm
503 addr = -1; // A
504 nz = a;
505 adc_data: {
506 if ( opcode & 0x20 )
507 data ^= 0xff; // SBC
508 int carry = (c >> 8) & 1;
509 int ov = (nz ^ 0x80) + carry + (BOOST::int8_t) data; // sign-extend
510 int hc = (nz & 15) + carry;
511 c = nz += data + carry;
512 hc = (nz & 15) - hc;
513 status = (status & ~(st_v | st_h)) | ((ov >> 2) & st_v) | ((hc >> 1) & st_h);
514 if ( addr < 0 ) {
515 a = (uint8_t) nz;
516 goto inc_pc_loop;
517 }
518 WRITE( addr, (uint8_t) nz );
519 goto inc_pc_loop;
520 }
521
522 }
523
524 // 6. ADDITION & SUBTRACTION COMMANDS.
525
526 #define INC_DEC_REG( reg, n ) \
527 nz = reg + n; \
528 reg = (uint8_t) nz; \
529 goto loop;
530
531 case 0xBC: INC_DEC_REG( a, 1 ) // INC A
532 case 0x3D: INC_DEC_REG( x, 1 ) // INC X
533 case 0xFC: INC_DEC_REG( y, 1 ) // INC Y
534
535 case 0x9C: INC_DEC_REG( a, -1 ) // DEC A
536 case 0x1D: INC_DEC_REG( x, -1 ) // DEC X
537 case 0xDC: INC_DEC_REG( y, -1 ) // DEC Y
538
539 case 0x9B: // DEC dp+X
540 case 0xBB: // INC dp+X
541 data = uint8_t (data + x);
542 case 0x8B: // DEC dp
543 case 0xAB: // INC dp
544 data += dp;
545 goto inc_abs;
546 case 0x8C: // DEC abs
547 case 0xAC: // INC abs
548 data = READ_PROG16( pc );
549 pc++;
550 inc_abs:
551 nz = ((opcode >> 4) & 2) - 1;
552 nz += READ( data );
553 WRITE( data, (uint8_t) nz );
554 goto inc_pc_loop;
555
556 // 7. SHIFT, ROTATION COMMANDS
557
558 case 0x5C: // LSR A
559 c = 0;
560 case 0x7C:{// ROR A
561 nz = ((c >> 1) & 0x80) | (a >> 1);
562 c = a << 8;
563 a = nz;
564 goto loop;
565 }
566
567 case 0x1C: // ASL A
568 c = 0;
569 case 0x3C:{// ROL A
570 int temp = (c >> 8) & 1;
571 c = a << 1;
572 nz = c | temp;
573 a = (uint8_t) nz;
574 goto loop;
575 }
576
577 case 0x0B: // ASL dp
578 c = 0;
579 data += dp;
580 goto rol_mem;
581 case 0x1B: // ASL dp+X
582 c = 0;
583 case 0x3B: // ROL dp+X
584 data = uint8_t (data + x);
585 case 0x2B: // ROL dp
586 data += dp;
587 goto rol_mem;
588 case 0x0C: // ASL abs
589 c = 0;
590 case 0x2C: // ROL abs
591 data = READ_PROG16( pc );
592 pc++;
593 rol_mem:
594 nz = (c >> 8) & 1;
595 nz |= (c = READ( data ) << 1);
596 WRITE( data, (uint8_t) nz );
597 goto inc_pc_loop;
598
599 case 0x4B: // LSR dp
600 c = 0;
601 data += dp;
602 goto ror_mem;
603 case 0x5B: // LSR dp+X
604 c = 0;
605 case 0x7B: // ROR dp+X
606 data = uint8_t (data + x);
607 case 0x6B: // ROR dp
608 data += dp;
609 goto ror_mem;
610 case 0x4C: // LSR abs
611 c = 0;
612 case 0x6C: // ROR abs
613 data = READ_PROG16( pc );
614 pc++;
615 ror_mem: {
616 int temp = READ( data );
617 nz = ((c >> 1) & 0x80) | (temp >> 1);
618 c = temp << 8;
619 WRITE( data, nz );
620 goto inc_pc_loop;
621 }
622
623 case 0x9F: // XCN
624 nz = a = (a >> 4) | uint8_t (a << 4);
625 goto loop;
626
627 // 8. 16-BIT TRANSMISION COMMANDS
628
629 case 0xBA: // MOVW YA,dp
630 a = READ_DP( data );
631 nz = (a & 0x7f) | (a >> 1);
632 y = READ_DP( uint8_t (data + 1) );
633 nz |= y;
634 goto inc_pc_loop;
635
636 case 0xDA: // MOVW dp,YA
637 WRITE_DP( data, a );
638 WRITE_DP( uint8_t (data + 1), y );
639 goto inc_pc_loop;
640
641 // 9. 16-BIT OPERATION COMMANDS.
642
643 case 0x3A: // INCW dp
644 case 0x1A:{// DECW dp
645 data += dp;
646
647 // low byte
648 int temp = READ( data );
649 temp += ((opcode >> 4) & 2) - 1; // +1 for INCW, -1 for DECW
650 nz = ((temp >> 1) | temp) & 0x7f;
651 WRITE( data, (uint8_t) temp );
652
653 // high byte
654 data = uint8_t (data + 1) + dp;
655 temp >>= 8;
656 temp = uint8_t (temp + READ( data ));
657 nz |= temp;
658 WRITE( data, temp );
659
660 goto inc_pc_loop;
661 }
662
663 case 0x9A: // SUBW YA,dp
664 case 0x7A: // ADDW YA,dp
665 {
666 // read 16-bit addend
667 int temp = READ_DP( data );
668 int sign = READ_DP( uint8_t (data + 1) );
669 temp += 0x100 * sign;
670 status &= ~(st_v | st_h);
671
672 // to do: fix half-carry for SUBW (it's probably wrong)
673
674 // for SUBW, negate and truncate to 16 bits
675 if ( opcode & 0x80 ) {
676 temp = (temp ^ 0xFFFF) + 1;
677 sign = temp >> 8;
678 }
679
680 // add low byte (A)
681 temp += a;
682 a = (uint8_t) temp;
683 nz = (temp | (temp >> 1)) & 0x7f;
684
685 // add high byte (Y)
686 temp >>= 8;
687 c = y + temp;
688 nz |= c;
689
690 // half-carry (temporary avoids CodeWarrior optimizer bug)
691 unsigned hc = (c & 15) - (y & 15);
692 status |= (hc >> 4) & st_h;
693
694 // overflow if sign of YA changed when previous sign and addend sign were same
695 status |= (((c ^ y) & ~(y ^ sign)) >> 1) & st_v;
696
697 y = (uint8_t) c;
698
699 goto inc_pc_loop;
700 }
701
702 case 0x5A: { // CMPW YA,dp
703 int temp = a - READ_DP( data );
704 nz = ((temp >> 1) | temp) & 0x7f;
705 temp = y + (temp >> 8);
706 temp -= READ_DP( uint8_t (data + 1) );
707 nz |= temp;
708 c = ~temp;
709 goto inc_pc_loop;
710 }
711
712 // 10. MULTIPLICATION & DIVISON COMMANDS.
713
714 case 0xCF: { // MUL YA
715 unsigned temp = y * a;
716 a = (uint8_t) temp;
717 nz = ((temp >> 1) | temp) & 0x7f;
718 y = temp >> 8;
719 nz |= y;
720 goto loop;
721 }
722
723 case 0x9E: // DIV YA,X
724 {
725 // behavior based on SPC CPU tests
726
727 status &= ~(st_h | st_v);
728
729 if ( y >= x )
730 status |= st_v;
731
732 if ( (y & 15) >= (x & 15) )
733 status |= st_h;
734
735 unsigned temp = y * 0x100 + a;
736 if ( y < x * 2 ) {
737 a = temp / x;
738 y = temp - a * x;
739 }
740 else {
741 temp -= x * 0x200;
742 a = temp / (256 - x);
743 y = temp - a * (256 - x) + x;
744 a = 255 - a;
745 }
746
747 nz = (uint8_t) a;
748 a = (uint8_t) a;
749
750 goto loop;
751 }
752
753 // 11. DECIMAL COMPENSATION COMMANDS.
754
755 // seem unused
756 // case 0xDF: // DAA
757 // case 0xBE: // DAS
758
759 // 12. BRANCHING COMMANDS.
760
761 case 0x2F: // BRA rel
762 goto branch_taken_loop;
763
764 case 0x30: // BMI
765 BRANCH( IS_NEG )
766
767 case 0x10: // BPL
768 BRANCH( !IS_NEG )
769
770 case 0xB0: // BCS
771 BRANCH( c & 0x100 )
772
773 case 0x90: // BCC
774 BRANCH( !(c & 0x100) )
775
776 case 0x70: // BVS
777 BRANCH( status & st_v )
778
779 case 0x50: // BVC
780 BRANCH( !(status & st_v) )
781
782 case 0x03: // BBS dp.bit,rel
783 case 0x23:
784 case 0x43:
785 case 0x63:
786 case 0x83:
787 case 0xA3:
788 case 0xC3:
789 case 0xE3:
790 pc++;
791 if ( (READ_DP( data ) >> (opcode >> 5)) & 1 )
792 goto cbranch_taken_loop;
793 goto inc_pc_loop;
794
795 case 0x13: // BBC dp.bit,rel
796 case 0x33:
797 case 0x53:
798 case 0x73:
799 case 0x93:
800 case 0xB3:
801 case 0xD3:
802 case 0xF3:
803 pc++;
804 if ( !((READ_DP( data ) >> (opcode >> 5)) & 1) )
805 goto cbranch_taken_loop;
806 goto inc_pc_loop;
807
808 case 0xDE: // CBNE dp+X,rel
809 data = uint8_t (data + x);
810 // fall through
811 case 0x2E: // CBNE dp,rel
812 pc++;
813 if ( READ_DP( data ) != a )
814 goto cbranch_taken_loop;
815 goto inc_pc_loop;
816
817 case 0xFE: // DBNZ Y,rel
818 y = uint8_t (y - 1);
819 if ( y )
820 goto branch_taken_loop;
821 goto inc_pc_loop;
822
823 case 0x6E: { // DBNZ dp,rel
824 pc++;
825 unsigned temp = READ_DP( data ) - 1;
826 WRITE_DP( (uint8_t) data, (uint8_t) temp );
827 if ( temp )
828 goto cbranch_taken_loop;
829 goto inc_pc_loop;
830 }
831
832 case 0x1F: // JMP (abs+X)
833 pc = READ_PROG16( pc ) + x;
834 // fall through
835 case 0x5F: // JMP abs
836 pc = READ_PROG16( pc );
837 goto loop;
838
839 // 13. SUB-ROUTINE CALL RETURN COMMANDS.
840
841 /*
842 // seems unused
843 case 0x0F: // BRK
844 PUSH16( pc + 1 );
845 pc = READ_PROG16( 0xffde ); // vector address verified
846 int temp;
847 CALC_STATUS( temp );
848 PUSH( temp );
849 status = (status | st_b) & ~st_i;
850 goto loop;
851 */
852
853 case 0x4F: // PCALL offset
854 pc++;
855 PUSH16( pc );
856 pc = 0xff00 + data;
857 goto loop;
858
859 case 0x01: // TCALL n
860 case 0x11:
861 case 0x21:
862 case 0x31:
863 case 0x41:
864 case 0x51:
865 case 0x61:
866 case 0x71:
867 case 0x81:
868 case 0x91:
869 case 0xA1:
870 case 0xB1:
871 case 0xC1:
872 case 0xD1:
873 case 0xE1:
874 case 0xF1:
875 PUSH16( pc );
876 pc = READ_PROG16( 0xffde - (opcode >> 3) );
877 goto loop;
878
879 // 14. STACK OPERATION COMMANDS.
880
881 {
882 int temp;
883 case 0x7F: // RET1
884 temp = POP();
885 pc = POP();
886 pc |= POP() << 8;
887 goto set_status;
888 case 0x8E: // POP PSW
889 temp = POP();
890 set_status:
891 SET_STATUS( temp );
892 goto loop;
893 }
894
895 case 0x0D: { // PUSH PSW
896 int temp;
897 CALC_STATUS( temp );
898 PUSH( temp );
899 goto loop;
900 }
901
902 case 0x2D: // PUSH A
903 PUSH( a );
904 goto loop;
905
906 case 0x4D: // PUSH X
907 PUSH( x );
908 goto loop;
909
910 case 0x6D: // PUSH Y
911 PUSH( y );
912 goto loop;
913
914 case 0xAE: // POP A
915 a = POP();
916 goto loop;
917
918 case 0xCE: // POP X
919 x = POP();
920 goto loop;
921
922 case 0xEE: // POP Y
923 y = POP();
924 goto loop;
925
926 // 15. BIT OPERATION COMMANDS.
927
928 case 0x02: // SET1
929 case 0x22:
930 case 0x42:
931 case 0x62:
932 case 0x82:
933 case 0xA2:
934 case 0xC2:
935 case 0xE2:
936 case 0x12: // CLR1
937 case 0x32:
938 case 0x52:
939 case 0x72:
940 case 0x92:
941 case 0xB2:
942 case 0xD2:
943 case 0xF2: {
944 data += dp;
945 int bit = 1 << (opcode >> 5);
946 int mask = ~bit;
947 if ( opcode & 0x10 )
948 bit = 0;
949 WRITE( data, (READ( data ) & mask) | bit );
950 goto inc_pc_loop;
951 }
952
953 case 0x0E: // TSET1 abs
954 case 0x4E:{// TCLR1 abs
955 data = READ_PROG16( pc );
956 pc += 2;
957 unsigned temp = READ( data );
958 nz = temp & a;
959 temp &= ~a;
960 if ( !(opcode & 0x40) )
961 temp |= a;
962 WRITE( data, temp );
963 goto loop;
964 }
965
966 case 0x4A: // AND1 C,mem.bit
967 c &= mem_bit( pc );
968 pc += 2;
969 goto loop;
970 /*
971 // seem unused
972 case 0x6A: // AND1 C,/mem.bit
973 c &= ~mem_bit( pc );
974 pc += 2;
975 goto loop;
976
977 case 0x0A: // OR1 C,mem.bit
978 c |= mem_bit( pc );
979 pc += 2;
980 goto loop;
981
982 case 0x2A: // OR1 C,/mem.bit
983 c |= ~mem_bit( pc );
984 pc += 2;
985 goto loop;
986 */
987
988 case 0x8A: // EOR1 C,mem.bit
989 c ^= mem_bit( pc );
990 pc += 2;
991 goto loop;
992
993 case 0xEA: { // NOT1 C,mem.bit
994 data = READ_PROG16( pc );
995 pc += 2;
996 unsigned temp = READ( data & 0x1fff );
997 temp ^= 1 << (data >> 13);
998 WRITE( data & 0x1fff, temp );
999 goto loop;
1000 }
1001
1002 case 0xCA: { // MOV1 mem.bit,C
1003 data = READ_PROG16( pc );
1004 pc += 2;
1005 unsigned temp = READ( data & 0x1fff );
1006 unsigned bit = data >> 13;
1007 temp = (temp & ~(1 << bit)) | ((c >> (8 - bit)) & 1);
1008 WRITE( data & 0x1fff, temp );
1009 goto loop;
1010 }
1011
1012 case 0xAA: // MOV1 C,mem.bit
1013 c = mem_bit( pc );
1014 pc += 2;
1015 goto loop;
1016
1017 // 16. PROGRAM STATUS FLAG OPERATION COMMANDS.
1018
1019 case 0x60: // CLRC
1020 c = 0;
1021 goto loop;
1022
1023 case 0x80: // SETC
1024 c = ~0;
1025 goto loop;
1026
1027 case 0xED: // NOTC
1028 c ^= 0x100;
1029 goto loop;
1030
1031 case 0xE0: // CLRV
1032 status &= ~(st_v | st_h);
1033 goto loop;
1034
1035 case 0x20: // CLRP
1036 dp = 0;
1037 goto loop;
1038
1039 case 0x40: // SETP
1040 dp = 0x100;
1041 goto loop;
1042
1043 /* // seem unused
1044 case 0xA0: // EI
1045 status |= st_i;
1046 goto loop;
1047
1048 case 0xC0: // DI
1049 status &= ~st_i;
1050 goto loop;
1051 */
1052
1053 // 17. OTHER COMMANDS.
1054
1055 case 0x00: // NOP
1056 goto loop;
1057
1058 //case 0xEF: // SLEEP
1059 //case 0xFF: // STOP
1060
1061 } // switch
1062
1063 // unhandled instructions fall out of switch so emulator can catch them
1064
1065 stop:
1066 pc--;
1067
1068 {
1069 int temp;
1070 CALC_STATUS( temp );
1071 r.status = temp;
1072 }
1073
1074 r.pc = pc;
1075 r.sp = GET_SP();
1076 r.a = a;
1077 r.x = x;
1078 r.y = y;
1079
1080 return remain_;
1081 }
1082