Mercurial > audlegacy
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 |