Mercurial > spc_convert
comparison snes_spc/SPC_CPU.h @ 0:e38dacceb958
initial import
author | Robert McIntyre <rlm@mit.edu> |
---|---|
date | Fri, 21 Oct 2011 05:53:11 -0700 |
parents | |
children |
comparison
equal
deleted
inserted
replaced
-1:000000000000 | 0:e38dacceb958 |
---|---|
1 // snes_spc 0.9.0. http://www.slack.net/~ant/ | |
2 | |
3 /* Copyright (C) 2004-2007 Shay Green. This module is free software; you | |
4 can redistribute it and/or modify it under the terms of the GNU Lesser | |
5 General Public License as published by the Free Software Foundation; either | |
6 version 2.1 of the License, or (at your option) any later version. This | |
7 module is distributed in the hope that it will be useful, but WITHOUT ANY | |
8 WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS | |
9 FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more | |
10 details. You should have received a copy of the GNU Lesser General Public | |
11 License along with this module; if not, write to the Free Software Foundation, | |
12 Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ | |
13 | |
14 //// Memory access | |
15 | |
16 #if SPC_MORE_ACCURACY | |
17 #define SUSPICIOUS_OPCODE( name ) ((void) 0) | |
18 #else | |
19 #define SUSPICIOUS_OPCODE( name ) dprintf( "SPC: suspicious opcode: " name "\n" ) | |
20 #endif | |
21 | |
22 #define CPU_READ( time, offset, addr )\ | |
23 cpu_read( addr, time + offset ) | |
24 | |
25 #define CPU_WRITE( time, offset, addr, data )\ | |
26 cpu_write( data, addr, time + offset ) | |
27 | |
28 #if SPC_MORE_ACCURACY | |
29 #define CPU_READ_TIMER( time, offset, addr, out )\ | |
30 { out = CPU_READ( time, offset, addr ); } | |
31 | |
32 #else | |
33 // timers are by far the most common thing read from dp | |
34 #define CPU_READ_TIMER( time, offset, addr_, out )\ | |
35 {\ | |
36 rel_time_t adj_time = time + offset;\ | |
37 int dp_addr = addr_;\ | |
38 int ti = dp_addr - (r_t0out + 0xF0);\ | |
39 if ( (unsigned) ti < timer_count )\ | |
40 {\ | |
41 Timer* t = &m.timers [ti];\ | |
42 if ( adj_time >= t->next_time )\ | |
43 t = run_timer_( t, adj_time );\ | |
44 out = t->counter;\ | |
45 t->counter = 0;\ | |
46 }\ | |
47 else\ | |
48 {\ | |
49 out = ram [dp_addr];\ | |
50 int i = dp_addr - 0xF0;\ | |
51 if ( (unsigned) i < 0x10 )\ | |
52 out = cpu_read_smp_reg( i, adj_time );\ | |
53 }\ | |
54 } | |
55 #endif | |
56 | |
57 #define TIME_ADJ( n ) (n) | |
58 | |
59 #define READ_TIMER( time, addr, out ) CPU_READ_TIMER( rel_time, TIME_ADJ(time), (addr), out ) | |
60 #define READ( time, addr ) CPU_READ ( rel_time, TIME_ADJ(time), (addr) ) | |
61 #define WRITE( time, addr, data ) CPU_WRITE( rel_time, TIME_ADJ(time), (addr), (data) ) | |
62 | |
63 #define DP_ADDR( addr ) (dp + (addr)) | |
64 | |
65 #define READ_DP_TIMER( time, addr, out ) CPU_READ_TIMER( rel_time, TIME_ADJ(time), DP_ADDR( addr ), out ) | |
66 #define READ_DP( time, addr ) READ ( time, DP_ADDR( addr ) ) | |
67 #define WRITE_DP( time, addr, data ) WRITE( time, DP_ADDR( addr ), data ) | |
68 | |
69 #define READ_PROG16( addr ) GET_LE16( ram + (addr) ) | |
70 | |
71 #define SET_PC( n ) (pc = ram + (n)) | |
72 #define GET_PC() (pc - ram) | |
73 #define READ_PC( pc ) (*(pc)) | |
74 #define READ_PC16( pc ) GET_LE16( pc ) | |
75 | |
76 // TODO: remove non-wrapping versions? | |
77 #define SPC_NO_SP_WRAPAROUND 0 | |
78 | |
79 #define SET_SP( v ) (sp = ram + 0x101 + (v)) | |
80 #define GET_SP() (sp - 0x101 - ram) | |
81 | |
82 #if SPC_NO_SP_WRAPAROUND | |
83 #define PUSH16( v ) (sp -= 2, SET_LE16( sp, v )) | |
84 #define PUSH( v ) (void) (*--sp = (uint8_t) (v)) | |
85 #define POP( out ) (void) ((out) = *sp++) | |
86 | |
87 #else | |
88 #define PUSH16( data )\ | |
89 {\ | |
90 int addr = (sp -= 2) - ram;\ | |
91 if ( addr > 0x100 )\ | |
92 {\ | |
93 SET_LE16( sp, data );\ | |
94 }\ | |
95 else\ | |
96 {\ | |
97 ram [(uint8_t) addr + 0x100] = (uint8_t) data;\ | |
98 sp [1] = (uint8_t) (data >> 8);\ | |
99 sp += 0x100;\ | |
100 }\ | |
101 } | |
102 | |
103 #define PUSH( data )\ | |
104 {\ | |
105 *--sp = (uint8_t) (data);\ | |
106 if ( sp - ram == 0x100 )\ | |
107 sp += 0x100;\ | |
108 } | |
109 | |
110 #define POP( out )\ | |
111 {\ | |
112 out = *sp++;\ | |
113 if ( sp - ram == 0x201 )\ | |
114 {\ | |
115 out = sp [-0x101];\ | |
116 sp -= 0x100;\ | |
117 }\ | |
118 } | |
119 | |
120 #endif | |
121 | |
122 #define MEM_BIT( rel ) CPU_mem_bit( pc, rel_time + rel ) | |
123 | |
124 unsigned SNES_SPC::CPU_mem_bit( uint8_t const* pc, rel_time_t rel_time ) | |
125 { | |
126 unsigned addr = READ_PC16( pc ); | |
127 unsigned t = READ( 0, addr & 0x1FFF ) >> (addr >> 13); | |
128 return t << 8 & 0x100; | |
129 } | |
130 | |
131 //// Status flag handling | |
132 | |
133 // Hex value in name to clarify code and bit shifting. | |
134 // Flag stored in indicated variable during emulation | |
135 int const n80 = 0x80; // nz | |
136 int const v40 = 0x40; // psw | |
137 int const p20 = 0x20; // dp | |
138 int const b10 = 0x10; // psw | |
139 int const h08 = 0x08; // psw | |
140 int const i04 = 0x04; // psw | |
141 int const z02 = 0x02; // nz | |
142 int const c01 = 0x01; // c | |
143 | |
144 int const nz_neg_mask = 0x880; // either bit set indicates N flag set | |
145 | |
146 #define GET_PSW( out )\ | |
147 {\ | |
148 out = psw & ~(n80 | p20 | z02 | c01);\ | |
149 out |= c >> 8 & c01;\ | |
150 out |= dp >> 3 & p20;\ | |
151 out |= ((nz >> 4) | nz) & n80;\ | |
152 if ( !(uint8_t) nz ) out |= z02;\ | |
153 } | |
154 | |
155 #define SET_PSW( in )\ | |
156 {\ | |
157 psw = in;\ | |
158 c = in << 8;\ | |
159 dp = in << 3 & 0x100;\ | |
160 nz = (in << 4 & 0x800) | (~in & z02);\ | |
161 } | |
162 | |
163 SPC_CPU_RUN_FUNC | |
164 { | |
165 uint8_t* const ram = RAM; | |
166 int a = m.cpu_regs.a; | |
167 int x = m.cpu_regs.x; | |
168 int y = m.cpu_regs.y; | |
169 uint8_t const* pc; | |
170 uint8_t* sp; | |
171 int psw; | |
172 int c; | |
173 int nz; | |
174 int dp; | |
175 | |
176 SET_PC( m.cpu_regs.pc ); | |
177 SET_SP( m.cpu_regs.sp ); | |
178 SET_PSW( m.cpu_regs.psw ); | |
179 | |
180 goto loop; | |
181 | |
182 | |
183 // Main loop | |
184 | |
185 cbranch_taken_loop: | |
186 pc += *(BOOST::int8_t const*) pc; | |
187 inc_pc_loop: | |
188 pc++; | |
189 loop: | |
190 { | |
191 unsigned opcode; | |
192 unsigned data; | |
193 | |
194 check( (unsigned) a < 0x100 ); | |
195 check( (unsigned) x < 0x100 ); | |
196 check( (unsigned) y < 0x100 ); | |
197 | |
198 opcode = *pc; | |
199 if ( (rel_time += m.cycle_table [opcode]) > 0 ) | |
200 goto out_of_time; | |
201 | |
202 #ifdef SPC_CPU_OPCODE_HOOK | |
203 SPC_CPU_OPCODE_HOOK( GET_PC(), opcode ); | |
204 #endif | |
205 /* | |
206 //SUB_CASE_COUNTER( 1 ); | |
207 #define PROFILE_TIMER_LOOP( op, addr, len )\ | |
208 if ( opcode == op )\ | |
209 {\ | |
210 int cond = (unsigned) ((addr) - 0xFD) < 3 &&\ | |
211 pc [len] == 0xF0 && pc [len+1] == 0xFE - len;\ | |
212 SUB_CASE_COUNTER( op && cond );\ | |
213 } | |
214 | |
215 PROFILE_TIMER_LOOP( 0xEC, GET_LE16( pc + 1 ), 3 ); | |
216 PROFILE_TIMER_LOOP( 0xEB, pc [1], 2 ); | |
217 PROFILE_TIMER_LOOP( 0xE4, pc [1], 2 ); | |
218 */ | |
219 | |
220 // TODO: if PC is at end of memory, this will get wrong operand (very obscure) | |
221 data = *++pc; | |
222 switch ( opcode ) | |
223 { | |
224 | |
225 // Common instructions | |
226 | |
227 #define BRANCH( cond )\ | |
228 {\ | |
229 pc++;\ | |
230 pc += (BOOST::int8_t) data;\ | |
231 if ( cond )\ | |
232 goto loop;\ | |
233 pc -= (BOOST::int8_t) data;\ | |
234 rel_time -= 2;\ | |
235 goto loop;\ | |
236 } | |
237 | |
238 case 0xF0: // BEQ | |
239 BRANCH( !(uint8_t) nz ) // 89% taken | |
240 | |
241 case 0xD0: // BNE | |
242 BRANCH( (uint8_t) nz ) | |
243 | |
244 case 0x3F:{// CALL | |
245 int old_addr = GET_PC() + 2; | |
246 SET_PC( READ_PC16( pc ) ); | |
247 PUSH16( old_addr ); | |
248 goto loop; | |
249 } | |
250 | |
251 case 0x6F:// RET | |
252 #if SPC_NO_SP_WRAPAROUND | |
253 { | |
254 SET_PC( GET_LE16( sp ) ); | |
255 sp += 2; | |
256 } | |
257 #else | |
258 { | |
259 int addr = sp - ram; | |
260 SET_PC( GET_LE16( sp ) ); | |
261 sp += 2; | |
262 if ( addr < 0x1FF ) | |
263 goto loop; | |
264 | |
265 SET_PC( sp [-0x101] * 0x100 + ram [(uint8_t) addr + 0x100] ); | |
266 sp -= 0x100; | |
267 } | |
268 #endif | |
269 goto loop; | |
270 | |
271 case 0xE4: // MOV a,dp | |
272 ++pc; | |
273 // 80% from timer | |
274 READ_DP_TIMER( 0, data, a = nz ); | |
275 goto loop; | |
276 | |
277 case 0xFA:{// MOV dp,dp | |
278 int temp; | |
279 READ_DP_TIMER( -2, data, temp ); | |
280 data = temp + no_read_before_write ; | |
281 } | |
282 // fall through | |
283 case 0x8F:{// MOV dp,#imm | |
284 int temp = READ_PC( pc + 1 ); | |
285 pc += 2; | |
286 | |
287 #if !SPC_MORE_ACCURACY | |
288 { | |
289 int i = dp + temp; | |
290 ram [i] = (uint8_t) data; | |
291 i -= 0xF0; | |
292 if ( (unsigned) i < 0x10 ) // 76% | |
293 { | |
294 REGS [i] = (uint8_t) data; | |
295 | |
296 // Registers other than $F2 and $F4-$F7 | |
297 //if ( i != 2 && i != 4 && i != 5 && i != 6 && i != 7 ) | |
298 if ( ((~0x2F00 << (bits_in_int - 16)) << i) < 0 ) // 12% | |
299 cpu_write_smp_reg( data, rel_time, i ); | |
300 } | |
301 } | |
302 #else | |
303 WRITE_DP( 0, temp, data ); | |
304 #endif | |
305 goto loop; | |
306 } | |
307 | |
308 case 0xC4: // MOV dp,a | |
309 ++pc; | |
310 #if !SPC_MORE_ACCURACY | |
311 { | |
312 int i = dp + data; | |
313 ram [i] = (uint8_t) a; | |
314 i -= 0xF0; | |
315 if ( (unsigned) i < 0x10 ) // 39% | |
316 { | |
317 unsigned sel = i - 2; | |
318 REGS [i] = (uint8_t) a; | |
319 | |
320 if ( sel == 1 ) // 51% $F3 | |
321 dsp_write( a, rel_time ); | |
322 else if ( sel > 1 ) // 1% not $F2 or $F3 | |
323 cpu_write_smp_reg_( a, rel_time, i ); | |
324 } | |
325 } | |
326 #else | |
327 WRITE_DP( 0, data, a ); | |
328 #endif | |
329 goto loop; | |
330 | |
331 #define CASE( n ) case n: | |
332 | |
333 // Define common address modes based on opcode for immediate mode. Execution | |
334 // ends with data set to the address of the operand. | |
335 #define ADDR_MODES_( op )\ | |
336 CASE( op - 0x02 ) /* (X) */\ | |
337 data = x + dp;\ | |
338 pc--;\ | |
339 goto end_##op;\ | |
340 CASE( op + 0x0F ) /* (dp)+Y */\ | |
341 data = READ_PROG16( data + dp ) + y;\ | |
342 goto end_##op;\ | |
343 CASE( op - 0x01 ) /* (dp+X) */\ | |
344 data = READ_PROG16( ((uint8_t) (data + x)) + dp );\ | |
345 goto end_##op;\ | |
346 CASE( op + 0x0E ) /* abs+Y */\ | |
347 data += y;\ | |
348 goto abs_##op;\ | |
349 CASE( op + 0x0D ) /* abs+X */\ | |
350 data += x;\ | |
351 CASE( op - 0x03 ) /* abs */\ | |
352 abs_##op:\ | |
353 data += 0x100 * READ_PC( ++pc );\ | |
354 goto end_##op;\ | |
355 CASE( op + 0x0C ) /* dp+X */\ | |
356 data = (uint8_t) (data + x); | |
357 | |
358 #define ADDR_MODES_NO_DP( op )\ | |
359 ADDR_MODES_( op )\ | |
360 data += dp;\ | |
361 end_##op: | |
362 | |
363 #define ADDR_MODES( op )\ | |
364 ADDR_MODES_( op )\ | |
365 CASE( op - 0x04 ) /* dp */\ | |
366 data += dp;\ | |
367 end_##op: | |
368 | |
369 // 1. 8-bit Data Transmission Commands. Group I | |
370 | |
371 ADDR_MODES_NO_DP( 0xE8 ) // MOV A,addr | |
372 a = nz = READ( 0, data ); | |
373 goto inc_pc_loop; | |
374 | |
375 case 0xBF:{// MOV A,(X)+ | |
376 int temp = x + dp; | |
377 x = (uint8_t) (x + 1); | |
378 a = nz = READ( -1, temp ); | |
379 goto loop; | |
380 } | |
381 | |
382 case 0xE8: // MOV A,imm | |
383 a = data; | |
384 nz = data; | |
385 goto inc_pc_loop; | |
386 | |
387 case 0xF9: // MOV X,dp+Y | |
388 data = (uint8_t) (data + y); | |
389 case 0xF8: // MOV X,dp | |
390 READ_DP_TIMER( 0, data, x = nz ); | |
391 goto inc_pc_loop; | |
392 | |
393 case 0xE9: // MOV X,abs | |
394 data = READ_PC16( pc ); | |
395 ++pc; | |
396 data = READ( 0, data ); | |
397 case 0xCD: // MOV X,imm | |
398 x = data; | |
399 nz = data; | |
400 goto inc_pc_loop; | |
401 | |
402 case 0xFB: // MOV Y,dp+X | |
403 data = (uint8_t) (data + x); | |
404 case 0xEB: // MOV Y,dp | |
405 // 70% from timer | |
406 pc++; | |
407 READ_DP_TIMER( 0, data, y = nz ); | |
408 goto loop; | |
409 | |
410 case 0xEC:{// MOV Y,abs | |
411 int temp = READ_PC16( pc ); | |
412 pc += 2; | |
413 READ_TIMER( 0, temp, y = nz ); | |
414 //y = nz = READ( 0, temp ); | |
415 goto loop; | |
416 } | |
417 | |
418 case 0x8D: // MOV Y,imm | |
419 y = data; | |
420 nz = data; | |
421 goto inc_pc_loop; | |
422 | |
423 // 2. 8-BIT DATA TRANSMISSION COMMANDS, GROUP 2 | |
424 | |
425 ADDR_MODES_NO_DP( 0xC8 ) // MOV addr,A | |
426 WRITE( 0, data, a ); | |
427 goto inc_pc_loop; | |
428 | |
429 { | |
430 int temp; | |
431 case 0xCC: // MOV abs,Y | |
432 temp = y; | |
433 goto mov_abs_temp; | |
434 case 0xC9: // MOV abs,X | |
435 temp = x; | |
436 mov_abs_temp: | |
437 WRITE( 0, READ_PC16( pc ), temp ); | |
438 pc += 2; | |
439 goto loop; | |
440 } | |
441 | |
442 case 0xD9: // MOV dp+Y,X | |
443 data = (uint8_t) (data + y); | |
444 case 0xD8: // MOV dp,X | |
445 WRITE( 0, data + dp, x ); | |
446 goto inc_pc_loop; | |
447 | |
448 case 0xDB: // MOV dp+X,Y | |
449 data = (uint8_t) (data + x); | |
450 case 0xCB: // MOV dp,Y | |
451 WRITE( 0, data + dp, y ); | |
452 goto inc_pc_loop; | |
453 | |
454 // 3. 8-BIT DATA TRANSMISSIN COMMANDS, GROUP 3. | |
455 | |
456 case 0x7D: // MOV A,X | |
457 a = x; | |
458 nz = x; | |
459 goto loop; | |
460 | |
461 case 0xDD: // MOV A,Y | |
462 a = y; | |
463 nz = y; | |
464 goto loop; | |
465 | |
466 case 0x5D: // MOV X,A | |
467 x = a; | |
468 nz = a; | |
469 goto loop; | |
470 | |
471 case 0xFD: // MOV Y,A | |
472 y = a; | |
473 nz = a; | |
474 goto loop; | |
475 | |
476 case 0x9D: // MOV X,SP | |
477 x = nz = GET_SP(); | |
478 goto loop; | |
479 | |
480 case 0xBD: // MOV SP,X | |
481 SET_SP( x ); | |
482 goto loop; | |
483 | |
484 //case 0xC6: // MOV (X),A (handled by MOV addr,A in group 2) | |
485 | |
486 case 0xAF: // MOV (X)+,A | |
487 WRITE_DP( 0, x, a + no_read_before_write ); | |
488 x++; | |
489 goto loop; | |
490 | |
491 // 5. 8-BIT LOGIC OPERATION COMMANDS | |
492 | |
493 #define LOGICAL_OP( op, func )\ | |
494 ADDR_MODES( op ) /* addr */\ | |
495 data = READ( 0, data );\ | |
496 case op: /* imm */\ | |
497 nz = a func##= data;\ | |
498 goto inc_pc_loop;\ | |
499 { unsigned addr;\ | |
500 case op + 0x11: /* X,Y */\ | |
501 data = READ_DP( -2, y );\ | |
502 addr = x + dp;\ | |
503 goto addr_##op;\ | |
504 case op + 0x01: /* dp,dp */\ | |
505 data = READ_DP( -3, data );\ | |
506 case op + 0x10:{/*dp,imm*/\ | |
507 uint8_t const* addr2 = pc + 1;\ | |
508 pc += 2;\ | |
509 addr = READ_PC( addr2 ) + dp;\ | |
510 }\ | |
511 addr_##op:\ | |
512 nz = data func READ( -1, addr );\ | |
513 WRITE( 0, addr, nz );\ | |
514 goto loop;\ | |
515 } | |
516 | |
517 LOGICAL_OP( 0x28, & ); // AND | |
518 | |
519 LOGICAL_OP( 0x08, | ); // OR | |
520 | |
521 LOGICAL_OP( 0x48, ^ ); // EOR | |
522 | |
523 // 4. 8-BIT ARITHMETIC OPERATION COMMANDS | |
524 | |
525 ADDR_MODES( 0x68 ) // CMP addr | |
526 data = READ( 0, data ); | |
527 case 0x68: // CMP imm | |
528 nz = a - data; | |
529 c = ~nz; | |
530 nz &= 0xFF; | |
531 goto inc_pc_loop; | |
532 | |
533 case 0x79: // CMP (X),(Y) | |
534 data = READ_DP( -2, y ); | |
535 nz = READ_DP( -1, x ) - data; | |
536 c = ~nz; | |
537 nz &= 0xFF; | |
538 goto loop; | |
539 | |
540 case 0x69: // CMP dp,dp | |
541 data = READ_DP( -3, data ); | |
542 case 0x78: // CMP dp,imm | |
543 nz = READ_DP( -1, READ_PC( ++pc ) ) - data; | |
544 c = ~nz; | |
545 nz &= 0xFF; | |
546 goto inc_pc_loop; | |
547 | |
548 case 0x3E: // CMP X,dp | |
549 data += dp; | |
550 goto cmp_x_addr; | |
551 case 0x1E: // CMP X,abs | |
552 data = READ_PC16( pc ); | |
553 pc++; | |
554 cmp_x_addr: | |
555 data = READ( 0, data ); | |
556 case 0xC8: // CMP X,imm | |
557 nz = x - data; | |
558 c = ~nz; | |
559 nz &= 0xFF; | |
560 goto inc_pc_loop; | |
561 | |
562 case 0x7E: // CMP Y,dp | |
563 data += dp; | |
564 goto cmp_y_addr; | |
565 case 0x5E: // CMP Y,abs | |
566 data = READ_PC16( pc ); | |
567 pc++; | |
568 cmp_y_addr: | |
569 data = READ( 0, data ); | |
570 case 0xAD: // CMP Y,imm | |
571 nz = y - data; | |
572 c = ~nz; | |
573 nz &= 0xFF; | |
574 goto inc_pc_loop; | |
575 | |
576 { | |
577 int addr; | |
578 case 0xB9: // SBC (x),(y) | |
579 case 0x99: // ADC (x),(y) | |
580 pc--; // compensate for inc later | |
581 data = READ_DP( -2, y ); | |
582 addr = x + dp; | |
583 goto adc_addr; | |
584 case 0xA9: // SBC dp,dp | |
585 case 0x89: // ADC dp,dp | |
586 data = READ_DP( -3, data ); | |
587 case 0xB8: // SBC dp,imm | |
588 case 0x98: // ADC dp,imm | |
589 addr = READ_PC( ++pc ) + dp; | |
590 adc_addr: | |
591 nz = READ( -1, addr ); | |
592 goto adc_data; | |
593 | |
594 // catch ADC and SBC together, then decode later based on operand | |
595 #undef CASE | |
596 #define CASE( n ) case n: case (n) + 0x20: | |
597 ADDR_MODES( 0x88 ) // ADC/SBC addr | |
598 data = READ( 0, data ); | |
599 case 0xA8: // SBC imm | |
600 case 0x88: // ADC imm | |
601 addr = -1; // A | |
602 nz = a; | |
603 adc_data: { | |
604 int flags; | |
605 if ( opcode >= 0xA0 ) // SBC | |
606 data ^= 0xFF; | |
607 | |
608 flags = data ^ nz; | |
609 nz += data + (c >> 8 & 1); | |
610 flags ^= nz; | |
611 | |
612 psw = (psw & ~(v40 | h08)) | | |
613 (flags >> 1 & h08) | | |
614 ((flags + 0x80) >> 2 & v40); | |
615 c = nz; | |
616 if ( addr < 0 ) | |
617 { | |
618 a = (uint8_t) nz; | |
619 goto inc_pc_loop; | |
620 } | |
621 WRITE( 0, addr, /*(uint8_t)*/ nz ); | |
622 goto inc_pc_loop; | |
623 } | |
624 | |
625 } | |
626 | |
627 // 6. ADDITION & SUBTRACTION COMMANDS | |
628 | |
629 #define INC_DEC_REG( reg, op )\ | |
630 nz = reg op;\ | |
631 reg = (uint8_t) nz;\ | |
632 goto loop; | |
633 | |
634 case 0xBC: INC_DEC_REG( a, + 1 ) // INC A | |
635 case 0x3D: INC_DEC_REG( x, + 1 ) // INC X | |
636 case 0xFC: INC_DEC_REG( y, + 1 ) // INC Y | |
637 | |
638 case 0x9C: INC_DEC_REG( a, - 1 ) // DEC A | |
639 case 0x1D: INC_DEC_REG( x, - 1 ) // DEC X | |
640 case 0xDC: INC_DEC_REG( y, - 1 ) // DEC Y | |
641 | |
642 case 0x9B: // DEC dp+X | |
643 case 0xBB: // INC dp+X | |
644 data = (uint8_t) (data + x); | |
645 case 0x8B: // DEC dp | |
646 case 0xAB: // INC dp | |
647 data += dp; | |
648 goto inc_abs; | |
649 case 0x8C: // DEC abs | |
650 case 0xAC: // INC abs | |
651 data = READ_PC16( pc ); | |
652 pc++; | |
653 inc_abs: | |
654 nz = (opcode >> 4 & 2) - 1; | |
655 nz += READ( -1, data ); | |
656 WRITE( 0, data, /*(uint8_t)*/ nz ); | |
657 goto inc_pc_loop; | |
658 | |
659 // 7. SHIFT, ROTATION COMMANDS | |
660 | |
661 case 0x5C: // LSR A | |
662 c = 0; | |
663 case 0x7C:{// ROR A | |
664 nz = (c >> 1 & 0x80) | (a >> 1); | |
665 c = a << 8; | |
666 a = nz; | |
667 goto loop; | |
668 } | |
669 | |
670 case 0x1C: // ASL A | |
671 c = 0; | |
672 case 0x3C:{// ROL A | |
673 int temp = c >> 8 & 1; | |
674 c = a << 1; | |
675 nz = c | temp; | |
676 a = (uint8_t) nz; | |
677 goto loop; | |
678 } | |
679 | |
680 case 0x0B: // ASL dp | |
681 c = 0; | |
682 data += dp; | |
683 goto rol_mem; | |
684 case 0x1B: // ASL dp+X | |
685 c = 0; | |
686 case 0x3B: // ROL dp+X | |
687 data = (uint8_t) (data + x); | |
688 case 0x2B: // ROL dp | |
689 data += dp; | |
690 goto rol_mem; | |
691 case 0x0C: // ASL abs | |
692 c = 0; | |
693 case 0x2C: // ROL abs | |
694 data = READ_PC16( pc ); | |
695 pc++; | |
696 rol_mem: | |
697 nz = c >> 8 & 1; | |
698 nz |= (c = READ( -1, data ) << 1); | |
699 WRITE( 0, data, /*(uint8_t)*/ nz ); | |
700 goto inc_pc_loop; | |
701 | |
702 case 0x4B: // LSR dp | |
703 c = 0; | |
704 data += dp; | |
705 goto ror_mem; | |
706 case 0x5B: // LSR dp+X | |
707 c = 0; | |
708 case 0x7B: // ROR dp+X | |
709 data = (uint8_t) (data + x); | |
710 case 0x6B: // ROR dp | |
711 data += dp; | |
712 goto ror_mem; | |
713 case 0x4C: // LSR abs | |
714 c = 0; | |
715 case 0x6C: // ROR abs | |
716 data = READ_PC16( pc ); | |
717 pc++; | |
718 ror_mem: { | |
719 int temp = READ( -1, data ); | |
720 nz = (c >> 1 & 0x80) | (temp >> 1); | |
721 c = temp << 8; | |
722 WRITE( 0, data, nz ); | |
723 goto inc_pc_loop; | |
724 } | |
725 | |
726 case 0x9F: // XCN | |
727 nz = a = (a >> 4) | (uint8_t) (a << 4); | |
728 goto loop; | |
729 | |
730 // 8. 16-BIT TRANSMISION COMMANDS | |
731 | |
732 case 0xBA: // MOVW YA,dp | |
733 a = READ_DP( -2, data ); | |
734 nz = (a & 0x7F) | (a >> 1); | |
735 y = READ_DP( 0, (uint8_t) (data + 1) ); | |
736 nz |= y; | |
737 goto inc_pc_loop; | |
738 | |
739 case 0xDA: // MOVW dp,YA | |
740 WRITE_DP( -1, data, a ); | |
741 WRITE_DP( 0, (uint8_t) (data + 1), y + no_read_before_write ); | |
742 goto inc_pc_loop; | |
743 | |
744 // 9. 16-BIT OPERATION COMMANDS | |
745 | |
746 case 0x3A: // INCW dp | |
747 case 0x1A:{// DECW dp | |
748 int temp; | |
749 // low byte | |
750 data += dp; | |
751 temp = READ( -3, data ); | |
752 temp += (opcode >> 4 & 2) - 1; // +1 for INCW, -1 for DECW | |
753 nz = ((temp >> 1) | temp) & 0x7F; | |
754 WRITE( -2, data, /*(uint8_t)*/ temp ); | |
755 | |
756 // high byte | |
757 data = (uint8_t) (data + 1) + dp; | |
758 temp = (uint8_t) ((temp >> 8) + READ( -1, data )); | |
759 nz |= temp; | |
760 WRITE( 0, data, temp ); | |
761 | |
762 goto inc_pc_loop; | |
763 } | |
764 | |
765 case 0x7A: // ADDW YA,dp | |
766 case 0x9A:{// SUBW YA,dp | |
767 int lo = READ_DP( -2, data ); | |
768 int hi = READ_DP( 0, (uint8_t) (data + 1) ); | |
769 int result; | |
770 int flags; | |
771 | |
772 if ( opcode == 0x9A ) // SUBW | |
773 { | |
774 lo = (lo ^ 0xFF) + 1; | |
775 hi ^= 0xFF; | |
776 } | |
777 | |
778 lo += a; | |
779 result = y + hi + (lo >> 8); | |
780 flags = hi ^ y ^ result; | |
781 | |
782 psw = (psw & ~(v40 | h08)) | | |
783 (flags >> 1 & h08) | | |
784 ((flags + 0x80) >> 2 & v40); | |
785 c = result; | |
786 a = (uint8_t) lo; | |
787 result = (uint8_t) result; | |
788 y = result; | |
789 nz = (((lo >> 1) | lo) & 0x7F) | result; | |
790 | |
791 goto inc_pc_loop; | |
792 } | |
793 | |
794 case 0x5A: { // CMPW YA,dp | |
795 int temp = a - READ_DP( -1, data ); | |
796 nz = ((temp >> 1) | temp) & 0x7F; | |
797 temp = y + (temp >> 8); | |
798 temp -= READ_DP( 0, (uint8_t) (data + 1) ); | |
799 nz |= temp; | |
800 c = ~temp; | |
801 nz &= 0xFF; | |
802 goto inc_pc_loop; | |
803 } | |
804 | |
805 // 10. MULTIPLICATION & DIVISON COMMANDS | |
806 | |
807 case 0xCF: { // MUL YA | |
808 unsigned temp = y * a; | |
809 a = (uint8_t) temp; | |
810 nz = ((temp >> 1) | temp) & 0x7F; | |
811 y = temp >> 8; | |
812 nz |= y; | |
813 goto loop; | |
814 } | |
815 | |
816 case 0x9E: // DIV YA,X | |
817 { | |
818 unsigned ya = y * 0x100 + a; | |
819 | |
820 psw &= ~(h08 | v40); | |
821 | |
822 if ( y >= x ) | |
823 psw |= v40; | |
824 | |
825 if ( (y & 15) >= (x & 15) ) | |
826 psw |= h08; | |
827 | |
828 if ( y < x * 2 ) | |
829 { | |
830 a = ya / x; | |
831 y = ya - a * x; | |
832 } | |
833 else | |
834 { | |
835 a = 255 - (ya - x * 0x200) / (256 - x); | |
836 y = x + (ya - x * 0x200) % (256 - x); | |
837 } | |
838 | |
839 nz = (uint8_t) a; | |
840 a = (uint8_t) a; | |
841 | |
842 goto loop; | |
843 } | |
844 | |
845 // 11. DECIMAL COMPENSATION COMMANDS | |
846 | |
847 case 0xDF: // DAA | |
848 SUSPICIOUS_OPCODE( "DAA" ); | |
849 if ( a > 0x99 || c & 0x100 ) | |
850 { | |
851 a += 0x60; | |
852 c = 0x100; | |
853 } | |
854 | |
855 if ( (a & 0x0F) > 9 || psw & h08 ) | |
856 a += 0x06; | |
857 | |
858 nz = a; | |
859 a = (uint8_t) a; | |
860 goto loop; | |
861 | |
862 case 0xBE: // DAS | |
863 SUSPICIOUS_OPCODE( "DAS" ); | |
864 if ( a > 0x99 || !(c & 0x100) ) | |
865 { | |
866 a -= 0x60; | |
867 c = 0; | |
868 } | |
869 | |
870 if ( (a & 0x0F) > 9 || !(psw & h08) ) | |
871 a -= 0x06; | |
872 | |
873 nz = a; | |
874 a = (uint8_t) a; | |
875 goto loop; | |
876 | |
877 // 12. BRANCHING COMMANDS | |
878 | |
879 case 0x2F: // BRA rel | |
880 pc += (BOOST::int8_t) data; | |
881 goto inc_pc_loop; | |
882 | |
883 case 0x30: // BMI | |
884 BRANCH( (nz & nz_neg_mask) ) | |
885 | |
886 case 0x10: // BPL | |
887 BRANCH( !(nz & nz_neg_mask) ) | |
888 | |
889 case 0xB0: // BCS | |
890 BRANCH( c & 0x100 ) | |
891 | |
892 case 0x90: // BCC | |
893 BRANCH( !(c & 0x100) ) | |
894 | |
895 case 0x70: // BVS | |
896 BRANCH( psw & v40 ) | |
897 | |
898 case 0x50: // BVC | |
899 BRANCH( !(psw & v40) ) | |
900 | |
901 #define CBRANCH( cond )\ | |
902 {\ | |
903 pc++;\ | |
904 if ( cond )\ | |
905 goto cbranch_taken_loop;\ | |
906 rel_time -= 2;\ | |
907 goto inc_pc_loop;\ | |
908 } | |
909 | |
910 case 0x03: // BBS dp.bit,rel | |
911 case 0x23: | |
912 case 0x43: | |
913 case 0x63: | |
914 case 0x83: | |
915 case 0xA3: | |
916 case 0xC3: | |
917 case 0xE3: | |
918 CBRANCH( READ_DP( -4, data ) >> (opcode >> 5) & 1 ) | |
919 | |
920 case 0x13: // BBC dp.bit,rel | |
921 case 0x33: | |
922 case 0x53: | |
923 case 0x73: | |
924 case 0x93: | |
925 case 0xB3: | |
926 case 0xD3: | |
927 case 0xF3: | |
928 CBRANCH( !(READ_DP( -4, data ) >> (opcode >> 5) & 1) ) | |
929 | |
930 case 0xDE: // CBNE dp+X,rel | |
931 data = (uint8_t) (data + x); | |
932 // fall through | |
933 case 0x2E:{// CBNE dp,rel | |
934 int temp; | |
935 // 61% from timer | |
936 READ_DP_TIMER( -4, data, temp ); | |
937 CBRANCH( temp != a ) | |
938 } | |
939 | |
940 case 0x6E: { // DBNZ dp,rel | |
941 unsigned temp = READ_DP( -4, data ) - 1; | |
942 WRITE_DP( -3, (uint8_t) data, /*(uint8_t)*/ temp + no_read_before_write ); | |
943 CBRANCH( temp ) | |
944 } | |
945 | |
946 case 0xFE: // DBNZ Y,rel | |
947 y = (uint8_t) (y - 1); | |
948 BRANCH( y ) | |
949 | |
950 case 0x1F: // JMP [abs+X] | |
951 SET_PC( READ_PC16( pc ) + x ); | |
952 // fall through | |
953 case 0x5F: // JMP abs | |
954 SET_PC( READ_PC16( pc ) ); | |
955 goto loop; | |
956 | |
957 // 13. SUB-ROUTINE CALL RETURN COMMANDS | |
958 | |
959 case 0x0F:{// BRK | |
960 int temp; | |
961 int ret_addr = GET_PC(); | |
962 SUSPICIOUS_OPCODE( "BRK" ); | |
963 SET_PC( READ_PROG16( 0xFFDE ) ); // vector address verified | |
964 PUSH16( ret_addr ); | |
965 GET_PSW( temp ); | |
966 psw = (psw | b10) & ~i04; | |
967 PUSH( temp ); | |
968 goto loop; | |
969 } | |
970 | |
971 case 0x4F:{// PCALL offset | |
972 int ret_addr = GET_PC() + 1; | |
973 SET_PC( 0xFF00 | data ); | |
974 PUSH16( ret_addr ); | |
975 goto loop; | |
976 } | |
977 | |
978 case 0x01: // TCALL n | |
979 case 0x11: | |
980 case 0x21: | |
981 case 0x31: | |
982 case 0x41: | |
983 case 0x51: | |
984 case 0x61: | |
985 case 0x71: | |
986 case 0x81: | |
987 case 0x91: | |
988 case 0xA1: | |
989 case 0xB1: | |
990 case 0xC1: | |
991 case 0xD1: | |
992 case 0xE1: | |
993 case 0xF1: { | |
994 int ret_addr = GET_PC(); | |
995 SET_PC( READ_PROG16( 0xFFDE - (opcode >> 3) ) ); | |
996 PUSH16( ret_addr ); | |
997 goto loop; | |
998 } | |
999 | |
1000 // 14. STACK OPERATION COMMANDS | |
1001 | |
1002 { | |
1003 int temp; | |
1004 case 0x7F: // RET1 | |
1005 temp = *sp; | |
1006 SET_PC( GET_LE16( sp + 1 ) ); | |
1007 sp += 3; | |
1008 goto set_psw; | |
1009 case 0x8E: // POP PSW | |
1010 POP( temp ); | |
1011 set_psw: | |
1012 SET_PSW( temp ); | |
1013 goto loop; | |
1014 } | |
1015 | |
1016 case 0x0D: { // PUSH PSW | |
1017 int temp; | |
1018 GET_PSW( temp ); | |
1019 PUSH( temp ); | |
1020 goto loop; | |
1021 } | |
1022 | |
1023 case 0x2D: // PUSH A | |
1024 PUSH( a ); | |
1025 goto loop; | |
1026 | |
1027 case 0x4D: // PUSH X | |
1028 PUSH( x ); | |
1029 goto loop; | |
1030 | |
1031 case 0x6D: // PUSH Y | |
1032 PUSH( y ); | |
1033 goto loop; | |
1034 | |
1035 case 0xAE: // POP A | |
1036 POP( a ); | |
1037 goto loop; | |
1038 | |
1039 case 0xCE: // POP X | |
1040 POP( x ); | |
1041 goto loop; | |
1042 | |
1043 case 0xEE: // POP Y | |
1044 POP( y ); | |
1045 goto loop; | |
1046 | |
1047 // 15. BIT OPERATION COMMANDS | |
1048 | |
1049 case 0x02: // SET1 | |
1050 case 0x22: | |
1051 case 0x42: | |
1052 case 0x62: | |
1053 case 0x82: | |
1054 case 0xA2: | |
1055 case 0xC2: | |
1056 case 0xE2: | |
1057 case 0x12: // CLR1 | |
1058 case 0x32: | |
1059 case 0x52: | |
1060 case 0x72: | |
1061 case 0x92: | |
1062 case 0xB2: | |
1063 case 0xD2: | |
1064 case 0xF2: { | |
1065 int bit = 1 << (opcode >> 5); | |
1066 int mask = ~bit; | |
1067 if ( opcode & 0x10 ) | |
1068 bit = 0; | |
1069 data += dp; | |
1070 WRITE( 0, data, (READ( -1, data ) & mask) | bit ); | |
1071 goto inc_pc_loop; | |
1072 } | |
1073 | |
1074 case 0x0E: // TSET1 abs | |
1075 case 0x4E: // TCLR1 abs | |
1076 data = READ_PC16( pc ); | |
1077 pc += 2; | |
1078 { | |
1079 unsigned temp = READ( -2, data ); | |
1080 nz = (uint8_t) (a - temp); | |
1081 temp &= ~a; | |
1082 if ( opcode == 0x0E ) | |
1083 temp |= a; | |
1084 WRITE( 0, data, temp ); | |
1085 } | |
1086 goto loop; | |
1087 | |
1088 case 0x4A: // AND1 C,mem.bit | |
1089 c &= MEM_BIT( 0 ); | |
1090 pc += 2; | |
1091 goto loop; | |
1092 | |
1093 case 0x6A: // AND1 C,/mem.bit | |
1094 c &= ~MEM_BIT( 0 ); | |
1095 pc += 2; | |
1096 goto loop; | |
1097 | |
1098 case 0x0A: // OR1 C,mem.bit | |
1099 c |= MEM_BIT( -1 ); | |
1100 pc += 2; | |
1101 goto loop; | |
1102 | |
1103 case 0x2A: // OR1 C,/mem.bit | |
1104 c |= ~MEM_BIT( -1 ); | |
1105 pc += 2; | |
1106 goto loop; | |
1107 | |
1108 case 0x8A: // EOR1 C,mem.bit | |
1109 c ^= MEM_BIT( -1 ); | |
1110 pc += 2; | |
1111 goto loop; | |
1112 | |
1113 case 0xEA: // NOT1 mem.bit | |
1114 data = READ_PC16( pc ); | |
1115 pc += 2; | |
1116 { | |
1117 unsigned temp = READ( -1, data & 0x1FFF ); | |
1118 temp ^= 1 << (data >> 13); | |
1119 WRITE( 0, data & 0x1FFF, temp ); | |
1120 } | |
1121 goto loop; | |
1122 | |
1123 case 0xCA: // MOV1 mem.bit,C | |
1124 data = READ_PC16( pc ); | |
1125 pc += 2; | |
1126 { | |
1127 unsigned temp = READ( -2, data & 0x1FFF ); | |
1128 unsigned bit = data >> 13; | |
1129 temp = (temp & ~(1 << bit)) | ((c >> 8 & 1) << bit); | |
1130 WRITE( 0, data & 0x1FFF, temp + no_read_before_write ); | |
1131 } | |
1132 goto loop; | |
1133 | |
1134 case 0xAA: // MOV1 C,mem.bit | |
1135 c = MEM_BIT( 0 ); | |
1136 pc += 2; | |
1137 goto loop; | |
1138 | |
1139 // 16. PROGRAM PSW FLAG OPERATION COMMANDS | |
1140 | |
1141 case 0x60: // CLRC | |
1142 c = 0; | |
1143 goto loop; | |
1144 | |
1145 case 0x80: // SETC | |
1146 c = ~0; | |
1147 goto loop; | |
1148 | |
1149 case 0xED: // NOTC | |
1150 c ^= 0x100; | |
1151 goto loop; | |
1152 | |
1153 case 0xE0: // CLRV | |
1154 psw &= ~(v40 | h08); | |
1155 goto loop; | |
1156 | |
1157 case 0x20: // CLRP | |
1158 dp = 0; | |
1159 goto loop; | |
1160 | |
1161 case 0x40: // SETP | |
1162 dp = 0x100; | |
1163 goto loop; | |
1164 | |
1165 case 0xA0: // EI | |
1166 SUSPICIOUS_OPCODE( "EI" ); | |
1167 psw |= i04; | |
1168 goto loop; | |
1169 | |
1170 case 0xC0: // DI | |
1171 SUSPICIOUS_OPCODE( "DI" ); | |
1172 psw &= ~i04; | |
1173 goto loop; | |
1174 | |
1175 // 17. OTHER COMMANDS | |
1176 | |
1177 case 0x00: // NOP | |
1178 goto loop; | |
1179 | |
1180 case 0xFF:{// STOP | |
1181 // handle PC wrap-around | |
1182 unsigned addr = GET_PC() - 1; | |
1183 if ( addr >= 0x10000 ) | |
1184 { | |
1185 addr &= 0xFFFF; | |
1186 SET_PC( addr ); | |
1187 dprintf( "SPC: PC wrapped around\n" ); | |
1188 goto loop; | |
1189 } | |
1190 } | |
1191 // fall through | |
1192 case 0xEF: // SLEEP | |
1193 SUSPICIOUS_OPCODE( "STOP/SLEEP" ); | |
1194 --pc; | |
1195 rel_time = 0; | |
1196 m.cpu_error = "SPC emulation error"; | |
1197 goto stop; | |
1198 } // switch | |
1199 | |
1200 assert( 0 ); // catch any unhandled instructions | |
1201 } | |
1202 out_of_time: | |
1203 rel_time -= m.cycle_table [*pc]; // undo partial execution of opcode | |
1204 stop: | |
1205 | |
1206 // Uncache registers | |
1207 if ( GET_PC() >= 0x10000 ) | |
1208 dprintf( "SPC: PC wrapped around\n" ); | |
1209 m.cpu_regs.pc = (uint16_t) GET_PC(); | |
1210 m.cpu_regs.sp = ( uint8_t) GET_SP(); | |
1211 m.cpu_regs.a = ( uint8_t) a; | |
1212 m.cpu_regs.x = ( uint8_t) x; | |
1213 m.cpu_regs.y = ( uint8_t) y; | |
1214 { | |
1215 int temp; | |
1216 GET_PSW( temp ); | |
1217 m.cpu_regs.psw = (uint8_t) temp; | |
1218 } | |
1219 } | |
1220 SPC_CPU_RUN_FUNC_END |